)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":24,"context_line":"non-zero return code"},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"Change-Id: Iaae0d9fb7a1949d1aad9aa77b0daeb249fb471b5"},{"line_number":27,"context_line":"Signed-off-by: Clay Gerrard \u003cclay.gerrard@gmail.com\u003e"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"661b2565_cc948a9c","line":27,"updated":"2025-10-10 12:22:57.000000000","message":"I think this warrants a launchpad bug: there\u0027s circumstances in which the relinker may never complete","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"}],"/PATCHSET_LEVEL":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"b5140071_fb39f2d8","updated":"2025-10-10 12:22:57.000000000","message":"This is a very nicely crafted patch. Test coverage is good, I\u0027ve introduced various regressions and seen the tests fail.\n\nWe have seen these hard link collisions in production and need this option to be able to complete a part power increase. The new behavior is confined to an option that defaults to off.\n\nI\u0027m not merging yet because think there ought to be a link to a launchpad bug with some background as to when and how the issue might occur.\n\nOtherwise, (not blockers) I\u0027m not sure I have understood the reasoning about error vs warning logs, and there\u0027s some cosmetic stuff in the tests that I have left suggestions for here https://review.opendev.org/c/openstack/swift/+/963686\n\nWe might add something to https://docs.openstack.org/swift/latest/ring_partpower.html","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"9ec72607795c900327b9256e1e0986046518e6ef","unresolved":false,"context_lines":[],"source_content_type":"","patch_set":2,"id":"ce050246_1d903e67","updated":"2025-10-10 15:32:14.000000000","message":"that\u0027s for the squash fixups!\n\ni\u0027ll work on the lp bug report.","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"}],"swift/cli/relinker.py":[{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":418,"context_line":"        try:"},{"line_number":419,"context_line":"            created \u003d diskfile.relink_paths(old_file, new_file)"},{"line_number":420,"context_line":"            success \u003d True"},{"line_number":421,"context_line":"        except FileExistsError:"},{"line_number":422,"context_line":"            # we\u0027ve detected a hardlink collision, so we need to handle it"},{"line_number":423,"context_line":"            # depending on what kind of file it is and our mode and"},{"line_number":424,"context_line":"            # configuration"}],"source_content_type":"text/x-python","patch_set":2,"id":"a155db57_69b9ff09","line":421,"range":{"start_line":421,"start_character":15,"end_line":421,"end_character":30},"updated":"2025-10-10 12:22:57.000000000","message":"TIL this is an alias of OSError(errno\u003dEEXIST). I bet there\u0027s a few other places we could have used this for slicker exception handling.","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":430,"context_line":"                    \"Relinking%s: tolerating different inodes for \""},{"line_number":431,"context_line":"                    \"tombstone with same timestamp: %s to %s\","},{"line_number":432,"context_line":"                    \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"},{"line_number":433,"context_line":"                    old_file, new_file)"},{"line_number":434,"context_line":"                success \u003d True"},{"line_number":435,"context_line":"            elif self.conf[\u0027clobber_hardlink_collisions\u0027]:"},{"line_number":436,"context_line":"                if self.do_cleanup:"}],"source_content_type":"text/x-python","patch_set":2,"id":"e70ab5f7_ed3fb82f","line":433,"updated":"2025-10-10 12:22:57.000000000","message":"off-topic: might be to useful to have a RelinkerLogger than does all this repeated log message annotation","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":432,"context_line":"                    \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"},{"line_number":433,"context_line":"                    old_file, new_file)"},{"line_number":434,"context_line":"                success \u003d True"},{"line_number":435,"context_line":"            elif self.conf[\u0027clobber_hardlink_collisions\u0027]:"},{"line_number":436,"context_line":"                if self.do_cleanup:"},{"line_number":437,"context_line":"                    # At this point your clients are already *in* the new part"},{"line_number":438,"context_line":"                    # dir, if the \"better\" data was in the old part dir you\u0027re"}],"source_content_type":"text/x-python","patch_set":2,"id":"daf7c25d_58088041","line":435,"updated":"2025-10-10 12:22:57.000000000","message":"ok, this elif clause contains the new behavior","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":445,"context_line":"                    # cleaned up.  But there might be a case to argue that"},{"line_number":446,"context_line":"                    # clobber_hardlink_collision should quarantine old_file"},{"line_number":447,"context_line":"                    # here before returning success."},{"line_number":448,"context_line":"                    self.logger.debug("},{"line_number":449,"context_line":"                        \"Relinking%s: tolerating hardlink collision: \""},{"line_number":450,"context_line":"                        \"%s to %s\","},{"line_number":451,"context_line":"                        \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"2907d364_7bf357b9","line":448,"updated":"2025-10-10 12:22:57.000000000","message":"hmmm, so in prod, where log_level is typically \u003e debug, we\u0027d get no feedback as to whether collisions occurred or not.","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":454,"context_line":"                elif already_quarantined:"},{"line_number":455,"context_line":"                    # Already attempted quarantine, this is a failure, but user"},{"line_number":456,"context_line":"                    # can retry (or already_quarantined becomes a counter?)"},{"line_number":457,"context_line":"                    # N.B. this can exit non-zero w/o logging at \"error\""},{"line_number":458,"context_line":"                    self.logger.warning("},{"line_number":459,"context_line":"                        \"Relinking%s: hardlink collision persists after \""},{"line_number":460,"context_line":"                        \"quarantine: %s to %s\","}],"source_content_type":"text/x-python","patch_set":2,"id":"6eb55c3e_9b08d85d","line":457,"updated":"2025-10-10 12:22:57.000000000","message":"do we want to add any stats for hardlink collisions? IIUC, the relinker will exist with non-zero and SRE have to grep logs to figure out why and the extent of the problem.","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":459,"context_line":"                        \"Relinking%s: hardlink collision persists after \""},{"line_number":460,"context_line":"                        \"quarantine: %s to %s\","},{"line_number":461,"context_line":"                        \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"},{"line_number":462,"context_line":"                        old_file, new_file)"},{"line_number":463,"context_line":"                else:"},{"line_number":464,"context_line":"                    # During relink phase, quarantine and retry once"},{"line_number":465,"context_line":"                    dev_path \u003d os.path.join(self.diskfile_mgr.devices, device)"}],"source_content_type":"text/x-python","patch_set":2,"id":"bb2bb4a5_d453aa99","line":462,"updated":"2025-10-10 12:22:57.000000000","message":"IIUC the reasoning for the drive-by is that we should log at error level if returning ``success\u003dFalse`` (non-zero exit code), in which case this should be at error level?\n\nI see the comment ``N.B. this can exit non-zero w/o logging at \"error\"`` but I\u0027m not sure why this is treated differently.\n\nBut ... the final log message in ``run()`` is also only a warning if there have been errors? I\u0027m not sure if I have understood the reasoning at line 490:\n\n\u003e we log them as error to match the expected non-zero return code.","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":464,"context_line":"                    # During relink phase, quarantine and retry once"},{"line_number":465,"context_line":"                    dev_path \u003d os.path.join(self.diskfile_mgr.devices, device)"},{"line_number":466,"context_line":"                    to_dir \u003d diskfile.quarantine_renamer(dev_path, new_file)"},{"line_number":467,"context_line":"                    self.logger.info("},{"line_number":468,"context_line":"                        \"Relinking%s: clobbering hardlink collision: \""},{"line_number":469,"context_line":"                        \"%s moved to %s\","},{"line_number":470,"context_line":"                        \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"b1dc4ddb_1810da6d","line":467,"updated":"2025-10-10 12:22:57.000000000","message":"every other place I found where we quarantine an object it is logged at warning (or higher severity) and the log includes the word \u0027quarantining\u0027.\n\nI think it would be good to at least include \u0027quarantining in the message, maybe:\n\n```\nRelinking%s: clobbering hardlink collision: quarantining %s to %s\"\n```","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":476,"context_line":"            else:"},{"line_number":477,"context_line":"                self.logger.error("},{"line_number":478,"context_line":"                    \"Error relinking%s: hardlink collision: \""},{"line_number":479,"context_line":"                    \"%s to %s (consider enabling clobber_hardlink_collisions)\","},{"line_number":480,"context_line":"                    \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"},{"line_number":481,"context_line":"                    old_file, new_file)"},{"line_number":482,"context_line":"        except Exception as exc:"}],"source_content_type":"text/x-python","patch_set":2,"id":"2669c59b_523a1e0a","line":479,"range":{"start_line":479,"start_character":31,"end_line":479,"end_character":76},"updated":"2025-10-10 12:22:57.000000000","message":"ok, nice touch, SRE says \u0027thanks for the hint\u0027","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":479,"context_line":"                    \"%s to %s (consider enabling clobber_hardlink_collisions)\","},{"line_number":480,"context_line":"                    \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"},{"line_number":481,"context_line":"                    old_file, new_file)"},{"line_number":482,"context_line":"        except Exception as exc:"},{"line_number":483,"context_line":"            # Depending on what kind of errors these are, it might be"},{"line_number":484,"context_line":"            # reasonable to consider them \"warnings\" if we expect re-running"},{"line_number":485,"context_line":"            # the relinker would be able to fix them (like if it\u0027s just a"}],"source_content_type":"text/x-python","patch_set":2,"id":"a54bd296_7fd52cee","line":482,"updated":"2025-10-10 12:22:57.000000000","message":"this is broadening from OSError to any Exception. I wonder if we shouldn\u0027t log exceptions other than OSError using ``.exception`` so we get the traceback?\n\nAlso, there\u0027s no test coverage for anything other than OSError","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":489,"context_line":"            # enumerated above and any unknown error conditions may not be"},{"line_number":490,"context_line":"            # fixable by simply re-running the relinker: so we log them as"},{"line_number":491,"context_line":"            # error to match the expected non-zero return code."},{"line_number":492,"context_line":"            self.logger.error("},{"line_number":493,"context_line":"                \"Error relinking%s: failed to relink %s to %s: %s\","},{"line_number":494,"context_line":"                \u0027 (cleanup)\u0027 if self.do_cleanup else \u0027\u0027,"},{"line_number":495,"context_line":"                old_file, new_file, exc)"}],"source_content_type":"text/x-python","patch_set":2,"id":"5fa99aa2_511dece0","line":492,"range":{"start_line":492,"start_character":23,"end_line":492,"end_character":29},"updated":"2025-10-10 12:22:57.000000000","message":"right, so this is upgraded from warning on master","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":500,"context_line":"                old_file, new_file)"},{"line_number":501,"context_line":"        return success, created"},{"line_number":502,"context_line":""},{"line_number":503,"context_line":"    def process_location(self, device, hash_path, new_hash_path):"},{"line_number":504,"context_line":"        \"\"\""},{"line_number":505,"context_line":"        Handle relink of all files in a hash_dir path."},{"line_number":506,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"df4402fa_a8683734","line":503,"updated":"2025-10-10 12:22:57.000000000","message":"ok, we need ``device`` for quarantining","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":509,"context_line":"        most up to date set of files. The new location may have newer"},{"line_number":510,"context_line":"        files if it has been updated since relinked."},{"line_number":511,"context_line":""},{"line_number":512,"context_line":"        If any new links are crated the suffix will be invalidated."},{"line_number":513,"context_line":"        In cleanup mode, the unwanted files in the old hash_path will be"},{"line_number":514,"context_line":"        removed as long as there are no errors."},{"line_number":515,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"6d3e0951_1f3bbd25","line":512,"range":{"start_line":512,"start_character":29,"end_line":512,"end_character":35},"updated":"2025-10-10 12:22:57.000000000","message":"typo: created","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":509,"context_line":"        most up to date set of files. The new location may have newer"},{"line_number":510,"context_line":"        files if it has been updated since relinked."},{"line_number":511,"context_line":""},{"line_number":512,"context_line":"        If any new links are crated the suffix will be invalidated."},{"line_number":513,"context_line":"        In cleanup mode, the unwanted files in the old hash_path will be"},{"line_number":514,"context_line":"        removed as long as there are no errors."},{"line_number":515,"context_line":""}],"source_content_type":"text/x-python","patch_set":2,"id":"752124e6_e905f1aa","line":512,"range":{"start_line":512,"start_character":29,"end_line":512,"end_character":35},"in_reply_to":"6d3e0951_1f3bbd25","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":566,"context_line":"            #    no longer required. The new file will eventually be"},{"line_number":567,"context_line":"            #    cleaned up again."},{"line_number":568,"context_line":"            self.stats[\u0027files\u0027] +\u003d 1"},{"line_number":569,"context_line":"            success, created \u003d self.do_relink("},{"line_number":570,"context_line":"                device, hash_path, new_hash_path, filename)"},{"line_number":571,"context_line":"            if success:"},{"line_number":572,"context_line":"                if created:"},{"line_number":573,"context_line":"                    created_links +\u003d 1"}],"source_content_type":"text/x-python","patch_set":2,"id":"d5b04be8_2125b909","line":570,"range":{"start_line":569,"start_character":31,"end_line":570,"end_character":59},"updated":"2025-10-10 12:22:57.000000000","message":"ok, this is a clean refactor to move code to do_relink, where the new behavior has then been added.","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":896,"context_line":"                        help\u003d\u0027Enable debug mode\u0027)"},{"line_number":897,"context_line":"    parser.add_argument(\u0027--clobber-hardlink-collisions\u0027, action\u003d\u0027store_true\u0027,"},{"line_number":898,"context_line":"                        help\u003d\u0027Quarantine colliding datafiles in new target \u0027"},{"line_number":899,"context_line":"                             \u0027part dir and retry the relink (default: false)\u0027)"},{"line_number":900,"context_line":""},{"line_number":901,"context_line":"    args \u003d parser.parse_args(args)"},{"line_number":902,"context_line":"    hubs.use_hub(get_hub())"}],"source_content_type":"text/x-python","patch_set":2,"id":"d302b2d0_9dca6204","line":899,"updated":"2025-10-10 12:22:57.000000000","message":"this ought to differentiate between relink and cleanup behavior\n\nAlso, it\u0027s not just data files? IUC the clobber would also apply to colliding meta files (which we haven\u0027t seen but could in theory happen??)","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":896,"context_line":"                        help\u003d\u0027Enable debug mode\u0027)"},{"line_number":897,"context_line":"    parser.add_argument(\u0027--clobber-hardlink-collisions\u0027, action\u003d\u0027store_true\u0027,"},{"line_number":898,"context_line":"                        help\u003d\u0027Quarantine colliding datafiles in new target \u0027"},{"line_number":899,"context_line":"                             \u0027part dir and retry the relink (default: false)\u0027)"},{"line_number":900,"context_line":""},{"line_number":901,"context_line":"    args \u003d parser.parse_args(args)"},{"line_number":902,"context_line":"    hubs.use_hub(get_hub())"}],"source_content_type":"text/x-python","patch_set":2,"id":"8e5fcbab_63f47926","line":899,"in_reply_to":"d302b2d0_9dca6204","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"}],"test/unit/cli/test_relinker.py":[{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"9ec72607795c900327b9256e1e0986046518e6ef","unresolved":true,"context_lines":[{"line_number":880,"context_line":"            self.assertEqual(sorted(exp_filenames), sorted(actual_old))"},{"line_number":881,"context_line":"        else:"},{"line_number":882,"context_line":"            self.assertFalse(os.path.exists(self.objdir))"},{"line_number":883,"context_line":"        self.assertEqual([], self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":884,"context_line":""},{"line_number":885,"context_line":"    def _relink_test(self, old_file_specs, new_file_specs,"},{"line_number":886,"context_line":"                     exp_old_specs, exp_new_specs):"}],"source_content_type":"text/x-python","patch_set":2,"id":"98b725f4_57b11408","side":"PARENT","line":883,"updated":"2025-10-10 15:32:14.000000000","message":"this was the pre-existing assertion that started my fight with log-levels\n\nSome of the callers of this method exit non-zero - it *failed* - but the common assertion is ALWAYS \"no errors\" (??)\n\nShould we take it as a commentary on \"this tool uses the warning log level to mean error and will never log an error\" - should we try to make the code behave such that \"if you exit non-zero you should log the error\"?","commit_id":"b035ed1385ee729411adbd459bb03033e90bb13a"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":756,"context_line":"            relinker.main([\u0027relink\u0027, conf_file, \u0027--device\u0027, \u0027sdx\u0027])"},{"line_number":757,"context_line":""},{"line_number":758,"context_line":"        self.assertEqual(False, captured_relinker_instance.conf["},{"line_number":759,"context_line":"            \u0027clobber_hardlink_collisions\u0027])"},{"line_number":760,"context_line":""},{"line_number":761,"context_line":"    def test_relinker_clobber_hardlink_collisions_config(self):"},{"line_number":762,"context_line":"        config \u003d \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"7d9e018e_f324e26c","line":759,"updated":"2025-10-10 12:22:57.000000000","message":"self.assertFalse?","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":756,"context_line":"            relinker.main([\u0027relink\u0027, conf_file, \u0027--device\u0027, \u0027sdx\u0027])"},{"line_number":757,"context_line":""},{"line_number":758,"context_line":"        self.assertEqual(False, captured_relinker_instance.conf["},{"line_number":759,"context_line":"            \u0027clobber_hardlink_collisions\u0027])"},{"line_number":760,"context_line":""},{"line_number":761,"context_line":"    def test_relinker_clobber_hardlink_collisions_config(self):"},{"line_number":762,"context_line":"        config \u003d \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"ee39b904_482fb706","line":759,"in_reply_to":"7d9e018e_f324e26c","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":786,"context_line":"            relinker.main([\u0027relink\u0027, conf_file, \u0027--device\u0027, \u0027sdx\u0027])"},{"line_number":787,"context_line":""},{"line_number":788,"context_line":"        self.assertEqual(True, captured_relinker_instance.conf["},{"line_number":789,"context_line":"            \u0027clobber_hardlink_collisions\u0027])"},{"line_number":790,"context_line":""},{"line_number":791,"context_line":"    def test_relinker_clobber_hardlink_collisions_arg(self):"},{"line_number":792,"context_line":"        config \u003d \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"b5a59fe1_8dbc8cb4","line":789,"updated":"2025-10-10 12:22:57.000000000","message":"self.assertTrue?","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":786,"context_line":"            relinker.main([\u0027relink\u0027, conf_file, \u0027--device\u0027, \u0027sdx\u0027])"},{"line_number":787,"context_line":""},{"line_number":788,"context_line":"        self.assertEqual(True, captured_relinker_instance.conf["},{"line_number":789,"context_line":"            \u0027clobber_hardlink_collisions\u0027])"},{"line_number":790,"context_line":""},{"line_number":791,"context_line":"    def test_relinker_clobber_hardlink_collisions_arg(self):"},{"line_number":792,"context_line":"        config \u003d \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"a78513c3_0bc50855","line":789,"in_reply_to":"b5a59fe1_8dbc8cb4","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":817,"context_line":"                           \u0027--clobber-hardlink-collisions\u0027])"},{"line_number":818,"context_line":""},{"line_number":819,"context_line":"        self.assertEqual(True, captured_relinker_instance.conf["},{"line_number":820,"context_line":"            \u0027clobber_hardlink_collisions\u0027])"},{"line_number":821,"context_line":""},{"line_number":822,"context_line":"    def test_relinker_utils_get_hub(self):"},{"line_number":823,"context_line":"        cli_cmd \u003d [\u0027relink\u0027, \u0027--device\u0027, \u0027sdx\u0027, \u0027--workers\u0027, \u0027auto\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"454e6cec_c8a474a6","line":820,"updated":"2025-10-10 12:22:57.000000000","message":"ditto","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":817,"context_line":"                           \u0027--clobber-hardlink-collisions\u0027])"},{"line_number":818,"context_line":""},{"line_number":819,"context_line":"        self.assertEqual(True, captured_relinker_instance.conf["},{"line_number":820,"context_line":"            \u0027clobber_hardlink_collisions\u0027])"},{"line_number":821,"context_line":""},{"line_number":822,"context_line":"    def test_relinker_utils_get_hub(self):"},{"line_number":823,"context_line":"        cli_cmd \u003d [\u0027relink\u0027, \u0027--device\u0027, \u0027sdx\u0027, \u0027--workers\u0027, \u0027auto\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"0a9c6b88_8c2fb2ff","line":820,"in_reply_to":"454e6cec_c8a474a6","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":985,"context_line":"            error_lines \u003d self.logger.get_lines_for_level(\u0027error\u0027)"},{"line_number":986,"context_line":"            self.assertEqual(len(conflict_file_specs), len(error_lines))"},{"line_number":987,"context_line":"            for err_msg in error_lines:"},{"line_number":988,"context_line":"                self.assertTrue(err_msg.startswith(\"Error relinking\"))"},{"line_number":989,"context_line":"                self.assertIn(\"hardlink collision\", err_msg)"},{"line_number":990,"context_line":"                self.assertTrue(err_msg.endswith("},{"line_number":991,"context_line":"                    \"(consider enabling clobber_hardlink_collisions)\"))"}],"source_content_type":"text/x-python","patch_set":2,"id":"0e77c2a8_f1dbf0f0","line":988,"updated":"2025-10-10 12:22:57.000000000","message":"nit: if this ever fails (with \"False is not True\") I know I am going to immediately need to dump all the ``error_lines`` by adding them as second arg","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":1228,"context_line":"        for line in info_lines:"},{"line_number":1229,"context_line":"            if line.startswith(\u0027Relinking: clobbering hardlink collision\u0027):"},{"line_number":1230,"context_line":"                clobbering_line \u003d line"},{"line_number":1231,"context_line":"                break"},{"line_number":1232,"context_line":"        else:"},{"line_number":1233,"context_line":"            self.fail(\u0027No clobbering line found:\\n%s\u0027 % \u0027\\n\u0027.join(info_lines))"},{"line_number":1234,"context_line":"        quarantine_dir \u003d os.path.join("}],"source_content_type":"text/x-python","patch_set":2,"id":"5d85e1f5_93bea10a","line":1231,"updated":"2025-10-10 12:22:57.000000000","message":"nit: this will miss unexpected other clobbering lines","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":1228,"context_line":"        for line in info_lines:"},{"line_number":1229,"context_line":"            if line.startswith(\u0027Relinking: clobbering hardlink collision\u0027):"},{"line_number":1230,"context_line":"                clobbering_line \u003d line"},{"line_number":1231,"context_line":"                break"},{"line_number":1232,"context_line":"        else:"},{"line_number":1233,"context_line":"            self.fail(\u0027No clobbering line found:\\n%s\u0027 % \u0027\\n\u0027.join(info_lines))"},{"line_number":1234,"context_line":"        quarantine_dir \u003d os.path.join("}],"source_content_type":"text/x-python","patch_set":2,"id":"db34bade_9c6937d3","line":1231,"in_reply_to":"5d85e1f5_93bea10a","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":1278,"context_line":""},{"line_number":1279,"context_line":"        # but there was a *another* hardlink collision (!?)"},{"line_number":1280,"context_line":"        warning_lines \u003d self.logger.get_lines_for_level(\u0027warning\u0027)"},{"line_number":1281,"context_line":"        for line in warning_lines:"},{"line_number":1282,"context_line":"            if line.startswith(\u0027Relinking: hardlink collision\u0027):"},{"line_number":1283,"context_line":"                collision_line \u003d line"},{"line_number":1284,"context_line":"                break"}],"source_content_type":"text/x-python","patch_set":2,"id":"970bc19d_2c8e83d2","line":1281,"updated":"2025-10-10 12:22:57.000000000","message":"we only expect 2 warning lines so these assertions could be tighter: the first warning should be the collision, the second is the end or run summary","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":1278,"context_line":""},{"line_number":1279,"context_line":"        # but there was a *another* hardlink collision (!?)"},{"line_number":1280,"context_line":"        warning_lines \u003d self.logger.get_lines_for_level(\u0027warning\u0027)"},{"line_number":1281,"context_line":"        for line in warning_lines:"},{"line_number":1282,"context_line":"            if line.startswith(\u0027Relinking: hardlink collision\u0027):"},{"line_number":1283,"context_line":"                collision_line \u003d line"},{"line_number":1284,"context_line":"                break"}],"source_content_type":"text/x-python","patch_set":2,"id":"5b3adef4_d2d7b4d5","line":1281,"in_reply_to":"970bc19d_2c8e83d2","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":1286,"context_line":"            self.fail(\u0027No collision line found:\\n%s\u0027 % \u0027\\n\u0027.join("},{"line_number":1287,"context_line":"                warning_lines))"},{"line_number":1288,"context_line":"        self.assertIn(\u0027persists after quarantine\u0027, collision_line)"},{"line_number":1289,"context_line":"        # XXX this is kind of sketch, is warning just an un-usable log level?"},{"line_number":1290,"context_line":"        self.assertEqual([], self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":1291,"context_line":""},{"line_number":1292,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"014f7c81_bc63bf45","line":1289,"updated":"2025-10-10 12:22:57.000000000","message":"I\u0027m not sure what this refers to?","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":1179,"name":"Clay Gerrard","email":"clay.gerrard@gmail.com","username":"clay-gerrard"},"change_message_id":"9ec72607795c900327b9256e1e0986046518e6ef","unresolved":true,"context_lines":[{"line_number":1286,"context_line":"            self.fail(\u0027No collision line found:\\n%s\u0027 % \u0027\\n\u0027.join("},{"line_number":1287,"context_line":"                warning_lines))"},{"line_number":1288,"context_line":"        self.assertIn(\u0027persists after quarantine\u0027, collision_line)"},{"line_number":1289,"context_line":"        # XXX this is kind of sketch, is warning just an un-usable log level?"},{"line_number":1290,"context_line":"        self.assertEqual([], self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":1291,"context_line":""},{"line_number":1292,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"c1e23013_768365ae","line":1289,"in_reply_to":"014f7c81_bc63bf45","updated":"2025-10-10 15:32:14.000000000","message":"because we log warnings and exit non-zero!  But there\u0027s no error log messages!?","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":true,"context_lines":[{"line_number":1286,"context_line":"            self.fail(\u0027No collision line found:\\n%s\u0027 % \u0027\\n\u0027.join("},{"line_number":1287,"context_line":"                warning_lines))"},{"line_number":1288,"context_line":"        self.assertIn(\u0027persists after quarantine\u0027, collision_line)"},{"line_number":1289,"context_line":"        # XXX this is kind of sketch, is warning just an un-usable log level?"},{"line_number":1290,"context_line":"        self.assertEqual([], self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":1291,"context_line":""},{"line_number":1292,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"}],"source_content_type":"text/x-python","patch_set":2,"id":"f8009125_178bc315","line":1289,"in_reply_to":"c1e23013_768365ae","updated":"2025-10-13 11:08:54.000000000","message":"\u003e is warning just an un-usable log level?\n\nI don\u0027t see how warning is un-usable. I do see that exit 1 with no error lines is questionable.\n\nI\u0027ll try to find some words...","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"f2923e8218a03bb85d6880206264d92da1b70bed","unresolved":true,"context_lines":[{"line_number":1289,"context_line":"        # XXX this is kind of sketch, is warning just an un-usable log level?"},{"line_number":1290,"context_line":"        self.assertEqual([], self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":1291,"context_line":""},{"line_number":1292,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"},{"line_number":1293,"context_line":""},{"line_number":1294,"context_line":"        self.assertEqual("},{"line_number":1295,"context_line":"            \u00271 hash dirs processed (cleanup\u003dFalse) \u0027"}],"source_content_type":"text/x-python","patch_set":2,"id":"f482f868_4965e2fd","line":1292,"updated":"2025-10-10 12:22:57.000000000","message":"unused, maybe some cut\u0027n\u0027paste cruft??","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"},{"author":{"_account_id":7847,"name":"Alistair Coles","email":"alistairncoles@gmail.com","username":"acoles"},"change_message_id":"2cf0875b3ed362b23320cd08f970cbcc7d705da0","unresolved":false,"context_lines":[{"line_number":1289,"context_line":"        # XXX this is kind of sketch, is warning just an un-usable log level?"},{"line_number":1290,"context_line":"        self.assertEqual([], self.logger.get_lines_for_level(\u0027error\u0027))"},{"line_number":1291,"context_line":""},{"line_number":1292,"context_line":"        info_lines \u003d self.logger.get_lines_for_level(\u0027info\u0027)"},{"line_number":1293,"context_line":""},{"line_number":1294,"context_line":"        self.assertEqual("},{"line_number":1295,"context_line":"            \u00271 hash dirs processed (cleanup\u003dFalse) \u0027"}],"source_content_type":"text/x-python","patch_set":2,"id":"3681b911_b25128bc","line":1292,"in_reply_to":"f482f868_4965e2fd","updated":"2025-10-13 11:08:54.000000000","message":"Done","commit_id":"1980d36ca98c5a78c202962a93f6b7d877ba7d54"}]}
