Merge branch 'jt/index-fd-wo-repo-regression-fix'

During Git 2.52 timeframe, we broke streaming computation of object
hash outside a repository, which has been corrected.

* jt/index-fd-wo-repo-regression-fix:
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index b867075..4992e52 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -668,6 +668,18 @@
    unsigned other_field:1;
    unsigned field_with_longer_name:1;
 
+ - When a function `F` accepts flags, those flags should be defined as `enum
+   F_flags`. Individual flag definitions should start with `F` and be in
+   all-uppercase letters. Flag values should be represented via bit shifts.
+   E.g.
+
+        enum frobnicate_flags {
+                FROBNICATE_FOO = (1 << 0),
+                FROBNICATE_BAR = (1 << 1),
+        };
+
+        int frobnicate(enum frobnicate_flags flags);
+
  - Array names should be named in the singular form if the individual items are
    subject of use. E.g.:
 
diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc
index c692ddd..27dbfdc 100644
--- a/Documentation/RelNotes/2.54.0.adoc
+++ b/Documentation/RelNotes/2.54.0.adoc
@@ -121,6 +121,9 @@
 
  * git replay now supports replaying down to the root commit.
 
+ * Handling of signed commits and tags in fast-import has been made more
+   configurable.
+
 
 Performance, Internal Implementation, Development Support etc.
 --------------------------------------------------------------
@@ -300,6 +303,19 @@
    that discarded constness when they return a pointer into a const
    string to preserve constness.
 
+ * A handful of inappropriate uses of the_repository have been
+   rewritten to use the right repository structure instance in the
+   read-cache.c codepath.
+
+ * Internals of "git fsck" have been refactored to not depend on the
+   global `the_repository` variable.
+
+ * Reduce dependency on `the_repository` in add-patch.c file.
+
+ * The way the "git log -L<range>:<file>" feature is bolted onto the
+   log/diff machinery is being reworked a bit to make the feature
+   compatible with more diff options, like -S/G.
+
 
 Fixes since v2.53
 -----------------
@@ -478,6 +494,21 @@
    refspec is a single-object refspec, which has been corrected.
    (merge 4e5dc601dd kj/refspec-parsing-outside-repository later to maint).
 
+ * Fix a regression in writing the commit-graph where commits with dates
+   exceeding 34 bits (beyond year 2514) could cause an underflow and
+   crash Git during the generation data overflow chunk writing.
+
+ * The value of a wrong pointer variable was referenced in an error
+   message that reported that it shouldn't be NULL.
+   (merge 753ecf4205 yc/path-walk-fix-error-reporting later to maint).
+
+ * The check in "receive-pack" to prevent a checked out branch from
+   getting updated via updateInstead mechanism has been corrected.
+
+ * "git backfill" is capable of auto-detecting a sparsely checked out
+   working tree, which was broken.
+   (merge 339eba65a7 th/backfill-auto-detect-sparseness-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint).
    (merge 7a747f972d dd/t5403-modernise later to maint).
diff --git a/Documentation/git-fast-import.adoc b/Documentation/git-fast-import.adoc
index b3f42d4..d68bc52 100644
--- a/Documentation/git-fast-import.adoc
+++ b/Documentation/git-fast-import.adoc
@@ -66,11 +66,10 @@
 remote-helpers that use the `import` capability, as they are
 already trusted to run their own code.
 
-`--signed-tags=(verbatim|warn-verbatim|warn-strip|strip|abort)`::
+`--signed-tags=<mode>`::
 	Specify how to handle signed tags. Behaves in the same way as
-	the `--signed-commits=<mode>` below, except that the
-	`strip-if-invalid` mode is not yet supported. Like for signed
-	commits, the default mode is `verbatim`.
+	the `--signed-commits=<mode>` below. Like for signed commits,
+	the default mode is `verbatim`.
 
 `--signed-commits=<mode>`::
 	Specify how to handle signed commits. The following <mode>s
@@ -90,6 +89,8 @@
   commit signatures and replaces invalid signatures with newly created ones.
   Valid signatures are left unchanged. If `<keyid>` is provided, that key is
   used for signing; otherwise the configured default signing key is used.
+* `abort-if-invalid` will make this program die when encountering a signed
+  commit that is unable to be verified.
 
 Options for Frontends
 ~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
index 997097e..a32f72a 100644
--- a/Documentation/git-replay.adoc
+++ b/Documentation/git-replay.adoc
@@ -9,7 +9,8 @@
 SYNOPSIS
 --------
 [verse]
-(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>
+(EXPERIMENTAL!) 'git replay' ([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)
+			     [--ref=<ref>] [--ref-action=<mode>] <revision-range>
 
 DESCRIPTION
 -----------
@@ -26,7 +27,7 @@
 OPTIONS
 -------
 
---onto <newbase>::
+--onto=<newbase>::
 	Starting point at which to create the new commits.  May be any
 	valid commit, and not just an existing branch name.
 +
@@ -34,7 +35,7 @@
 updated to point at the new commits, similar to the way `git rebase --update-refs`
 updates multiple branches in the affected range.
 
---advance <branch>::
+--advance=<branch>::
 	Starting point at which to create the new commits; must be a
 	branch name.
 +
@@ -42,7 +43,7 @@
 point at the tip of the resulting history. This is different from `--onto`,
 which uses the target only as a starting point without updating it.
 
---revert <branch>::
+--revert=<branch>::
 	Starting point at which to create the reverted commits; must be a
 	branch name.
 +
@@ -65,6 +66,16 @@
 	Update all branches that point at commits in
 	<revision-range>. Requires `--onto`.
 
+--ref=<ref>::
+	Override which reference is updated with the result of the replay.
+	The ref must be fully qualified.
+	When used with `--onto`, the `<revision-range>` should have a
+	single tip and only the specified reference is updated instead of
+	inferring refs from the revision range.
+	When used with `--advance` or `--revert`, the specified reference is
+	updated instead of the branch given to those options.
+	This option is incompatible with `--contained`.
+
 --ref-action[=<mode>]::
 	Control how references are updated. The mode can be:
 +
@@ -79,8 +90,8 @@
 
 <revision-range>::
 	Range of commits to replay; see "Specifying Ranges" in
-	linkgit:git-rev-parse[1]. In `--advance <branch>` or
-	`--revert <branch>` mode, the range should have a single tip,
+	linkgit:git-rev-parse[1]. In `--advance=<branch>` or
+	`--revert=<branch>` mode, the range should have a single tip,
 	so that it's clear to which tip the advanced or reverted
 	<branch> should point. Any commits in the range whose changes
 	are already present in the branch the commits are being
@@ -127,7 +138,7 @@
 To simply rebase `mybranch` onto `target`:
 
 ------------
-$ git replay --onto target origin/main..mybranch
+$ git replay --onto=target origin/main..mybranch
 ------------
 
 The refs are updated atomically and no output is produced on success.
@@ -135,14 +146,14 @@
 To see what would be updated without actually updating:
 
 ------------
-$ git replay --ref-action=print --onto target origin/main..mybranch
+$ git replay --ref-action=print --onto=target origin/main..mybranch
 update refs/heads/mybranch ${NEW_mybranch_HASH} ${OLD_mybranch_HASH}
 ------------
 
 To cherry-pick the commits from mybranch onto target:
 
 ------------
-$ git replay --advance target origin/main..mybranch
+$ git replay --advance=target origin/main..mybranch
 ------------
 
 Note that the first two examples replay the exact same commits and on
@@ -154,7 +165,7 @@
 you'd really like to rebase the whole set?
 
 ------------
-$ git replay --contained --onto origin/main origin/main..tipbranch
+$ git replay --contained --onto=origin/main origin/main..tipbranch
 ------------
 
 All three branches (`branch1`, `branch2`, and `tipbranch`) are updated
@@ -165,7 +176,7 @@
 do:
 
 ------------
-$ git replay --onto origin/main ^base branch1 branch2 branch3
+$ git replay --onto=origin/main ^base branch1 branch2 branch3
 ------------
 
 This will simultaneously rebase `branch1`, `branch2`, and `branch3`,
@@ -176,7 +187,7 @@
 To revert commits on a branch:
 
 ------------
-$ git replay --revert main topic~2..topic
+$ git replay --revert=main topic~2..topic
 ------------
 
 This reverts the last two commits from `topic`, creating revert commits on
@@ -188,6 +199,16 @@
 commit-by-commit), consider using `git merge-tree --merge-base $TIP HEAD $BASE`
 which can avoid unnecessary merge conflicts.
 
+To replay onto a specific commit while updating a different reference:
+
+------------
+$ git replay --onto=112233 --ref=refs/heads/mybranch aabbcc..ddeeff
+------------
+
+This replays the range `aabbcc..ddeeff` onto commit `112233` and updates
+`refs/heads/mybranch` to point at the result. This can be useful when you want
+to use bare commit IDs instead of branch names.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/Documentation/line-range-options.adoc b/Documentation/line-range-options.adoc
index c44ba05..ecb2c79 100644
--- a/Documentation/line-range-options.adoc
+++ b/Documentation/line-range-options.adoc
@@ -12,4 +12,8 @@
 	(namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`,
 	`--name-only`, `--name-status`, `--check`) are not currently implemented.
 +
+Patch formatting options such as `--word-diff`, `--color-moved`,
+`--no-prefix`, and whitespace options (`-w`, `-b`) are supported,
+as are pickaxe options (`-S`, `-G`).
++
 include::line-range-format.adoc[]
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 92ea811..9c55beb 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEF_VER=v2.54.0-rc0
+DEF_VER=v2.54.0-rc1
 
 LF='
 '
diff --git a/add-patch.c b/add-patch.c
index 4e28e5c..f27edcb 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -558,8 +558,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 		strvec_push(&args,
 			    /* could be on an unborn branch */
 			    !strcmp("HEAD", s->revision) &&
-			    repo_get_oid(the_repository, "HEAD", &oid) ?
-			    empty_tree_oid_hex(the_repository->hash_algo) : s->revision);
+			    repo_get_oid(s->r, "HEAD", &oid) ?
+			    empty_tree_oid_hex(s->r->hash_algo) : s->revision);
 	}
 	color_arg_index = args.nr;
 	/* Use `--no-color` explicitly, just in case `diff.color = always`. */
@@ -1271,7 +1271,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
 				"removed, then the edit is\n"
 				"aborted and the hunk is left unchanged.\n"));
 
-	if (strbuf_edit_interactively(the_repository, &s->buf,
+	if (strbuf_edit_interactively(s->r, &s->buf,
 				      "addp-hunk-edit.diff", NULL) < 0)
 		return -1;
 
@@ -1679,7 +1679,7 @@ static size_t patch_update_file(struct add_p_state *s,
 		if (file_diff->hunk_nr) {
 			if (rendered_hunk_index != hunk_index) {
 				if (use_pager) {
-					setup_pager(the_repository);
+					setup_pager(s->r);
 					sigchain_push(SIGPIPE, SIG_IGN);
 				}
 				render_hunk(s, hunk, 0, colored, &s->buf);
diff --git a/apply.c b/apply.c
index 63d5e3c..4aa1694 100644
--- a/apply.c
+++ b/apply.c
@@ -1840,8 +1840,16 @@ static int parse_fragment(struct apply_state *state,
 			trailing++;
 			check_old_for_crlf(patch, line, len);
 			if (!state->apply_in_reverse &&
-			    state->ws_error_action == correct_ws_error)
-				check_whitespace(state, line, len, patch->ws_rule);
+			    state->ws_error_action == correct_ws_error) {
+				const char *test_line = line;
+				int test_len = len;
+				if (*line == '\n') {
+					test_line = " \n";
+					test_len = 2;
+				}
+				check_whitespace(state, test_line, test_len,
+						 patch->ws_rule);
+			}
 			break;
 		case '-':
 			if (!state->apply_in_reverse)
diff --git a/builtin/backfill.c b/builtin/backfill.c
index 2c5ce56..d794dd8 100644
--- a/builtin/backfill.c
+++ b/builtin/backfill.c
@@ -119,7 +119,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit
 		.repo = repo,
 		.current_batch = OID_ARRAY_INIT,
 		.min_batch_size = 50000,
-		.sparse = 0,
+		.sparse = -1,
 		.revs = REV_INFO_INIT,
 	};
 	struct option options[] = {
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index cd13a3a..d9fbad5 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -161,7 +161,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 
 	case 'e':
 		ret = !odb_has_object(the_repository->objects, &oid,
-				      HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR);
+				      ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR);
 		goto cleanup;
 
 	case 'w':
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 13621b0..2eb43a2 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -64,7 +64,8 @@ static int parse_opt_sign_mode(const struct option *opt,
 	if (unset)
 		return 0;
 
-	if (parse_sign_mode(arg, val, NULL))
+	if (parse_sign_mode(arg, val, NULL) || (*val == SIGN_STRIP_IF_INVALID) ||
+	    (*val == SIGN_SIGN_IF_INVALID) || (*val == SIGN_ABORT_IF_INVALID))
 		return error(_("unknown %s mode: %s"), opt->long_name, arg);
 
 	return 0;
@@ -822,12 +823,6 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
 			die(_("encountered signed commit %s; use "
 			      "--signed-commits=<mode> to handle it"),
 			    oid_to_hex(&commit->object.oid));
-		case SIGN_STRIP_IF_INVALID:
-			die(_("'strip-if-invalid' is not a valid mode for "
-			      "git fast-export with --signed-commits=<mode>"));
-		case SIGN_SIGN_IF_INVALID:
-			die(_("'sign-if-invalid' is not a valid mode for "
-			      "git fast-export with --signed-commits=<mode>"));
 		default:
 			BUG("invalid signed_commit_mode value %d", signed_commit_mode);
 		}
@@ -970,12 +965,6 @@ static void handle_tag(const char *name, struct tag *tag)
 				die(_("encountered signed tag %s; use "
 				      "--signed-tags=<mode> to handle it"),
 				    oid_to_hex(&tag->object.oid));
-			case SIGN_STRIP_IF_INVALID:
-				die(_("'strip-if-invalid' is not a valid mode for "
-				      "git fast-export with --signed-tags=<mode>"));
-			case SIGN_SIGN_IF_INVALID:
-				die(_("'sign-if-invalid' is not a valid mode for "
-				      "git fast-export with --signed-tags=<mode>"));
 			default:
 				BUG("invalid signed_commit_mode value %d", signed_commit_mode);
 			}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index 570fd04..82bc6dc 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -191,6 +191,7 @@ static const char *global_prefix;
 static enum sign_mode signed_tag_mode = SIGN_VERBATIM;
 static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
 static const char *signed_commit_keyid;
+static const char *signed_tag_keyid;
 
 /* Memory pools */
 static struct mem_pool fi_mem_pool = {
@@ -2892,6 +2893,9 @@ static void handle_signature_if_invalid(struct strbuf *new_data,
 	ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check);
 
 	if (ret) {
+		if (mode == SIGN_ABORT_IF_INVALID)
+			die(_("aborting due to invalid signature"));
+
 		warn_invalid_signature(&signature_check, msg->buf, mode);
 
 		if (mode == SIGN_SIGN_IF_INVALID) {
@@ -2983,6 +2987,7 @@ static void parse_new_commit(const char *arg)
 		case SIGN_VERBATIM:
 		case SIGN_STRIP_IF_INVALID:
 		case SIGN_SIGN_IF_INVALID:
+		case SIGN_ABORT_IF_INVALID:
 			import_one_signature(&sig_sha1, &sig_sha256, v);
 			break;
 
@@ -3068,7 +3073,8 @@ static void parse_new_commit(const char *arg)
 			encoding);
 
 	if ((signed_commit_mode == SIGN_STRIP_IF_INVALID ||
-	     signed_commit_mode == SIGN_SIGN_IF_INVALID) &&
+	     signed_commit_mode == SIGN_SIGN_IF_INVALID ||
+	     signed_commit_mode == SIGN_ABORT_IF_INVALID) &&
 	    (sig_sha1.hash_algo || sig_sha256.hash_algo))
 		handle_signature_if_invalid(&new_data, &sig_sha1, &sig_sha256,
 					    &msg, signed_commit_mode);
@@ -3084,7 +3090,50 @@ static void parse_new_commit(const char *arg)
 	b->last_commit = object_count_by_type[OBJ_COMMIT];
 }
 
-static void handle_tag_signature(struct strbuf *msg, const char *name)
+static void handle_tag_signature_if_invalid(struct strbuf *buf,
+					    struct strbuf *msg,
+					    size_t sig_offset)
+{
+	struct strbuf signature = STRBUF_INIT;
+	struct strbuf payload = STRBUF_INIT;
+	struct signature_check sigc = { 0 };
+
+	strbuf_addbuf(&payload, buf);
+	strbuf_addch(&payload, '\n');
+	strbuf_add(&payload, msg->buf, sig_offset);
+	strbuf_add(&signature, msg->buf + sig_offset, msg->len - sig_offset);
+
+	sigc.payload_type = SIGNATURE_PAYLOAD_TAG;
+	sigc.payload = strbuf_detach(&payload, &sigc.payload_len);
+
+	if (!check_signature(&sigc, signature.buf, signature.len))
+		goto out;
+
+	if (signed_tag_mode == SIGN_ABORT_IF_INVALID)
+		die(_("aborting due to invalid signature"));
+
+	strbuf_setlen(msg, sig_offset);
+
+	if (signed_tag_mode == SIGN_SIGN_IF_INVALID) {
+		strbuf_attach(&payload, sigc.payload, sigc.payload_len,
+			      sigc.payload_len + 1);
+		sigc.payload = NULL;
+		strbuf_reset(&signature);
+
+		if (sign_buffer(&payload, &signature, signed_tag_keyid,
+				SIGN_BUFFER_USE_DEFAULT_KEY))
+			die(_("failed to sign tag object"));
+
+		strbuf_addbuf(msg, &signature);
+	}
+
+out:
+	signature_check_clear(&sigc);
+	strbuf_release(&signature);
+	strbuf_release(&payload);
+}
+
+static void handle_tag_signature(struct strbuf *buf, struct strbuf *msg, const char *name)
 {
 	size_t sig_offset = parse_signed_buffer(msg->buf, msg->len);
 
@@ -3110,17 +3159,16 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
 		/* Truncate the buffer to remove the signature */
 		strbuf_setlen(msg, sig_offset);
 		break;
+	case SIGN_ABORT_IF_INVALID:
+	case SIGN_SIGN_IF_INVALID:
+	case SIGN_STRIP_IF_INVALID:
+		handle_tag_signature_if_invalid(buf, msg, sig_offset);
+		break;
 
 	/* Third, aborting modes */
 	case SIGN_ABORT:
 		die(_("encountered signed tag; use "
 		      "--signed-tags=<mode> to handle it"));
-	case SIGN_STRIP_IF_INVALID:
-		die(_("'strip-if-invalid' is not a valid mode for "
-		      "git fast-import with --signed-tags=<mode>"));
-	case SIGN_SIGN_IF_INVALID:
-		die(_("'sign-if-invalid' is not a valid mode for "
-		      "git fast-import with --signed-tags=<mode>"));
 	default:
 		BUG("invalid signed_tag_mode value %d from tag '%s'",
 		    signed_tag_mode, name);
@@ -3190,8 +3238,6 @@ static void parse_new_tag(const char *arg)
 	/* tag payload/message */
 	parse_data(&msg, 0, NULL);
 
-	handle_tag_signature(&msg, t->name);
-
 	/* build the tag object */
 	strbuf_reset(&new_data);
 
@@ -3203,6 +3249,9 @@ static void parse_new_tag(const char *arg)
 	if (tagger)
 		strbuf_addf(&new_data,
 			    "tagger %s\n", tagger);
+
+	handle_tag_signature(&new_data, &msg, t->name);
+
 	strbuf_addch(&new_data, '\n');
 	strbuf_addbuf(&new_data, &msg);
 	free(tagger);
@@ -3713,7 +3762,7 @@ static int parse_one_option(const char *option)
 		if (parse_sign_mode(option, &signed_commit_mode, &signed_commit_keyid))
 			usagef(_("unknown --signed-commits mode '%s'"), option);
 	} else if (skip_prefix(option, "signed-tags=", &option)) {
-		if (parse_sign_mode(option, &signed_tag_mode, NULL))
+		if (parse_sign_mode(option, &signed_tag_mode, &signed_tag_keyid))
 			usagef(_("unknown --signed-tags mode '%s'"), option);
 	} else if (!strcmp(option, "quiet")) {
 		show_stats = 0;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 4795b2a..a22c319 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -946,7 +946,7 @@ static int update_local_ref(struct ref *ref,
 	int fast_forward = 0;
 
 	if (!odb_has_object(the_repository->objects, &ref->new_oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		die(_("object %s not found"), oid_to_hex(&ref->new_oid));
 
 	if (oideq(&ref->old_oid, &ref->new_oid)) {
@@ -1396,7 +1396,7 @@ static int check_exist_and_connected(struct ref *ref_map)
 	 */
 	for (r = rm; r; r = r->next) {
 		if (!odb_has_object(the_repository->objects, &r->old_oid,
-				    HAS_OBJECT_RECHECK_PACKED))
+				    ODB_HAS_OBJECT_RECHECK_PACKED))
 			return -1;
 	}
 
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 9bab32e..248f8ff 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -1,4 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
 #include "builtin.h"
 #include "gettext.h"
 #include "hex.h"
@@ -42,8 +41,8 @@ static int check_full = 1;
 static int connectivity_only;
 static int check_strict;
 static int keep_cache_objects;
-static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
-static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
+static struct fsck_options fsck_walk_options;
+static struct fsck_options fsck_obj_options;
 static int errors_found;
 static int write_lost_and_found;
 static int verbose;
@@ -66,14 +65,14 @@ static const char *describe_object(const struct object_id *oid)
 	return fsck_describe_object(&fsck_walk_options, oid);
 }
 
-static const char *printable_type(const struct object_id *oid,
+static const char *printable_type(struct repository *repo,
+				  const struct object_id *oid,
 				  enum object_type type)
 {
 	const char *ret;
 
 	if (type == OBJ_NONE)
-		type = odb_read_object_info(the_repository->objects,
-					    oid, NULL);
+		type = odb_read_object_info(repo->objects, oid, NULL);
 
 	ret = type_name(type);
 	if (!ret)
@@ -82,17 +81,17 @@ static const char *printable_type(const struct object_id *oid,
 	return ret;
 }
 
-static int objerror(struct object *obj, const char *err)
+static int objerror(struct repository *repo, struct object *obj, const char *err)
 {
 	errors_found |= ERROR_OBJECT;
 	/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 	fprintf_ln(stderr, _("error in %s %s: %s"),
-		   printable_type(&obj->oid, obj->type),
+		   printable_type(repo, &obj->oid, obj->type),
 		   describe_object(&obj->oid), err);
 	return -1;
 }
 
-static int fsck_objects_error_func(struct fsck_options *o UNUSED,
+static int fsck_objects_error_func(struct fsck_options *o,
 				   void *fsck_report,
 				   enum fsck_msg_type msg_type,
 				   enum fsck_msg_id msg_id UNUSED,
@@ -106,13 +105,13 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
 	case FSCK_WARN:
 		/* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */
 		fprintf_ln(stderr, _("warning in %s %s: %s"),
-			   printable_type(oid, object_type),
+			   printable_type(o->repo, oid, object_type),
 			   describe_object(oid), message);
 		return 0;
 	case FSCK_ERROR:
 		/* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */
 		fprintf_ln(stderr, _("error in %s %s: %s"),
-			   printable_type(oid, object_type),
+			   printable_type(o->repo, oid, object_type),
 			   describe_object(oid), message);
 		return 1;
 	default:
@@ -124,7 +123,7 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED,
 static struct object_array pending;
 
 static int mark_object(struct object *obj, enum object_type type,
-		       void *data, struct fsck_options *options UNUSED)
+		       void *data, struct fsck_options *options)
 {
 	struct object *parent = data;
 
@@ -136,7 +135,7 @@ static int mark_object(struct object *obj, enum object_type type,
 	if (!obj) {
 		/* ... these references to parent->fld are safe here */
 		printf_ln(_("broken link from %7s %s"),
-			  printable_type(&parent->oid, parent->type),
+			  printable_type(options->repo, &parent->oid, parent->type),
 			  describe_object(&parent->oid));
 		printf_ln(_("broken link from %7s %s"),
 			  (type == OBJ_ANY ? _("unknown") : type_name(type)),
@@ -147,13 +146,13 @@ static int mark_object(struct object *obj, enum object_type type,
 
 	if (type != OBJ_ANY && obj->type != type)
 		/* ... and the reference to parent is safe here */
-		objerror(parent, _("wrong object type in link"));
+		objerror(options->repo, parent, _("wrong object type in link"));
 
 	if (obj->flags & REACHABLE)
 		return 0;
 	obj->flags |= REACHABLE;
 
-	if (is_promisor_object(the_repository, &obj->oid))
+	if (is_promisor_object(options->repo, &obj->oid))
 		/*
 		 * Further recursion does not need to be performed on this
 		 * object since it is a promisor object (so it does not need to
@@ -162,13 +161,13 @@ static int mark_object(struct object *obj, enum object_type type,
 		return 0;
 
 	if (!(obj->flags & HAS_OBJ)) {
-		if (parent && !odb_has_object(the_repository->objects, &obj->oid,
-					      HAS_OBJECT_RECHECK_PACKED)) {
+		if (parent && !odb_has_object(options->repo->objects, &obj->oid,
+					      ODB_HAS_OBJECT_RECHECK_PACKED)) {
 			printf_ln(_("broken link from %7s %s\n"
 				    "              to %7s %s"),
-				  printable_type(&parent->oid, parent->type),
+				  printable_type(options->repo, &parent->oid, parent->type),
 				  describe_object(&parent->oid),
-				  printable_type(&obj->oid, obj->type),
+				  printable_type(options->repo, &obj->oid, obj->type),
 				  describe_object(&obj->oid));
 			errors_found |= ERROR_REACHABLE;
 		}
@@ -181,7 +180,7 @@ static int mark_object(struct object *obj, enum object_type type,
 
 static void mark_object_reachable(struct object *obj)
 {
-	mark_object(obj, OBJ_ANY, NULL, NULL);
+	mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options);
 }
 
 static int traverse_one_object(struct object *obj)
@@ -195,13 +194,13 @@ static int traverse_one_object(struct object *obj)
 	return result;
 }
 
-static int traverse_reachable(void)
+static int traverse_reachable(struct repository *repo)
 {
 	struct progress *progress = NULL;
 	unsigned int nr = 0;
 	int result = 0;
 	if (show_progress)
-		progress = start_delayed_progress(the_repository,
+		progress = start_delayed_progress(repo,
 						  _("Checking connectivity"), 0);
 	while (pending.nr) {
 		result |= traverse_one_object(object_array_pop(&pending));
@@ -222,10 +221,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED,
 
 static int mark_unreachable_referents(const struct object_id *oid,
 				      struct object_info *oi UNUSED,
-				      void *data UNUSED)
+				      void *data)
 {
-	struct fsck_options options = FSCK_OPTIONS_DEFAULT;
-	struct object *obj = lookup_object(the_repository, oid);
+	struct repository *repo = data;
+	struct fsck_options options;
+	struct object *obj = lookup_object(data, oid);
 
 	if (!obj || !(obj->flags & HAS_OBJ))
 		return 0; /* not part of our original set */
@@ -237,12 +237,13 @@ static int mark_unreachable_referents(const struct object_id *oid,
 	 * (and we want to avoid parsing blobs).
 	 */
 	if (obj->type == OBJ_NONE) {
-		enum object_type type = odb_read_object_info(the_repository->objects,
+		enum object_type type = odb_read_object_info(repo->objects,
 							     &obj->oid, NULL);
 		if (type > 0)
 			object_as_type(obj, type, 0);
 	}
 
+	fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT);
 	options.walk = mark_used;
 	fsck_walk(obj, NULL, &options);
 	if (obj->type == OBJ_TREE)
@@ -254,7 +255,7 @@ static int mark_unreachable_referents(const struct object_id *oid,
 /*
  * Check a single reachable object
  */
-static void check_reachable_object(struct object *obj)
+static void check_reachable_object(struct repository *repo, struct object *obj)
 {
 	/*
 	 * We obviously want the object to be parsed,
@@ -262,12 +263,12 @@ static void check_reachable_object(struct object *obj)
 	 * do a full fsck
 	 */
 	if (!(obj->flags & HAS_OBJ)) {
-		if (is_promisor_object(the_repository, &obj->oid))
+		if (is_promisor_object(repo, &obj->oid))
 			return;
-		if (has_object_pack(the_repository, &obj->oid))
+		if (has_object_pack(repo, &obj->oid))
 			return; /* it is in pack - forget about it */
 		printf_ln(_("missing %s %s"),
-			  printable_type(&obj->oid, obj->type),
+			  printable_type(repo, &obj->oid, obj->type),
 			  describe_object(&obj->oid));
 		errors_found |= ERROR_REACHABLE;
 		return;
@@ -277,7 +278,7 @@ static void check_reachable_object(struct object *obj)
 /*
  * Check a single unreachable object
  */
-static void check_unreachable_object(struct object *obj)
+static void check_unreachable_object(struct repository *repo, struct object *obj)
 {
 	/*
 	 * Missing unreachable object? Ignore it. It's not like
@@ -294,7 +295,7 @@ static void check_unreachable_object(struct object *obj)
 	 */
 	if (show_unreachable) {
 		printf_ln(_("unreachable %s %s"),
-			  printable_type(&obj->oid, obj->type),
+			  printable_type(repo, &obj->oid, obj->type),
 			  describe_object(&obj->oid));
 		return;
 	}
@@ -314,22 +315,22 @@ static void check_unreachable_object(struct object *obj)
 	if (!(obj->flags & USED)) {
 		if (show_dangling)
 			printf_ln(_("dangling %s %s"),
-				  printable_type(&obj->oid, obj->type),
+				  printable_type(repo, &obj->oid, obj->type),
 				  describe_object(&obj->oid));
 		if (write_lost_and_found) {
-			char *filename = repo_git_path(the_repository, "lost-found/%s/%s",
+			char *filename = repo_git_path(repo, "lost-found/%s/%s",
 				obj->type == OBJ_COMMIT ? "commit" : "other",
 				describe_object(&obj->oid));
 			FILE *f;
 
-			if (safe_create_leading_directories_const(the_repository, filename)) {
+			if (safe_create_leading_directories_const(repo, filename)) {
 				error(_("could not create lost-found"));
 				free(filename);
 				return;
 			}
 			f = xfopen(filename, "w");
 			if (obj->type == OBJ_BLOB) {
-				if (odb_stream_blob_to_fd(the_repository->objects, fileno(f),
+				if (odb_stream_blob_to_fd(repo->objects, fileno(f),
 							  &obj->oid, NULL, 1))
 					die_errno(_("could not write '%s'"), filename);
 			} else
@@ -349,23 +350,23 @@ static void check_unreachable_object(struct object *obj)
 	 */
 }
 
-static void check_object(struct object *obj)
+static void check_object(struct repository *repo, struct object *obj)
 {
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid));
 
 	if (obj->flags & REACHABLE)
-		check_reachable_object(obj);
+		check_reachable_object(repo, obj);
 	else
-		check_unreachable_object(obj);
+		check_unreachable_object(repo, obj);
 }
 
-static void check_connectivity(void)
+static void check_connectivity(struct repository *repo)
 {
 	int i, max;
 
 	/* Traverse the pending reachable objects */
-	traverse_reachable();
+	traverse_reachable(repo);
 
 	/*
 	 * With --connectivity-only, we won't have actually opened and marked
@@ -383,24 +384,25 @@ static void check_connectivity(void)
 		 * and ignore any that weren't present in our earlier
 		 * traversal.
 		 */
-		odb_for_each_object(the_repository->objects, NULL,
-				    mark_unreachable_referents, NULL, 0);
+		odb_for_each_object(repo->objects, NULL,
+				    mark_unreachable_referents, repo, 0);
 	}
 
 	/* Look up all the requirements, warn about missing objects.. */
-	max = get_max_object_index(the_repository);
+	max = get_max_object_index(repo);
 	if (verbose)
 		fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max);
 
 	for (i = 0; i < max; i++) {
-		struct object *obj = get_indexed_object(the_repository, i);
+		struct object *obj = get_indexed_object(repo, i);
 
 		if (obj)
-			check_object(obj);
+			check_object(repo, obj);
 	}
 }
 
-static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
+static int fsck_obj(struct repository *repo,
+		    struct object *obj, void *buffer, unsigned long size)
 {
 	int err;
 
@@ -410,11 +412,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
 	if (verbose)
 		fprintf_ln(stderr, _("Checking %s %s"),
-			   printable_type(&obj->oid, obj->type),
+			   printable_type(repo, &obj->oid, obj->type),
 			   describe_object(&obj->oid));
 
 	if (fsck_walk(obj, NULL, &fsck_obj_options))
-		objerror(obj, _("broken links"));
+		objerror(repo, obj, _("broken links"));
 	err = fsck_object(obj, buffer, size, &fsck_obj_options);
 	if (err)
 		goto out;
@@ -432,7 +434,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 
 		if (show_tags && tag->tagged) {
 			printf_ln(_("tagged %s %s (%s) in %s"),
-				  printable_type(&tag->tagged->oid, tag->tagged->type),
+				  printable_type(repo, &tag->tagged->oid, tag->tagged->type),
 				  describe_object(&tag->tagged->oid),
 				  tag->tag,
 				  describe_object(&tag->object.oid));
@@ -446,15 +448,16 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size)
 }
 
 static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
-			   unsigned long size, void *buffer, int *eaten)
+			   unsigned long size, void *buffer, int *eaten, void *cb_data)
 {
+	struct repository *repo = cb_data;
+	struct object *obj;
+
 	/*
 	 * Note, buffer may be NULL if type is OBJ_BLOB. See
 	 * verify_packfile(), data_valid variable for details.
 	 */
-	struct object *obj;
-	obj = parse_object_buffer(the_repository, oid, type, size, buffer,
-				  eaten);
+	obj = parse_object_buffer(repo, oid, type, size, buffer, eaten);
 	if (!obj) {
 		errors_found |= ERROR_OBJECT;
 		return error(_("%s: object corrupt or missing"),
@@ -462,18 +465,19 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
 	}
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
-	return fsck_obj(obj, buffer, size);
+	return fsck_obj(repo, obj, buffer, size);
 }
 
 static int default_refs;
 
-static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
-	timestamp_t timestamp)
+static void fsck_handle_reflog_oid(struct repository *repo,
+				   const char *refname, struct object_id *oid,
+				   timestamp_t timestamp)
 {
 	struct object *obj;
 
 	if (!is_null_oid(oid)) {
-		obj = lookup_object(the_repository, oid);
+		obj = lookup_object(repo, oid);
 		if (obj && (obj->flags & HAS_OBJ)) {
 			if (timestamp)
 				fsck_put_object_name(&fsck_walk_options, oid,
@@ -481,7 +485,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
 						     refname, timestamp);
 			obj->flags |= USED;
 			mark_object_reachable(obj);
-		} else if (!is_promisor_object(the_repository, oid)) {
+		} else if (!is_promisor_object(repo, oid)) {
 			error(_("%s: invalid reflog entry %s"),
 			      refname, oid_to_hex(oid));
 			errors_found |= ERROR_REACHABLE;
@@ -493,8 +497,10 @@ static int fsck_handle_reflog_ent(const char *refname,
 				  struct object_id *ooid, struct object_id *noid,
 				  const char *email UNUSED,
 				  timestamp_t timestamp, int tz UNUSED,
-				  const char *message UNUSED, void *cb_data UNUSED)
+				  const char *message UNUSED, void *cb_data)
 {
+	struct repository *repo = cb_data;
+
 	if (now && timestamp > now)
 		return 0;
 
@@ -502,19 +508,20 @@ static int fsck_handle_reflog_ent(const char *refname,
 		fprintf_ln(stderr, _("Checking reflog %s->%s"),
 			   oid_to_hex(ooid), oid_to_hex(noid));
 
-	fsck_handle_reflog_oid(refname, ooid, 0);
-	fsck_handle_reflog_oid(refname, noid, timestamp);
+	fsck_handle_reflog_oid(repo, refname, ooid, 0);
+	fsck_handle_reflog_oid(repo, refname, noid, timestamp);
 	return 0;
 }
 
 static int fsck_handle_reflog(const char *logname, void *cb_data)
 {
 	struct strbuf refname = STRBUF_INIT;
+	struct worktree *wt = cb_data;
 
-	strbuf_worktree_ref(cb_data, &refname, logname);
-	refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+	strbuf_worktree_ref(wt, &refname, logname);
+	refs_for_each_reflog_ent(get_main_ref_store(wt->repo),
 				 refname.buf, fsck_handle_reflog_ent,
-				 NULL);
+				 wt->repo);
 	strbuf_release(&refname);
 	return 0;
 }
@@ -532,14 +539,20 @@ struct snapshot {
 	/* TODO: Consider also snapshotting the index of each worktree. */
 };
 
+struct snapshot_ref_data {
+	struct repository *repo;
+	struct snapshot *snap;
+};
+
 static int snapshot_ref(const struct reference *ref, void *cb_data)
 {
-	struct snapshot *snap = cb_data;
+	struct snapshot_ref_data *data = cb_data;
+	struct snapshot *snap = data->snap;
 	struct object *obj;
 
-	obj = parse_object(the_repository, ref->oid);
+	obj = parse_object(data->repo, ref->oid);
 	if (!obj) {
-		if (is_promisor_object(the_repository, ref->oid)) {
+		if (is_promisor_object(data->repo, ref->oid)) {
 			/*
 			 * Increment default_refs anyway, because this is a
 			 * valid ref.
@@ -567,11 +580,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data)
 	return 0;
 }
 
-static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
+static int fsck_handle_ref(const struct reference *ref, void *cb_data)
 {
+	struct repository *repo = cb_data;
 	struct object *obj;
 
-	obj = parse_object(the_repository, ref->oid);
+	obj = parse_object(repo, ref->oid);
 	obj->flags |= USED;
 	fsck_put_object_name(&fsck_walk_options,
 			     ref->oid, "%s", ref->name);
@@ -580,11 +594,16 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED)
 	return 0;
 }
 
-static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
+static void snapshot_refs(struct repository *repo,
+			  struct snapshot *snap, int argc, const char **argv)
 {
 	struct refs_for_each_ref_options opts = {
 		.flags = REFS_FOR_EACH_INCLUDE_BROKEN,
 	};
+	struct snapshot_ref_data data = {
+		.repo = repo,
+		.snap = snap,
+	};
 	struct worktree **worktrees, **p;
 	const char *head_points_at;
 	struct object_id head_oid;
@@ -592,13 +611,13 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 	for (int i = 0; i < argc; i++) {
 		const char *arg = argv[i];
 		struct object_id oid;
-		if (!repo_get_oid(the_repository, arg, &oid)) {
+		if (!repo_get_oid(repo, arg, &oid)) {
 			struct reference ref = {
 				.name = arg,
 				.oid = &oid,
 			};
 
-			snapshot_ref(&ref, snap);
+			snapshot_ref(&ref, &data);
 			continue;
 		}
 		error(_("invalid parameter: expected sha1, got '%s'"), arg);
@@ -610,8 +629,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 		return;
 	}
 
-	refs_for_each_ref_ext(get_main_ref_store(the_repository),
-			      snapshot_ref, snap, &opts);
+	refs_for_each_ref_ext(get_main_ref_store(repo),
+			      snapshot_ref, &data, &opts);
 
 	worktrees = get_worktrees();
 	for (p = worktrees; *p; p++) {
@@ -620,7 +639,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 
 		strbuf_worktree_ref(wt, &refname, "HEAD");
 
-		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+		head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo),
 							 refname.buf, 0, &head_oid, NULL);
 
 		if (head_points_at && !is_null_oid(&head_oid)) {
@@ -629,7 +648,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv)
 				.oid = &head_oid,
 			};
 
-			snapshot_ref(&ref, snap);
+			snapshot_ref(&ref, &data);
 		}
 		strbuf_release(&refname);
 
@@ -653,7 +672,7 @@ static void free_snapshot_refs(struct snapshot *snap)
 	free(snap->ref);
 }
 
-static void process_refs(struct snapshot *snap)
+static void process_refs(struct repository *repo, struct snapshot *snap)
 {
 	struct worktree **worktrees, **p;
 
@@ -662,7 +681,7 @@ static void process_refs(struct snapshot *snap)
 			.name = snap->ref[i].refname,
 			.oid = &snap->ref[i].oid,
 		};
-		fsck_handle_ref(&ref, NULL);
+		fsck_handle_ref(&ref, repo);
 	}
 
 	if (include_reflogs) {
@@ -694,27 +713,28 @@ static void process_refs(struct snapshot *snap)
 	}
 }
 
-struct for_each_loose_cb
-{
+struct for_each_loose_cb {
+	struct repository *repo;
 	struct progress *progress;
 };
 
 static int fsck_loose(const struct object_id *oid, const char *path,
-		      void *data UNUSED)
+		      void *cb_data)
 {
+	struct for_each_loose_cb *data = cb_data;
 	struct object *obj;
 	enum object_type type = OBJ_NONE;
 	unsigned long size;
 	void *contents = NULL;
 	int eaten;
 	struct object_info oi = OBJECT_INFO_INIT;
-	struct object_id real_oid = *null_oid(the_hash_algo);
+	struct object_id real_oid = *null_oid(data->repo->hash_algo);
 	int err = 0;
 
 	oi.sizep = &size;
 	oi.typep = &type;
 
-	if (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) {
+	if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) {
 		if (contents && !oideq(&real_oid, oid))
 			err = error(_("%s: hash-path mismatch, found at: %s"),
 				    oid_to_hex(&real_oid), path);
@@ -731,7 +751,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
 	if (!contents && type != OBJ_BLOB)
 		BUG("read_loose_object streamed a non-blob");
 
-	obj = parse_object_buffer(the_repository, oid, type, size,
+	obj = parse_object_buffer(data->repo, oid, type, size,
 				  contents, &eaten);
 
 	if (!obj) {
@@ -745,7 +765,7 @@ static int fsck_loose(const struct object_id *oid, const char *path,
 
 	obj->flags &= ~(REACHABLE | SEEN);
 	obj->flags |= HAS_OBJ;
-	if (fsck_obj(obj, contents, size))
+	if (fsck_obj(data->repo, obj, contents, size))
 		errors_found |= ERROR_OBJECT;
 
 	if (!eaten)
@@ -769,10 +789,11 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data)
 	return 0;
 }
 
-static void fsck_source(struct odb_source *source)
+static void fsck_source(struct repository *repo, struct odb_source *source)
 {
 	struct progress *progress = NULL;
 	struct for_each_loose_cb cb_data = {
+		.repo = source->odb->repo,
 		.progress = progress,
 	};
 
@@ -780,7 +801,7 @@ static void fsck_source(struct odb_source *source)
 		fprintf_ln(stderr, _("Checking object directory"));
 
 	if (show_progress)
-		progress = start_progress(the_repository,
+		progress = start_progress(repo,
 					  _("Checking object directories"), 256);
 
 	for_each_loose_file_in_source(source, fsck_loose,
@@ -789,7 +810,7 @@ static void fsck_source(struct odb_source *source)
 	stop_progress(&progress);
 }
 
-static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
+static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path)
 {
 	int i;
 	int err = 0;
@@ -798,7 +819,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 		fprintf_ln(stderr, _("Checking cache tree of %s"), index_path);
 
 	if (0 <= it->entry_count) {
-		struct object *obj = parse_object(the_repository, &it->oid);
+		struct object *obj = parse_object(repo, &it->oid);
 		if (!obj) {
 			error(_("%s: invalid sha1 pointer in cache-tree of %s"),
 			      oid_to_hex(&it->oid), index_path);
@@ -809,10 +830,10 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path)
 		fsck_put_object_name(&fsck_walk_options, &it->oid, ":");
 		mark_object_reachable(obj);
 		if (obj->type != OBJ_TREE)
-			err |= objerror(obj, _("non-tree in cache-tree"));
+			err |= objerror(repo, obj, _("non-tree in cache-tree"));
 	}
 	for (i = 0; i < it->subtree_nr; i++)
-		err |= fsck_cache_tree(it->down[i]->cache_tree, index_path);
+		err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path);
 	return err;
 }
 
@@ -838,7 +859,7 @@ static int fsck_resolve_undo(struct index_state *istate,
 			if (!ru->mode[i] || !S_ISREG(ru->mode[i]))
 				continue;
 
-			obj = parse_object(the_repository, &ru->oid[i]);
+			obj = parse_object(istate->repo, &ru->oid[i]);
 			if (!obj) {
 				error(_("%s: invalid sha1 pointer in resolve-undo of %s"),
 				      oid_to_hex(&ru->oid[i]),
@@ -870,7 +891,7 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 		mode = istate->cache[i]->ce_mode;
 		if (S_ISGITLINK(mode))
 			continue;
-		blob = lookup_blob(the_repository,
+		blob = lookup_blob(istate->repo,
 				   &istate->cache[i]->oid);
 		if (!blob)
 			continue;
@@ -883,15 +904,16 @@ static void fsck_index(struct index_state *istate, const char *index_path,
 		mark_object_reachable(obj);
 	}
 	if (istate->cache_tree)
-		fsck_cache_tree(istate->cache_tree, index_path);
+		fsck_cache_tree(istate->repo, istate->cache_tree, index_path);
 	fsck_resolve_undo(istate, index_path);
 }
 
 static int mark_object_for_connectivity(const struct object_id *oid,
 					struct object_info *oi UNUSED,
-					void *cb_data UNUSED)
+					void *cb_data)
 {
-	struct object *obj = lookup_unknown_object(the_repository, oid);
+	struct repository *repo = cb_data;
+	struct object *obj = lookup_unknown_object(repo, oid);
 	obj->flags |= HAS_OBJ;
 	return 0;
 }
@@ -906,7 +928,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress)
 	if (show_progress) {
 		repo_for_each_pack(r, p)
 			pack_count++;
-		progress = start_delayed_progress(the_repository,
+		progress = start_delayed_progress(r,
 						  "Verifying reverse pack-indexes", pack_count);
 		pack_count = 0;
 	}
@@ -986,7 +1008,7 @@ static struct option fsck_opts[] = {
 int cmd_fsck(int argc,
 	     const char **argv,
 	     const char *prefix,
-	     struct repository *repo UNUSED)
+	     struct repository *repo)
 {
 	struct odb_source *source;
 	struct snapshot snap = {
@@ -1004,7 +1026,10 @@ int cmd_fsck(int argc,
 
 	argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
 
+	fsck_options_init(&fsck_walk_options, repo, FSCK_OPTIONS_DEFAULT);
 	fsck_walk_options.walk = mark_object;
+
+	fsck_options_init(&fsck_obj_options, repo, FSCK_OPTIONS_DEFAULT);
 	fsck_obj_options.walk = mark_used;
 	fsck_obj_options.error_func = fsck_objects_error_func;
 	if (check_strict)
@@ -1023,11 +1048,11 @@ int cmd_fsck(int argc,
 	if (name_objects)
 		fsck_enable_object_names(&fsck_walk_options);
 
-	repo_config(the_repository, git_fsck_config, &fsck_obj_options);
-	prepare_repo_settings(the_repository);
+	repo_config(repo, git_fsck_config, &fsck_obj_options);
+	prepare_repo_settings(repo);
 
 	if (check_references)
-		fsck_refs(the_repository);
+		fsck_refs(repo);
 
 	/*
 	 * Take a snapshot of the refs before walking objects to avoid looking
@@ -1035,18 +1060,18 @@ int cmd_fsck(int argc,
 	 * objects. We can still walk over new objects that are added during the
 	 * execution of fsck but won't miss any objects that were reachable.
 	 */
-	snapshot_refs(&snap, argc, argv);
+	snapshot_refs(repo, &snap, argc, argv);
 
 	/* Ensure we get a "fresh" view of the odb */
-	odb_reprepare(the_repository->objects);
+	odb_reprepare(repo->objects);
 
 	if (connectivity_only) {
-		odb_for_each_object(the_repository->objects, NULL,
-				    mark_object_for_connectivity, NULL, 0);
+		odb_for_each_object(repo->objects, NULL,
+				    mark_object_for_connectivity, repo, 0);
 	} else {
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next)
-			fsck_source(source);
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next)
+			fsck_source(repo, source);
 
 		if (check_full) {
 			struct packed_git *p;
@@ -1054,20 +1079,20 @@ int cmd_fsck(int argc,
 			struct progress *progress = NULL;
 
 			if (show_progress) {
-				repo_for_each_pack(the_repository, p) {
+				repo_for_each_pack(repo, p) {
 					if (open_pack_index(p))
 						continue;
 					total += p->num_objects;
 				}
 
-				progress = start_progress(the_repository,
+				progress = start_progress(repo,
 							  _("Checking objects"), total);
 			}
 
-			repo_for_each_pack(the_repository, p) {
+			repo_for_each_pack(repo, p) {
 				/* verify gives error messages itself */
-				if (verify_pack(the_repository,
-						p, fsck_obj_buffer,
+				if (verify_pack(repo,
+						p, fsck_obj_buffer, repo,
 						progress, count))
 					errors_found |= ERROR_PACK;
 				count += p->num_objects;
@@ -1080,7 +1105,7 @@ int cmd_fsck(int argc,
 	}
 
 	/* Process the snapshotted refs and the reflogs. */
-	process_refs(&snap);
+	process_refs(repo, &snap);
 
 	/* If not given any explicit objects, process index files too. */
 	if (!argc)
@@ -1100,7 +1125,7 @@ int cmd_fsck(int argc,
 		for (p = worktrees; *p; p++) {
 			struct worktree *wt = *p;
 			struct index_state istate =
-				INDEX_STATE_INIT(the_repository);
+				INDEX_STATE_INIT(repo);
 			char *path, *wt_gitdir;
 
 			/*
@@ -1121,17 +1146,17 @@ int cmd_fsck(int argc,
 		free_worktrees(worktrees);
 	}
 
-	errors_found |= check_pack_rev_indexes(the_repository, show_progress);
-	if (verify_bitmap_files(the_repository))
+	errors_found |= check_pack_rev_indexes(repo, show_progress);
+	if (verify_bitmap_files(repo))
 		errors_found |= ERROR_BITMAP;
 
-	check_connectivity();
+	check_connectivity(repo);
 
-	if (the_repository->settings.core_commit_graph) {
+	if (repo->settings.core_commit_graph) {
 		struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
 
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next) {
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next) {
 			child_process_init(&commit_graph_verify);
 			commit_graph_verify.git_cmd = 1;
 			strvec_pushl(&commit_graph_verify.args, "commit-graph",
@@ -1145,11 +1170,11 @@ int cmd_fsck(int argc,
 		}
 	}
 
-	if (the_repository->settings.core_multi_pack_index) {
+	if (repo->settings.core_multi_pack_index) {
 		struct child_process midx_verify = CHILD_PROCESS_INIT;
 
-		odb_prepare_alternates(the_repository->objects);
-		for (source = the_repository->objects->sources; source; source = source->next) {
+		odb_prepare_alternates(repo->objects);
+		for (source = repo->objects->sources; source; source = source->next) {
 			child_process_init(&midx_verify);
 			midx_verify.git_cmd = 1;
 			strvec_pushl(&midx_verify.args, "multi-pack-index",
diff --git a/builtin/history.c b/builtin/history.c
index 568dc75..9526938 100644
--- a/builtin/history.c
+++ b/builtin/history.c
@@ -437,8 +437,8 @@ static int cmd_history_reword(int argc,
 	enum ref_action action = REF_ACTION_DEFAULT;
 	int dry_run = 0;
 	struct option options[] = {
-		OPT_CALLBACK_F(0, "update-refs", &action, N_("<action>"),
-			       N_("control which refs should be updated (branches|head)"),
+		OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
+			       N_("control which refs should be updated"),
 			       PARSE_OPT_NONEG, parse_ref_action),
 		OPT_BOOL('n', "dry-run", &dry_run,
 			 N_("perform a dry-run without updating any refs")),
@@ -666,8 +666,8 @@ static int cmd_history_split(int argc,
 	enum ref_action action = REF_ACTION_DEFAULT;
 	int dry_run = 0;
 	struct option options[] = {
-		OPT_CALLBACK_F(0, "update-refs", &action, N_("<refs>"),
-			       N_("control ref update behavior (branches|head|print)"),
+		OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
+			       N_("control ref update behavior"),
 			       PARSE_OPT_NONEG, parse_ref_action),
 		OPT_BOOL('n', "dry-run", &dry_run,
 			 N_("perform a dry-run without updating any refs")),
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index d1e4727..ca7784d 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -136,7 +136,7 @@ static int nr_threads;
 static int from_stdin;
 static int strict;
 static int do_fsck_object;
-static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
+static struct fsck_options fsck_options;
 static int verbose;
 static const char *progress_title;
 static int show_resolving_progress;
@@ -891,7 +891,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
 	if (startup_info->have_repository) {
 		read_lock();
 		collision_test_needed = odb_has_object(the_repository->objects, oid,
-						       HAS_OBJECT_FETCH_PROMISOR);
+						       ODB_HAS_OBJECT_FETCH_PROMISOR);
 		read_unlock();
 	}
 
@@ -1908,6 +1908,8 @@ int cmd_index_pack(int argc,
 	show_usage_if_asked(argc, argv, index_pack_usage);
 
 	disable_replace_refs();
+
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 	fsck_options.walk = mark_link;
 
 	reset_pack_idx_option(&opts);
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 7cf6e12..f40264a 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -16,7 +16,7 @@ static char const * const builtin_mktag_usage[] = {
 };
 static int option_strict = 1;
 
-static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static struct fsck_options fsck_options;
 
 static int mktag_fsck_error_func(struct fsck_options *o UNUSED,
 				 void *fsck_report UNUSED,
@@ -75,7 +75,7 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
 int cmd_mktag(int argc,
 	      const char **argv,
 	      const char *prefix,
-	      struct repository *repo UNUSED)
+	      struct repository *repo)
 {
 	static struct option builtin_mktag_options[] = {
 		OPT_BOOL(0, "strict", &option_strict,
@@ -94,6 +94,7 @@ int cmd_mktag(int argc,
 	if (strbuf_read(&buf, 0, 0) < 0)
 		die_errno(_("could not read from stdin"));
 
+	fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
 	fsck_options.error_func = mktag_fsck_error_func;
 	fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY,
 				   FSCK_WARN);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index cb3656a..dada558 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -1384,32 +1384,16 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
 	return 0;
 }
 
-/*
- * NEEDSWORK: we should consolidate various implementations of "are we
- * on an unborn branch?" test into one, and make the unified one more
- * robust. !get_sha1() based check used here and elsewhere would not
- * allow us to tell an unborn branch from corrupt ref, for example.
- * For the purpose of fixing "deploy-to-update does not work when
- * pushing into an empty repository" issue, this should suffice for
- * now.
- */
-static int head_has_history(void)
-{
-	struct object_id oid;
-
-	return !repo_get_oid(the_repository, "HEAD", &oid);
-}
-
 static const char *push_to_deploy(unsigned char *sha1,
 				  struct strvec *env,
-				  const char *work_tree)
+				  const struct worktree *worktree)
 {
 	struct child_process child = CHILD_PROCESS_INIT;
 
 	strvec_pushl(&child.args, "update-index", "-q", "--ignore-submodules",
 		     "--refresh", NULL);
 	strvec_pushv(&child.env, env->v);
-	child.dir = work_tree;
+	child.dir = worktree->path;
 	child.no_stdin = 1;
 	child.stdout_to_stderr = 1;
 	child.git_cmd = 1;
@@ -1421,7 +1405,7 @@ static const char *push_to_deploy(unsigned char *sha1,
 	strvec_pushl(&child.args, "diff-files", "--quiet",
 		     "--ignore-submodules", "--", NULL);
 	strvec_pushv(&child.env, env->v);
-	child.dir = work_tree;
+	child.dir = worktree->path;
 	child.no_stdin = 1;
 	child.stdout_to_stderr = 1;
 	child.git_cmd = 1;
@@ -1431,9 +1415,16 @@ static const char *push_to_deploy(unsigned char *sha1,
 	child_process_init(&child);
 	strvec_pushl(&child.args, "diff-index", "--quiet", "--cached",
 		     "--ignore-submodules",
-		     /* diff-index with either HEAD or an empty tree */
-		     head_has_history() ? "HEAD" : empty_tree_oid_hex(the_repository->hash_algo),
-		     "--", NULL);
+		     /*
+		      * diff-index with either HEAD or an empty tree
+		      *
+		      * NEEDSWORK: is_null_oid() cannot know whether it's an
+		      * unborn HEAD or a corrupt ref. It works for now because
+		      * it's only needed to know if we are comparing HEAD or an
+		      * empty tree.
+		      */
+		     !is_null_oid(&worktree->head_oid) ? "HEAD" :
+		     empty_tree_oid_hex(the_repository->hash_algo), "--", NULL);
 	strvec_pushv(&child.env, env->v);
 	child.no_stdin = 1;
 	child.no_stdout = 1;
@@ -1446,7 +1437,7 @@ static const char *push_to_deploy(unsigned char *sha1,
 	strvec_pushl(&child.args, "read-tree", "-u", "-m", hash_to_hex(sha1),
 		     NULL);
 	strvec_pushv(&child.env, env->v);
-	child.dir = work_tree;
+	child.dir = worktree->path;
 	child.no_stdin = 1;
 	child.no_stdout = 1;
 	child.stdout_to_stderr = 0;
@@ -1494,7 +1485,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
 
 	retval = push_to_checkout(sha1, &invoked_hook, &env, worktree->path);
 	if (!invoked_hook)
-		retval = push_to_deploy(sha1, &env, worktree->path);
+		retval = push_to_deploy(sha1, &env, worktree);
 
 	strvec_clear(&env);
 	free(git_dir);
@@ -1550,7 +1541,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 
 	if (!is_null_oid(new_oid) &&
 	    !odb_has_object(the_repository->objects, new_oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		error("unpack should have generated %s, "
 		      "but I can't find it!", oid_to_hex(new_oid));
 		ret = "bad pack";
diff --git a/builtin/refs.c b/builtin/refs.c
index 3064f88..e3125bc 100644
--- a/builtin/refs.c
+++ b/builtin/refs.c
@@ -78,9 +78,9 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
 }
 
 static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
-			   struct repository *repo UNUSED)
+			   struct repository *repo)
 {
-	struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT;
+	struct fsck_options fsck_refs_options;
 	struct worktree **worktrees;
 	const char * const verify_usage[] = {
 		REFS_VERIFY_USAGE,
@@ -93,6 +93,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix,
 	};
 	int ret = 0;
 
+	fsck_options_init(&fsck_refs_options, repo, FSCK_OPTIONS_REFS);
+
 	argc = parse_options(argc, argv, prefix, options, verify_usage, 0);
 	if (argc)
 		usage(_("'git refs verify' takes no arguments"));
diff --git a/builtin/remote.c b/builtin/remote.c
index 0fddaa1..de989ea 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -473,7 +473,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
 		else if (is_null_oid(&ref->old_oid))
 			info->status = PUSH_STATUS_CREATE;
 		else if (odb_has_object(the_repository->objects, &ref->old_oid,
-					HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) &&
+					ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) &&
 			 ref_newer(&ref->new_oid, &ref->old_oid))
 			info->status = PUSH_STATUS_FASTFORWARD;
 		else
diff --git a/builtin/replay.c b/builtin/replay.c
index a0879b0..39e3a86 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -84,25 +84,33 @@ int cmd_replay(int argc,
 
 	const char *const replay_usage[] = {
 		N_("(EXPERIMENTAL!) git replay "
-		   "([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) "
-		   "[--ref-action[=<mode>]] <revision-range>"),
+		   "([--contained] --onto=<newbase> | --advance=<branch> | --revert=<branch>)\n"
+		   "[--ref=<ref>] [--ref-action=<mode>] <revision-range>"),
 		NULL
 	};
 	struct option replay_options[] = {
-		OPT_STRING(0, "advance", &opts.advance,
-			   N_("branch"),
-			   N_("make replay advance given branch")),
-		OPT_STRING(0, "onto", &opts.onto,
-			   N_("revision"),
-			   N_("replay onto given commit")),
 		OPT_BOOL(0, "contained", &opts.contained,
 			 N_("update all branches that point at commits in <revision-range>")),
-		OPT_STRING(0, "revert", &opts.revert,
-			   N_("branch"),
-			   N_("revert commits onto given branch")),
-		OPT_STRING(0, "ref-action", &ref_action,
-			   N_("mode"),
-			   N_("control ref update behavior (update|print)")),
+		OPT_STRING_F(0, "onto", &opts.onto,
+			     N_("revision"),
+			     N_("replay onto given commit"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "advance", &opts.advance,
+			     N_("branch"),
+			     N_("make replay advance given branch"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "revert", &opts.revert,
+			     N_("branch"),
+			     N_("revert commits onto given branch"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "ref", &opts.ref,
+			     N_("branch"),
+			     N_("reference to update with result"),
+			     PARSE_OPT_NONEG),
+		OPT_STRING_F(0, "ref-action", &ref_action,
+			     N_("mode"),
+			     N_("control ref update behavior (update|print)"),
+			     PARSE_OPT_NONEG),
 		OPT_END()
 	};
 
@@ -122,6 +130,8 @@ int cmd_replay(int argc,
 				  opts.contained, "--contained");
 	die_for_incompatible_opt2(!!opts.revert, "--revert",
 				  opts.contained, "--contained");
+	die_for_incompatible_opt2(!!opts.ref, "--ref",
+				  !!opts.contained, "--contained");
 
 	/* Parse ref action mode from command line or config */
 	ref_mode = get_ref_action_mode(repo, ref_action);
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 5d31ace..d508441 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -37,7 +37,7 @@ static void show_one(const struct show_one_options *opts,
 	struct object_id peeled;
 
 	if (!odb_has_object(the_repository->objects, ref->oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		die("git show-ref: bad ref %s (%s)", ref->name,
 		    oid_to_hex(ref->oid));
 
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 6fc64e9..e01cf6e 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -29,7 +29,7 @@ static unsigned int offset, len;
 static off_t consumed_bytes;
 static off_t max_input_size;
 static struct git_hash_ctx ctx;
-static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;
+static struct fsck_options fsck_options;
 static struct progress *progress;
 
 /*
@@ -449,7 +449,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
 		if (!delta_data)
 			return;
 		if (odb_has_object(the_repository->objects, &base_oid,
-				   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			; /* Ok we have this one */
 		else if (resolve_against_held(nr, &base_oid,
 					      delta_data, delta_size))
@@ -613,7 +613,7 @@ static void unpack_all(void)
 int cmd_unpack_objects(int argc,
 		       const char **argv,
 		       const char *prefix UNUSED,
-		       struct repository *repo UNUSED)
+		       struct repository *repo)
 {
 	int i;
 	struct object_id oid;
@@ -627,6 +627,8 @@ int cmd_unpack_objects(int argc,
 
 	show_usage_if_asked(argc, argv, unpack_usage);
 
+	fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT);
+
 	for (i = 1 ; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/cache-tree.c b/cache-tree.c
index 60bcc07..7881b42 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -239,7 +239,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
 		return 0;
 	if (it->entry_count < 0 ||
 	    odb_has_object(the_repository->objects, &it->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return 0;
 	for (i = 0; i < it->subtree_nr; i++) {
 		if (!cache_tree_fully_valid(it->down[i]->cache_tree))
@@ -292,7 +292,7 @@ static int update_one(struct cache_tree *it,
 
 	if (0 <= it->entry_count &&
 	    odb_has_object(the_repository->objects, &it->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return it->entry_count;
 
 	/*
@@ -400,7 +400,7 @@ static int update_one(struct cache_tree *it,
 		if (is_null_oid(oid) ||
 		    (!ce_missing_ok &&
 		     !odb_has_object(the_repository->objects, oid,
-				     HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))) {
+				     ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))) {
 			strbuf_release(&buffer);
 			if (expected_missing)
 				return -1;
@@ -448,7 +448,7 @@ static int update_one(struct cache_tree *it,
 		struct object_id oid;
 		hash_object_file(the_hash_algo, buffer.buf, buffer.len,
 				 OBJ_TREE, &oid);
-		if (odb_has_object(the_repository->objects, &oid, HAS_OBJECT_RECHECK_PACKED))
+		if (odb_has_object(the_repository->objects, &oid, ODB_HAS_OBJECT_RECHECK_PACKED))
 			oidcpy(&it->oid, &oid);
 		else
 			to_invalidate = 1;
@@ -456,7 +456,7 @@ static int update_one(struct cache_tree *it,
 		hash_object_file(the_hash_algo, buffer.buf, buffer.len,
 				 OBJ_TREE, &it->oid);
 	} else if (odb_write_object_ext(the_repository->objects, buffer.buf, buffer.len, OBJ_TREE,
-					&it->oid, NULL, flags & WRITE_TREE_SILENT ? WRITE_OBJECT_SILENT : 0)) {
+					&it->oid, NULL, flags & WRITE_TREE_SILENT ? ODB_WRITE_OBJECT_SILENT : 0)) {
 		strbuf_release(&buffer);
 		return -1;
 	}
@@ -488,12 +488,12 @@ int cache_tree_update(struct index_state *istate, int flags)
 		prefetch_cache_entries(istate, must_check_existence);
 
 	trace_performance_enter();
-	trace2_region_enter("cache_tree", "update", the_repository);
+	trace2_region_enter("cache_tree", "update", istate->repo);
 	transaction = odb_transaction_begin(the_repository->objects);
 	i = update_one(istate->cache_tree, istate->cache, istate->cache_nr,
 		       "", 0, &skip, flags);
 	odb_transaction_commit(transaction);
-	trace2_region_leave("cache_tree", "update", the_repository);
+	trace2_region_leave("cache_tree", "update", istate->repo);
 	trace_performance_leave("cache_tree_update");
 	if (i < 0)
 		return i;
diff --git a/commit-graph.c b/commit-graph.c
index df4b4a1..9abe62b 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f,
 	return 0;
 }
 
+/*
+ * Compute the generation offset between the commit date and its generation.
+ * This is what's ultimately stored as generation number in the commit graph.
+ *
+ * Note that the computation of the commit date is more involved than you might
+ * think. Instead of using the full commit date, we're in fact masking bits so
+ * that only the 34 lowest bits are considered. This results from the fact that
+ * commit graphs themselves only ever store 34 bits of the commit date
+ * themselves.
+ *
+ * This means that if we have a commit date that exceeds 34 bits we'll end up
+ * in situations where depending on whether the commit has been parsed from the
+ * object database or the commit graph we'll have different dates, where the
+ * ones parsed from the object database would have full 64 bit precision.
+ *
+ * But ultimately, we only ever want the offset to be relative to what we
+ * actually end up storing on disk, and hence we have to mask all the other
+ * bits.
+ */
+static timestamp_t compute_generation_offset(struct commit *c)
+{
+	timestamp_t masked_date;
+
+	if (sizeof(timestamp_t) > 4)
+		masked_date = c->date & (((timestamp_t) 1 << 34) - 1);
+	else
+		masked_date = c->date;
+
+	return commit_graph_data_at(c)->generation - masked_date;
+}
+
 static int write_graph_chunk_generation_data(struct hashfile *f,
 					     void *data)
 {
@@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f,
 		struct commit *c = ctx->commits.items[i];
 		timestamp_t offset;
 		repo_parse_commit(ctx->r, c);
-		offset = commit_graph_data_at(c)->generation - c->date;
+		offset = compute_generation_offset(c);
 		display_progress(ctx->progress, ++ctx->progress_cnt);
 
 		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
@@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
 	int i;
 	for (i = 0; i < ctx->commits.nr; i++) {
 		struct commit *c = ctx->commits.items[i];
-		timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+		timestamp_t offset = compute_generation_offset(c);
 		display_progress(ctx->progress, ++ctx->progress_cnt);
 
 		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
@@ -1741,7 +1772,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 
 	for (i = 0; i < ctx->commits.nr; i++) {
 		struct commit *c = ctx->commits.items[i];
-		timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+		timestamp_t offset = compute_generation_offset(c);
 		if (offset > GENERATION_NUMBER_V2_OFFSET_MAX)
 			ctx->num_generation_data_overflows++;
 	}
diff --git a/diff.c b/diff.c
index e87847f..397e38b 100644
--- a/diff.c
+++ b/diff.c
@@ -608,6 +608,52 @@ struct emit_callback {
 	struct strbuf *header;
 };
 
+/*
+ * State for the line-range callback wrappers that sit between
+ * xdi_diff_outf() and fn_out_consume().  xdiff produces a normal,
+ * unfiltered diff; the wrappers intercept each hunk header and line,
+ * track post-image position, and forward only lines that fall within
+ * the requested ranges.  Contiguous in-range lines are collected into
+ * range hunks and flushed with a synthetic @@ header so that
+ * fn_out_consume() sees well-formed unified-diff fragments.
+ *
+ * Removal lines ('-') cannot be classified by post-image position, so
+ * they are buffered in pending_rm until the next '+' or ' ' line
+ * reveals whether they precede an in-range line (flush into range hunk) or
+ * an out-of-range line (discard).
+ */
+struct line_range_callback {
+	xdiff_emit_line_fn orig_line_fn;
+	void *orig_cb_data;
+	const struct range_set *ranges;	/* 0-based [start, end) */
+	unsigned int cur_range;		/* index into the range_set */
+
+	/* Post/pre-image line counters (1-based, set from hunk headers) */
+	long lno_post;
+	long lno_pre;
+
+	/*
+	 * Function name from most recent xdiff hunk header;
+	 * size matches struct func_line.buf in xdiff/xemit.c.
+	 */
+	char func[80];
+	long funclen;
+
+	/* Range hunk being accumulated for the current range */
+	struct strbuf rhunk;
+	long rhunk_old_begin, rhunk_old_count;
+	long rhunk_new_begin, rhunk_new_count;
+	int rhunk_active;
+	int rhunk_has_changes;		/* any '+' or '-' lines? */
+
+	/* Removal lines not yet known to be in-range */
+	struct strbuf pending_rm;
+	int pending_rm_count;
+	long pending_rm_pre_begin;	/* pre-image line of first pending */
+
+	int ret;			/* latched error from orig_line_fn */
+};
+
 static int count_lines(const char *data, int size)
 {
 	int count, ch, completely_empty = 1, nl_just_seen = 0;
@@ -2493,6 +2539,188 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED
 	return 1;
 }
 
+static void discard_pending_rm(struct line_range_callback *s)
+{
+	strbuf_reset(&s->pending_rm);
+	s->pending_rm_count = 0;
+}
+
+static void flush_rhunk(struct line_range_callback *s)
+{
+	struct strbuf hdr = STRBUF_INIT;
+	const char *p, *end;
+
+	if (!s->rhunk_active || s->ret)
+		return;
+
+	/* Drain any pending removal lines into the range hunk */
+	if (s->pending_rm_count) {
+		strbuf_addbuf(&s->rhunk, &s->pending_rm);
+		s->rhunk_old_count += s->pending_rm_count;
+		s->rhunk_has_changes = 1;
+		discard_pending_rm(s);
+	}
+
+	/*
+	 * Suppress context-only hunks: they contain no actual changes
+	 * and would just be noise.  This can happen when the inflated
+	 * ctxlen causes xdiff to emit context covering a range that
+	 * has no changes in this commit.
+	 */
+	if (!s->rhunk_has_changes) {
+		s->rhunk_active = 0;
+		strbuf_reset(&s->rhunk);
+		return;
+	}
+
+	strbuf_addf(&hdr, "@@ -%ld,%ld +%ld,%ld @@",
+		    s->rhunk_old_begin, s->rhunk_old_count,
+		    s->rhunk_new_begin, s->rhunk_new_count);
+	if (s->funclen > 0) {
+		strbuf_addch(&hdr, ' ');
+		strbuf_add(&hdr, s->func, s->funclen);
+	}
+	strbuf_addch(&hdr, '\n');
+
+	s->ret = s->orig_line_fn(s->orig_cb_data, hdr.buf, hdr.len);
+	strbuf_release(&hdr);
+
+	/*
+	 * Replay buffered lines one at a time through fn_out_consume.
+	 * The cast discards const because xdiff_emit_line_fn takes
+	 * char *, though fn_out_consume does not modify the buffer.
+	 */
+	p = s->rhunk.buf;
+	end = p + s->rhunk.len;
+	while (!s->ret && p < end) {
+		const char *eol = memchr(p, '\n', end - p);
+		unsigned long line_len = eol ? (unsigned long)(eol - p + 1)
+					     : (unsigned long)(end - p);
+		s->ret = s->orig_line_fn(s->orig_cb_data, (char *)p, line_len);
+		p += line_len;
+	}
+
+	s->rhunk_active = 0;
+	strbuf_reset(&s->rhunk);
+}
+
+static void line_range_hunk_fn(void *data,
+			       long old_begin, long old_nr UNUSED,
+			       long new_begin, long new_nr UNUSED,
+			       const char *func, long funclen)
+{
+	struct line_range_callback *s = data;
+
+	/*
+	 * When count > 0, begin is 1-based.  When count == 0, begin is
+	 * adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of
+	 * that type will arrive, so the value is unused.
+	 *
+	 * Any pending removal lines from the previous xdiff hunk are
+	 * intentionally left in pending_rm: the line callback will
+	 * flush or discard them when the next content line reveals
+	 * whether the removals precede in-range content.
+	 */
+	s->lno_post = new_begin;
+	s->lno_pre = old_begin;
+
+	if (funclen > 0) {
+		if (funclen > (long)sizeof(s->func))
+			funclen = sizeof(s->func);
+		memcpy(s->func, func, funclen);
+	}
+	s->funclen = funclen;
+}
+
+static int line_range_line_fn(void *priv, char *line, unsigned long len)
+{
+	struct line_range_callback *s = priv;
+	const struct range *cur;
+	long lno_0, cur_pre;
+
+	if (s->ret)
+		return s->ret;
+
+	if (line[0] == '-') {
+		if (!s->pending_rm_count)
+			s->pending_rm_pre_begin = s->lno_pre;
+		s->lno_pre++;
+		strbuf_add(&s->pending_rm, line, len);
+		s->pending_rm_count++;
+		return s->ret;
+	}
+
+	if (line[0] == '\\') {
+		if (s->pending_rm_count)
+			strbuf_add(&s->pending_rm, line, len);
+		else if (s->rhunk_active)
+			strbuf_add(&s->rhunk, line, len);
+		/* otherwise outside tracked range; drop silently */
+		return s->ret;
+	}
+
+	if (line[0] != '+' && line[0] != ' ')
+		BUG("unexpected diff line type '%c'", line[0]);
+
+	lno_0 = s->lno_post - 1;
+	cur_pre = s->lno_pre;	/* save before advancing for context lines */
+	s->lno_post++;
+	if (line[0] == ' ')
+		s->lno_pre++;
+
+	/* Advance past ranges we've passed */
+	while (s->cur_range < s->ranges->nr &&
+	       lno_0 >= s->ranges->ranges[s->cur_range].end) {
+		if (s->rhunk_active)
+			flush_rhunk(s);
+		discard_pending_rm(s);
+		s->cur_range++;
+	}
+
+	/* Past all ranges */
+	if (s->cur_range >= s->ranges->nr) {
+		discard_pending_rm(s);
+		return s->ret;
+	}
+
+	cur = &s->ranges->ranges[s->cur_range];
+
+	/* Before current range */
+	if (lno_0 < cur->start) {
+		discard_pending_rm(s);
+		return s->ret;
+	}
+
+	/* In range so start a new range hunk if needed */
+	if (!s->rhunk_active) {
+		s->rhunk_active = 1;
+		s->rhunk_has_changes = 0;
+		s->rhunk_new_begin = lno_0 + 1;
+		s->rhunk_old_begin = s->pending_rm_count
+			? s->pending_rm_pre_begin : cur_pre;
+		s->rhunk_old_count = 0;
+		s->rhunk_new_count = 0;
+		strbuf_reset(&s->rhunk);
+	}
+
+	/* Flush pending removals into range hunk */
+	if (s->pending_rm_count) {
+		strbuf_addbuf(&s->rhunk, &s->pending_rm);
+		s->rhunk_old_count += s->pending_rm_count;
+		s->rhunk_has_changes = 1;
+		discard_pending_rm(s);
+	}
+
+	strbuf_add(&s->rhunk, line, len);
+	s->rhunk_new_count++;
+	if (line[0] == '+')
+		s->rhunk_has_changes = 1;
+	else
+		s->rhunk_old_count++;
+
+	return s->ret;
+}
+
 static void pprint_rename(struct strbuf *name, const char *a, const char *b)
 {
 	const char *old_name = a;
@@ -3592,7 +3820,8 @@ static void builtin_diff(const char *name_a,
 			 const char *xfrm_msg,
 			 int must_show_header,
 			 struct diff_options *o,
-			 int complete_rewrite)
+			 int complete_rewrite,
+			 const struct range_set *line_ranges)
 {
 	mmfile_t mf1, mf2;
 	const char *lbl[2];
@@ -3833,6 +4062,52 @@ static void builtin_diff(const char *name_a,
 			 */
 			xdi_diff_outf(&mf1, &mf2, NULL, quick_consume,
 				      &ecbdata, &xpp, &xecfg);
+		} else if (line_ranges) {
+			struct line_range_callback lr_state;
+			unsigned int i;
+			long max_span = 0;
+
+			memset(&lr_state, 0, sizeof(lr_state));
+			lr_state.orig_line_fn = fn_out_consume;
+			lr_state.orig_cb_data = &ecbdata;
+			lr_state.ranges = line_ranges;
+			strbuf_init(&lr_state.rhunk, 0);
+			strbuf_init(&lr_state.pending_rm, 0);
+
+			/*
+			 * Inflate ctxlen so that all changes within
+			 * any single range are merged into one xdiff
+			 * hunk and the inter-change context is emitted.
+			 * The callback clips back to range boundaries.
+			 *
+			 * The optimal ctxlen depends on where changes
+			 * fall within the range, which is only known
+			 * after xdiff runs; the max range span is the
+			 * upper bound that guarantees correctness in a
+			 * single pass.
+			 */
+			for (i = 0; i < line_ranges->nr; i++) {
+				long span = line_ranges->ranges[i].end -
+					    line_ranges->ranges[i].start;
+				if (span > max_span)
+					max_span = span;
+			}
+			if (max_span > xecfg.ctxlen)
+				xecfg.ctxlen = max_span;
+
+			if (xdi_diff_outf(&mf1, &mf2,
+					  line_range_hunk_fn,
+					  line_range_line_fn,
+					  &lr_state, &xpp, &xecfg))
+				die("unable to generate diff for %s",
+				    one->path);
+
+			flush_rhunk(&lr_state);
+			if (lr_state.ret)
+				die("unable to generate diff for %s",
+				    one->path);
+			strbuf_release(&lr_state.rhunk);
+			strbuf_release(&lr_state.pending_rm);
 		} else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
 					 &ecbdata, &xpp, &xecfg))
 			die("unable to generate diff for %s", one->path);
@@ -4674,7 +4949,7 @@ static void run_diff_cmd(const struct external_diff *pgm,
 
 		builtin_diff(name, other ? other : name,
 			     one, two, xfrm_msg, must_show_header,
-			     o, complete_rewrite);
+			     o, complete_rewrite, p->line_ranges);
 		if (p->status == DIFF_STATUS_COPIED ||
 		    p->status == DIFF_STATUS_RENAMED)
 			o->found_changes = 1;
diff --git a/diffcore.h b/diffcore.h
index 9c0a0e7..d75038d 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -19,6 +19,17 @@ struct userdiff_driver;
  * in anything else.
  */
 
+/* A range [start, end).  Lines are numbered starting at 0. */
+struct range {
+	long start, end;
+};
+
+/* A set of ranges.  The ranges must always be disjoint and sorted. */
+struct range_set {
+	unsigned int alloc, nr;
+	struct range *ranges;
+};
+
 /* We internally use unsigned short as the score value,
  * and rely on an int capable to hold 32-bits.  -B can take
  * -Bmerge_score/break_score format and the two scores are
@@ -106,6 +117,11 @@ int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
 struct diff_filepair {
 	struct diff_filespec *one;
 	struct diff_filespec *two;
+	/*
+	 * Tracked line ranges for -L filtering; borrowed from
+	 * line_log_data and must not be freed.
+	 */
+	const struct range_set *line_ranges;
 	unsigned short int score;
 	char status; /* M C R A D U etc. (see Documentation/diff-format.adoc or DIFF_STATUS_* in diff.h) */
 	unsigned broken_pair : 1;
diff --git a/fetch-pack.c b/fetch-pack.c
index a32224e..c8fa0a6 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -51,7 +51,6 @@ static int server_supports_filtering;
 static int advertise_sid;
 static struct shallow_lock shallow_lock;
 static const char *alternate_shallow_file;
-static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
 static struct strbuf fsck_msg_types = STRBUF_INIT;
 static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
 
@@ -145,7 +144,7 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
 	if (commit) {
 		if (mark_tags_complete_and_check_obj_db) {
 			if (!odb_has_object(the_repository->objects, oid,
-					    HAS_OBJECT_RECHECK_PACKED))
+					    ODB_HAS_OBJECT_RECHECK_PACKED))
 				die_in_commit_graph_only(oid);
 		}
 		return commit;
@@ -1096,6 +1095,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 				 struct shallow_info *si,
 				 struct string_list *pack_lockfiles)
 {
+	struct fsck_options fsck_options = { 0 };
 	struct repository *r = the_repository;
 	struct ref *ref = copy_ref_list(orig_ref);
 	struct object_id oid;
@@ -1224,6 +1224,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		alternate_shallow_file = setup_temporary_shallow(si->shallow);
 	} else
 		alternate_shallow_file = NULL;
+
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
 	if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought,
 		     &fsck_options.gitmodules_found))
 		die(_("git fetch-pack: fetch failed."));
@@ -1231,6 +1233,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 		die("fsck failed");
 
  all_done:
+	fsck_options_clear(&fsck_options);
 	if (negotiator)
 		negotiator->release(negotiator);
 	return ref;
@@ -1650,6 +1653,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 				    struct string_list *pack_lockfiles)
 {
 	struct repository *r = the_repository;
+	struct fsck_options fsck_options;
 	struct ref *ref = copy_ref_list(orig_ref);
 	enum fetch_state state = FETCH_CHECK_LOCAL;
 	struct oidset common = OIDSET_INIT;
@@ -1667,6 +1671,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	struct strvec index_pack_args = STRVEC_INIT;
 	const char *promisor_remote_config;
 
+	fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES);
+
 	if (server_feature_v2("promisor-remote", &promisor_remote_config))
 		promisor_remote_reply(promisor_remote_config, NULL);
 
@@ -1878,6 +1884,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
 	if (negotiator)
 		negotiator->release(negotiator);
 
+	fsck_options_clear(&fsck_options);
 	oidset_clear(&common);
 	return ref;
 }
@@ -2009,7 +2016,7 @@ static void update_shallow(struct fetch_pack_args *args,
 		struct object_id *oid = si->shallow->oid;
 		for (i = 0; i < si->shallow->nr; i++)
 			if (odb_has_object(the_repository->objects, &oid[i],
-					   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+					   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 				oid_array_append(&extra, &oid[i]);
 		if (extra.nr) {
 			setup_alternate_shallow(&shallow_lock,
diff --git a/fsck.c b/fsck.c
index 0f02cf8..b72200c 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1,5 +1,3 @@
-#define USE_THE_REPOSITORY_VARIABLE
-
 #include "git-compat-util.h"
 #include "date.h"
 #include "dir.h"
@@ -207,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values)
 			if (equal == len)
 				die("skiplist requires a path");
 			oidset_parse_file(&options->skip_oids, buf + equal + 1,
-					  the_repository->hash_algo);
+					  options->repo->hash_algo);
 			buf += len + 1;
 			continue;
 		}
@@ -360,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 	int res = 0;
 	const char *name;
 
-	if (repo_parse_tree(the_repository, tree))
+	if (repo_parse_tree(options->repo, tree))
 		return -1;
 
 	name = fsck_get_object_name(options, &tree->object.oid);
@@ -375,14 +373,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
 			continue;
 
 		if (S_ISDIR(entry.mode)) {
-			obj = (struct object *)lookup_tree(the_repository, &entry.oid);
+			obj = (struct object *)lookup_tree(options->repo, &entry.oid);
 			if (name && obj)
 				fsck_put_object_name(options, &entry.oid, "%s%s/",
 						     name, entry.path);
 			result = options->walk(obj, OBJ_TREE, data, options);
 		}
 		else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
-			obj = (struct object *)lookup_blob(the_repository, &entry.oid);
+			obj = (struct object *)lookup_blob(options->repo, &entry.oid);
 			if (name && obj)
 				fsck_put_object_name(options, &entry.oid, "%s%s",
 						     name, entry.path);
@@ -409,7 +407,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 	int result;
 	const char *name;
 
-	if (repo_parse_commit(the_repository, commit))
+	if (repo_parse_commit(options->repo, commit))
 		return -1;
 
 	name = fsck_get_object_name(options, &commit->object.oid);
@@ -417,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
 		fsck_put_object_name(options, get_commit_tree_oid(commit),
 				     "%s:", name);
 
-	result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit),
+	result = options->walk((struct object *) repo_get_commit_tree(options->repo, commit),
 			       OBJ_TREE, data, options);
 	if (result < 0)
 		return result;
@@ -474,7 +472,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio
 {
 	const char *name = fsck_get_object_name(options, &tag->object.oid);
 
-	if (parse_tag(the_repository, tag))
+	if (parse_tag(options->repo, tag))
 		return -1;
 	if (name)
 		fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
@@ -487,7 +485,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
 		return -1;
 
 	if (obj->type == OBJ_NONE)
-		parse_object(the_repository, &obj->oid);
+		parse_object(options->repo, &obj->oid);
 
 	switch (obj->type) {
 	case OBJ_BLOB:
@@ -970,14 +968,14 @@ static int fsck_commit(const struct object_id *oid,
 
 	if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer))
 		return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
-	if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
+	if (parse_oid_hex_algop(buffer, &tree_oid, &p, options->repo->hash_algo) || *p != '\n') {
 		err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
 		if (err)
 			return err;
 	}
 	buffer = p + 1;
 	while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) {
-		if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
+		if (parse_oid_hex_algop(buffer, &parent_oid, &p, options->repo->hash_algo) || *p != '\n') {
 			err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
 			if (err)
 				return err;
@@ -1044,7 +1042,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
 		goto done;
 	}
-	if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') {
+	if (parse_oid_hex_algop(buffer, tagged_oid, &p, options->repo->hash_algo) || *p != '\n') {
 		ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
 		if (ret)
 			goto done;
@@ -1336,9 +1334,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done,
 		if (oidset_contains(blobs_done, oid))
 			continue;
 
-		buf = odb_read_object(the_repository->objects, oid, &type, &size);
+		buf = odb_read_object(options->repo->objects, oid, &type, &size);
 		if (!buf) {
-			if (is_promisor_object(the_repository, oid))
+			if (is_promisor_object(options->repo, oid))
 				continue;
 			ret |= report(options,
 				      oid, OBJ_BLOB, msg_missing,
@@ -1380,6 +1378,54 @@ bool fsck_has_queued_checks(struct fsck_options *options)
 	       !oidset_equal(&options->gitattributes_found, &options->gitattributes_done);
 }
 
+void fsck_options_init(struct fsck_options *options,
+		       struct repository *repo,
+		       enum fsck_options_type type)
+{
+	static const struct fsck_options defaults[] = {
+		[FSCK_OPTIONS_DEFAULT] = {
+			.skip_oids = OIDSET_INIT,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_function
+		},
+		[FSCK_OPTIONS_STRICT] = {
+			.strict = 1,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_function,
+		},
+		[FSCK_OPTIONS_MISSING_GITMODULES] = {
+			.strict = 1,
+			.gitmodules_found = OIDSET_INIT,
+			.gitmodules_done = OIDSET_INIT,
+			.gitattributes_found = OIDSET_INIT,
+			.gitattributes_done = OIDSET_INIT,
+			.error_func = fsck_objects_error_cb_print_missing_gitmodules,
+		},
+		[FSCK_OPTIONS_REFS] = {
+			.error_func = fsck_refs_error_function,
+		},
+	};
+
+	switch (type) {
+	case FSCK_OPTIONS_DEFAULT:
+	case FSCK_OPTIONS_STRICT:
+	case FSCK_OPTIONS_MISSING_GITMODULES:
+	case FSCK_OPTIONS_REFS:
+		memcpy(options, &defaults[type], sizeof(*options));
+		break;
+	default:
+		BUG("unknown fsck options type %d", type);
+	}
+
+	options->repo = repo;
+}
+
 void fsck_options_clear(struct fsck_options *options)
 {
 	free(options->msg_type);
diff --git a/fsck.h b/fsck.h
index 65ecbb7..e77935c 100644
--- a/fsck.h
+++ b/fsck.h
@@ -166,7 +166,10 @@ struct fsck_ref_report {
 	const char *path;
 };
 
+struct repository;
+
 struct fsck_options {
+	struct repository *repo;
 	fsck_walk_func walk;
 	fsck_error error_func;
 	unsigned strict;
@@ -180,34 +183,6 @@ struct fsck_options {
 	kh_oid_map_t *object_names;
 };
 
-#define FSCK_OPTIONS_DEFAULT { \
-	.skip_oids = OIDSET_INIT, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_function \
-}
-#define FSCK_OPTIONS_STRICT { \
-	.strict = 1, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_function, \
-}
-#define FSCK_OPTIONS_MISSING_GITMODULES { \
-	.strict = 1, \
-	.gitmodules_found = OIDSET_INIT, \
-	.gitmodules_done = OIDSET_INIT, \
-	.gitattributes_found = OIDSET_INIT, \
-	.gitattributes_done = OIDSET_INIT, \
-	.error_func = fsck_objects_error_cb_print_missing_gitmodules, \
-}
-#define FSCK_REFS_OPTIONS_DEFAULT { \
-	.error_func = fsck_refs_error_function, \
-}
-
 /* descend in all linked child objects
  * the return value is:
  *    -1	error in processing the object
@@ -255,6 +230,17 @@ int fsck_finish(struct fsck_options *options);
  */
 bool fsck_has_queued_checks(struct fsck_options *options);
 
+enum fsck_options_type {
+	FSCK_OPTIONS_DEFAULT,
+	FSCK_OPTIONS_STRICT,
+	FSCK_OPTIONS_MISSING_GITMODULES,
+	FSCK_OPTIONS_REFS,
+};
+
+void fsck_options_init(struct fsck_options *options,
+		       struct repository *repo,
+		       enum fsck_options_type type);
+
 /*
  * Clear the fsck_options struct, freeing any allocated memory.
  */
diff --git a/gpg-interface.c b/gpg-interface.c
index d517425..dafd537 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1164,6 +1164,8 @@ int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid)
 		*mode = SIGN_WARN_STRIP;
 	} else if (!strcmp(arg, "strip")) {
 		*mode = SIGN_STRIP;
+	} else if (!strcmp(arg, "abort-if-invalid")) {
+		*mode = SIGN_ABORT_IF_INVALID;
 	} else if (!strcmp(arg, "strip-if-invalid")) {
 		*mode = SIGN_STRIP_IF_INVALID;
 	} else if (!strcmp(arg, "sign-if-invalid")) {
diff --git a/gpg-interface.h b/gpg-interface.h
index a365586..3d95f5e 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -115,6 +115,7 @@ void print_signature_buffer(const struct signature_check *sigc,
 /* Modes for --signed-tags=<mode> and --signed-commits=<mode> options. */
 enum sign_mode {
 	SIGN_ABORT,
+	SIGN_ABORT_IF_INVALID,
 	SIGN_WARN_VERBATIM,
 	SIGN_VERBATIM,
 	SIGN_WARN_STRIP,
diff --git a/http-push.c b/http-push.c
index 9ae6062..06c3acb 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1449,7 +1449,7 @@ static void one_remote_ref(const char *refname)
 	 */
 	if (repo->can_update_info_refs &&
 	    !odb_has_object(the_repository->objects, &ref->old_oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		obj = lookup_unknown_object(the_repository, &ref->old_oid);
 		fprintf(stderr,	"  fetch %s for %s\n",
 			oid_to_hex(&ref->old_oid), refname);
@@ -1655,7 +1655,7 @@ static int delete_remote_branch(const char *pattern, int force)
 		if (is_null_oid(&head_oid))
 			return error("Unable to resolve remote HEAD");
 		if (!odb_has_object(the_repository->objects, &head_oid,
-				    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid));
 
 		/* Remote branch must resolve to a known object */
@@ -1663,7 +1663,7 @@ static int delete_remote_branch(const char *pattern, int force)
 			return error("Unable to resolve remote branch %s",
 				     remote_ref->name);
 		if (!odb_has_object(the_repository->objects, &remote_ref->old_oid,
-				    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid));
 
 		/* Remote branch must be an ancestor of remote HEAD */
@@ -1886,7 +1886,7 @@ int cmd_main(int argc, const char **argv)
 		    !is_null_oid(&ref->old_oid) &&
 		    !ref->force) {
 			if (!odb_has_object(the_repository->objects, &ref->old_oid,
-					    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
+					    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) ||
 			    !ref_newer(&ref->peer_ref->new_oid,
 				       &ref->old_oid)) {
 				/*
diff --git a/http-walker.c b/http-walker.c
index e886e64..1b6d496 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -139,7 +139,7 @@ static int fill_active_slot(void *data UNUSED)
 		obj_req = list_entry(pos, struct object_request, node);
 		if (obj_req->state == WAITING) {
 			if (odb_has_object(the_repository->objects, &obj_req->oid,
-					   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+					   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 				obj_req->state = COMPLETE;
 			else {
 				start_object_request(obj_req);
@@ -495,7 +495,7 @@ static int fetch_object(struct walker *walker, const struct object_id *oid)
 		return error("Couldn't find request for %s in the queue", hex);
 
 	if (odb_has_object(the_repository->objects, &obj_req->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		if (obj_req->req)
 			abort_http_object_request(&obj_req->req);
 		abort_object_request(obj_req);
diff --git a/line-log.c b/line-log.c
index eeaf684..858a899 100644
--- a/line-log.c
+++ b/line-log.c
@@ -858,173 +858,33 @@ static void queue_diffs(struct line_log_data *range,
 	diff_queue_clear(&diff_queued_diff);
 	diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
 	if (opt->detect_rename && diff_might_be_rename()) {
-		/* must look at the full tree diff to detect renames */
-		clear_pathspec(&opt->pathspec);
-		diff_queue_clear(&diff_queued_diff);
+		struct diff_options rename_opts;
 
-		diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
+		/*
+		 * Build a private diff_options for rename detection so
+		 * that any user-specified options on the original opts
+		 * (e.g. pickaxe) cannot discard diff pairs needed for
+		 * rename tracking.  Similar to blame's find_rename().
+		 */
+		repo_diff_setup(opt->repo, &rename_opts);
+		rename_opts.flags.recursive = 1;
+		rename_opts.detect_rename = opt->detect_rename;
+		rename_opts.rename_score = opt->rename_score;
+		rename_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+		diff_setup_done(&rename_opts);
+
+		/* must look at the full tree diff to detect renames */
+		diff_queue_clear(&diff_queued_diff);
+		diff_tree_oid(parent_tree_oid, tree_oid, "", &rename_opts);
 
 		filter_diffs_for_paths(range, 1);
-		diffcore_std(opt);
+		diffcore_std(&rename_opts);
 		filter_diffs_for_paths(range, 0);
+		diff_free(&rename_opts);
 	}
 	move_diff_queue(queue, &diff_queued_diff);
 }
 
-static char *get_nth_line(long line, unsigned long *ends, void *data)
-{
-	if (line == 0)
-		return (char *)data;
-	else
-		return (char *)data + ends[line] + 1;
-}
-
-static void print_line(const char *prefix, char first,
-		       long line, unsigned long *ends, void *data,
-		       const char *color, const char *reset, FILE *file)
-{
-	char *begin = get_nth_line(line, ends, data);
-	char *end = get_nth_line(line+1, ends, data);
-	int had_nl = 0;
-
-	if (end > begin && end[-1] == '\n') {
-		end--;
-		had_nl = 1;
-	}
-
-	fputs(prefix, file);
-	fputs(color, file);
-	putc(first, file);
-	fwrite(begin, 1, end-begin, file);
-	fputs(reset, file);
-	putc('\n', file);
-	if (!had_nl)
-		fputs("\\ No newline at end of file\n", file);
-}
-
-static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
-{
-	unsigned int i, j = 0;
-	long p_lines, t_lines;
-	unsigned long *p_ends = NULL, *t_ends = NULL;
-	struct diff_filepair *pair = range->pair;
-	struct diff_ranges *diff = &range->diff;
-
-	struct diff_options *opt = &rev->diffopt;
-	const char *prefix = diff_line_prefix(opt);
-	const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
-	const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
-	const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
-	const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
-	const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
-	const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT);
-
-	if (!pair || !diff)
-		goto out;
-
-	if (pair->one->oid_valid)
-		fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
-	fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends);
-
-	fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
-	fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
-	       pair->one->oid_valid ? "a/" : "",
-	       pair->one->oid_valid ? pair->one->path : "/dev/null",
-	       c_reset);
-	fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
-	for (i = 0; i < range->ranges.nr; i++) {
-		long p_start, p_end;
-		long t_start = range->ranges.ranges[i].start;
-		long t_end = range->ranges.ranges[i].end;
-		long t_cur = t_start;
-		unsigned int j_last;
-
-		/*
-		 * If a diff range touches multiple line ranges, then all
-		 * those line ranges should be shown, so take a step back if
-		 * the current line range is still in the previous diff range
-		 * (even if only partially).
-		 */
-		if (j > 0 && diff->target.ranges[j-1].end > t_start)
-			j--;
-
-		while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
-			j++;
-		if (j == diff->target.nr || diff->target.ranges[j].start >= t_end)
-			continue;
-
-		/* Scan ahead to determine the last diff that falls in this range */
-		j_last = j;
-		while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end)
-			j_last++;
-		if (j_last > j)
-			j_last--;
-
-		/*
-		 * Compute parent hunk headers: we know that the diff
-		 * has the correct line numbers (but not all hunks).
-		 * So it suffices to shift the start/end according to
-		 * the line numbers of the first/last hunk(s) that
-		 * fall in this range.
-		 */
-		if (t_start < diff->target.ranges[j].start)
-			p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start);
-		else
-			p_start = diff->parent.ranges[j].start;
-		if (t_end > diff->target.ranges[j_last].end)
-			p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end);
-		else
-			p_end = diff->parent.ranges[j_last].end;
-
-		if (!p_start && !p_end) {
-			p_start = -1;
-			p_end = -1;
-		}
-
-		/* Now output a diff hunk for this range */
-		fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
-		       prefix, c_frag,
-		       p_start+1, p_end-p_start, t_start+1, t_end-t_start,
-		       c_reset);
-		while (j < diff->target.nr && diff->target.ranges[j].start < t_end) {
-			int k;
-			for (; t_cur < diff->target.ranges[j].start; t_cur++)
-				print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
-					   c_context, c_reset, opt->file);
-			for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
-				print_line(prefix, '-', k, p_ends, pair->one->data,
-					   c_old, c_reset, opt->file);
-			for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
-				print_line(prefix, '+', t_cur, t_ends, pair->two->data,
-					   c_new, c_reset, opt->file);
-			j++;
-		}
-		for (; t_cur < t_end; t_cur++)
-			print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
-				   c_context, c_reset, opt->file);
-	}
-
-out:
-	free(p_ends);
-	free(t_ends);
-}
-
-/*
- * NEEDSWORK: manually building a diff here is not the Right
- * Thing(tm).  log -L should be built into the diff pipeline.
- */
-static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
-{
-	const char *prefix = diff_line_prefix(&rev->diffopt);
-
-	fprintf(rev->diffopt.file, "%s\n", prefix);
-
-	while (range) {
-		dump_diff_hacky_one(rev, range);
-		range = range->next;
-	}
-}
-
 /*
  * Unlike most other functions, this destructively operates on
  * 'range'.
@@ -1088,7 +948,7 @@ static int process_diff_filepair(struct rev_info *rev,
 
 static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
 {
-	struct diff_filepair *new_filepair = xmalloc(sizeof(struct diff_filepair));
+	struct diff_filepair *new_filepair = xcalloc(1, sizeof(struct diff_filepair));
 	new_filepair->one = pair->one;
 	new_filepair->two = pair->two;
 	new_filepair->one->count++;
@@ -1146,11 +1006,25 @@ static int process_all_files(struct line_log_data **range_out,
 
 int line_log_print(struct rev_info *rev, struct commit *commit)
 {
-
 	show_log(rev);
 	if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) {
 		struct line_log_data *range = lookup_line_range(rev, commit);
-		dump_diff_hacky(rev, range);
+		struct line_log_data *r;
+		const char *prefix = diff_line_prefix(&rev->diffopt);
+
+		fprintf(rev->diffopt.file, "%s\n", prefix);
+
+		for (r = range; r; r = r->next) {
+			if (r->pair) {
+				struct diff_filepair *p =
+					diff_filepair_dup(r->pair);
+				p->line_ranges = &r->ranges;
+				diff_q(&diff_queued_diff, p);
+			}
+		}
+
+		diffcore_std(&rev->diffopt);
+		diff_flush(&rev->diffopt);
 	}
 	return 1;
 }
diff --git a/line-log.h b/line-log.h
index e9dadbc..04a6ea6 100644
--- a/line-log.h
+++ b/line-log.h
@@ -1,22 +1,12 @@
 #ifndef LINE_LOG_H
 #define LINE_LOG_H
 
+#include "diffcore.h" /* struct range, struct range_set */
+
 struct rev_info;
 struct commit;
 struct string_list;
 
-/* A range [start,end].  Lines are numbered starting at 0, and the
- * ranges include start but exclude end. */
-struct range {
-	long start, end;
-};
-
-/* A set of ranges.  The ranges must always be disjoint and sorted. */
-struct range_set {
-	unsigned int alloc, nr;
-	struct range *ranges;
-};
-
 /* A diff, encoded as the set of pre- and post-image ranges where the
  * files differ. A pair of ranges corresponds to a hunk. */
 struct diff_ranges {
diff --git a/list-objects.c b/list-objects.c
index 91b23e2..724d723 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -75,7 +75,7 @@ static void process_blob(struct traversal_context *ctx,
 	 */
 	if (ctx->revs->exclude_promisor_objects &&
 	    !odb_has_object(the_repository->objects, &obj->oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) &&
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) &&
 	    is_promisor_object(ctx->revs->repo, &obj->oid))
 		return;
 
diff --git a/notes.c b/notes.c
index 51a7ef9..8f315e2 100644
--- a/notes.c
+++ b/notes.c
@@ -796,7 +796,7 @@ static int prune_notes_helper(const struct object_id *object_oid,
 	struct note_delete_list *n;
 
 	if (odb_has_object(the_repository->objects, object_oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return 0; /* nothing to do for this note */
 
 	/* failed to find object => prune this note */
diff --git a/object-file.c b/object-file.c
index 63408fc..2acc952 100644
--- a/object-file.c
+++ b/object-file.c
@@ -909,7 +909,7 @@ static int start_loose_object_common(struct odb_source *source,
 
 	fd = create_tmpfile(source->odb->repo, tmp_file, filename);
 	if (fd < 0) {
-		if (flags & WRITE_OBJECT_SILENT)
+		if (flags & ODB_WRITE_OBJECT_SILENT)
 			return -1;
 		else if (errno == EACCES)
 			return error(_("insufficient permission for adding "
@@ -1042,7 +1042,7 @@ static int write_loose_object(struct odb_source *source,
 		utb.actime = mtime;
 		utb.modtime = mtime;
 		if (utime(tmp_file.buf, &utb) < 0 &&
-		    !(flags & WRITE_OBJECT_SILENT))
+		    !(flags & ODB_WRITE_OBJECT_SILENT))
 			warning_errno(_("failed utime() on %s"), tmp_file.buf);
 	}
 
@@ -1169,7 +1169,8 @@ int odb_source_loose_write_stream(struct odb_source *source,
 int odb_source_loose_write_object(struct odb_source *source,
 				  const void *buf, unsigned long len,
 				  enum object_type type, struct object_id *oid,
-				  struct object_id *compat_oid_in, unsigned flags)
+				  struct object_id *compat_oid_in,
+				  enum odb_write_object_flags flags)
 {
 	const struct git_hash_algo *algo = source->odb->repo->hash_algo;
 	const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
@@ -1282,8 +1283,9 @@ static int index_mem(struct index_state *istate,
 		}
 	}
 	if (flags & INDEX_FORMAT_CHECK) {
-		struct fsck_options opts = FSCK_OPTIONS_DEFAULT;
+		struct fsck_options opts;
 
+		fsck_options_init(&opts, the_repository, FSCK_OPTIONS_DEFAULT);
 		opts.strict = 1;
 		opts.error_func = hash_format_check_report;
 		if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts))
@@ -1377,7 +1379,7 @@ static int already_written(struct odb_transaction_files *transaction,
 {
 	/* The object may already exist in the repository */
 	if (odb_has_object(transaction->base.source->odb, oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 		return 1;
 
 	/* Might want to keep the list sorted */
diff --git a/object-file.h b/object-file.h
index 3686f18..5241b8d 100644
--- a/object-file.h
+++ b/object-file.h
@@ -68,7 +68,8 @@ int odb_source_loose_freshen_object(struct odb_source *source,
 int odb_source_loose_write_object(struct odb_source *source,
 				  const void *buf, unsigned long len,
 				  enum object_type type, struct object_id *oid,
-				  struct object_id *compat_oid_in, unsigned flags);
+				  struct object_id *compat_oid_in,
+				  enum odb_write_object_flags flags);
 
 int odb_source_loose_write_stream(struct odb_source *source,
 				  struct odb_write_stream *stream, size_t len,
diff --git a/odb.c b/odb.c
index 3f94a53..9b28fe2 100644
--- a/odb.c
+++ b/odb.c
@@ -872,15 +872,15 @@ void *odb_read_object_peeled(struct object_database *odb,
 }
 
 int odb_has_object(struct object_database *odb, const struct object_id *oid,
-		   enum has_object_flags flags)
+		   enum odb_has_object_flags flags)
 {
 	unsigned object_info_flags = 0;
 
 	if (!startup_info->have_repository)
 		return 0;
-	if (!(flags & HAS_OBJECT_RECHECK_PACKED))
+	if (!(flags & ODB_HAS_OBJECT_RECHECK_PACKED))
 		object_info_flags |= OBJECT_INFO_QUICK;
-	if (!(flags & HAS_OBJECT_FETCH_PROMISOR))
+	if (!(flags & ODB_HAS_OBJECT_FETCH_PROMISOR))
 		object_info_flags |= OBJECT_INFO_SKIP_FETCH_OBJECT;
 
 	return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0;
@@ -922,7 +922,7 @@ int odb_for_each_object(struct object_database *odb,
 			const struct object_info *request,
 			odb_for_each_object_cb cb,
 			void *cb_data,
-			unsigned flags)
+			enum odb_for_each_object_flags flags)
 {
 	struct odb_for_each_object_options opts = {
 		.flags = flags,
@@ -1053,7 +1053,7 @@ int odb_write_object_ext(struct object_database *odb,
 			 enum object_type type,
 			 struct object_id *oid,
 			 struct object_id *compat_oid,
-			 unsigned flags)
+			 enum odb_write_object_flags flags)
 {
 	return odb_source_write_object(odb->sources, buf, len, type,
 				       oid, compat_oid, flags);
diff --git a/odb.h b/odb.h
index 984bafc..3a711f6 100644
--- a/odb.h
+++ b/odb.h
@@ -1,19 +1,17 @@
 #ifndef ODB_H
 #define ODB_H
 
-#include "hashmap.h"
 #include "object.h"
 #include "oidset.h"
 #include "oidmap.h"
 #include "string-list.h"
 #include "thread-utils.h"
 
-struct oidmap;
-struct oidtree;
+struct cached_object_entry;
+struct packed_git;
+struct repository;
 struct strbuf;
 struct strvec;
-struct repository;
-struct multi_pack_index;
 
 /*
  * Set this to 0 to prevent odb_read_object_info_extended() from fetching missing
@@ -31,10 +29,6 @@ extern int fetch_if_missing;
  */
 char *compute_alternate_path(const char *path, struct strbuf *err);
 
-struct packed_git;
-struct packfile_store;
-struct cached_object_entry;
-
 /*
  * A transaction may be started for an object database prior to writing new
  * objects via odb_transaction_begin(). These objects are not committed until
@@ -395,11 +389,11 @@ int odb_read_object_info(struct object_database *odb,
 			 const struct object_id *oid,
 			 unsigned long *sizep);
 
-enum has_object_flags {
+enum odb_has_object_flags {
 	/* Retry packed storage after checking packed and loose storage */
-	HAS_OBJECT_RECHECK_PACKED = (1 << 0),
+	ODB_HAS_OBJECT_RECHECK_PACKED = (1 << 0),
 	/* Allow fetching the object in case the repository has a promisor remote. */
-	HAS_OBJECT_FETCH_PROMISOR = (1 << 1),
+	ODB_HAS_OBJECT_FETCH_PROMISOR = (1 << 1),
 };
 
 /*
@@ -408,7 +402,7 @@ enum has_object_flags {
  */
 int odb_has_object(struct object_database *odb,
 		   const struct object_id *oid,
-		   enum has_object_flags flags);
+		   enum odb_has_object_flags flags);
 
 int odb_freshen_object(struct object_database *odb,
 		       const struct object_id *oid);
@@ -522,7 +516,7 @@ int odb_for_each_object(struct object_database *odb,
 			const struct object_info *request,
 			odb_for_each_object_cb cb,
 			void *cb_data,
-			unsigned flags);
+			enum odb_for_each_object_flags flags);
 
 enum odb_count_objects_flags {
 	/*
@@ -561,19 +555,19 @@ int odb_find_abbrev_len(struct object_database *odb,
 			int min_len,
 			unsigned *out);
 
-enum {
+enum odb_write_object_flags {
 	/*
 	 * By default, `odb_write_object()` does not actually write anything
 	 * into the object store, but only computes the object ID. This flag
 	 * changes that so that the object will be written as a loose object
 	 * and persisted.
 	 */
-	WRITE_OBJECT_PERSIST = (1 << 0),
+	ODB_WRITE_OBJECT_PERSIST = (1 << 0),
 
 	/*
 	 * Do not print an error in case something goes wrong.
 	 */
-	WRITE_OBJECT_SILENT = (1 << 1),
+	ODB_WRITE_OBJECT_SILENT = (1 << 1),
 };
 
 /*
@@ -589,7 +583,7 @@ int odb_write_object_ext(struct object_database *odb,
 			 enum object_type type,
 			 struct object_id *oid,
 			 struct object_id *compat_oid,
-			 unsigned flags);
+			 enum odb_write_object_flags flags);
 
 static inline int odb_write_object(struct object_database *odb,
 				   const void *buf, unsigned long len,
diff --git a/odb/source-files.c b/odb/source-files.c
index 7679756..b5abd20 100644
--- a/odb/source-files.c
+++ b/odb/source-files.c
@@ -161,7 +161,7 @@ static int odb_source_files_write_object(struct odb_source *source,
 					 enum object_type type,
 					 struct object_id *oid,
 					 struct object_id *compat_oid,
-					 unsigned flags)
+					 enum odb_write_object_flags flags)
 {
 	return odb_source_loose_write_object(source, buf, len, type,
 					     oid, compat_oid, flags);
diff --git a/odb/source.h b/odb/source.h
index a9d7d0b..f706e06 100644
--- a/odb/source.h
+++ b/odb/source.h
@@ -197,7 +197,7 @@ struct odb_source {
 			    enum object_type type,
 			    struct object_id *oid,
 			    struct object_id *compat_oid,
-			    unsigned flags);
+			    enum odb_write_object_flags flags);
 
 	/*
 	 * This callback is expected to persist the given object stream into
@@ -405,7 +405,7 @@ static inline int odb_source_write_object(struct odb_source *source,
 					  enum object_type type,
 					  struct object_id *oid,
 					  struct object_id *compat_oid,
-					  unsigned flags)
+					  enum odb_write_object_flags flags)
 {
 	return source->write_object(source, buf, len, type, oid,
 				    compat_oid, flags);
diff --git a/pack-check.c b/pack-check.c
index 7378c80..79992bb 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r,
 			   struct packed_git *p,
 			   struct pack_window **w_curs,
 			   verify_fn fn,
+			   void *fn_data,
 			   struct progress *progress, uint32_t base_count)
 
 {
@@ -161,7 +162,7 @@ static int verify_packfile(struct repository *r,
 				    oid_to_hex(&oid), p->pack_name);
 		else if (fn) {
 			int eaten = 0;
-			err |= fn(&oid, type, size, data, &eaten);
+			err |= fn(&oid, type, size, data, &eaten, fn_data);
 			if (eaten)
 				data = NULL;
 		}
@@ -192,7 +193,7 @@ int verify_pack_index(struct packed_git *p)
 	return err;
 }
 
-int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
+int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data,
 		struct progress *progress, uint32_t base_count)
 {
 	int err = 0;
@@ -202,7 +203,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn,
 	if (!p->index_data)
 		return -1;
 
-	err |= verify_packfile(r, p, &w_curs, fn, progress, base_count);
+	err |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count);
 	unuse_pack(&w_curs);
 
 	return err;
diff --git a/pack.h b/pack.h
index ec76472..1cde920 100644
--- a/pack.h
+++ b/pack.h
@@ -85,7 +85,11 @@ struct pack_idx_entry {
 
 struct progress;
 /* Note, the data argument could be NULL if object type is blob */
-typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
+typedef int (*verify_fn)(const struct object_id *oid,
+			 enum object_type type,
+			 unsigned long size,
+			 void *buffer, int *eaten,
+			 void *fn_data);
 
 const char *write_idx_file(struct repository *repo,
 			   const char *index_name,
@@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo,
 			   const unsigned char *sha1);
 int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 int verify_pack_index(struct packed_git *);
-int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t);
+int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data,
+		struct progress *, uint32_t);
 off_t write_pack_header(struct hashfile *f, uint32_t);
 void fixup_pack_header_footer(const struct git_hash_algo *, int,
 			      unsigned char *, const char *, uint32_t,
diff --git a/packfile.c b/packfile.c
index 48c8874..b012d64 100644
--- a/packfile.c
+++ b/packfile.c
@@ -2300,7 +2300,7 @@ int has_object_kept_pack(struct repository *r, const struct object_id *oid,
 
 int for_each_object_in_pack(struct packed_git *p,
 			    each_packed_object_fn cb, void *data,
-			    unsigned flags)
+			    enum odb_for_each_object_flags flags)
 {
 	uint32_t i;
 	int r = 0;
diff --git a/packfile.h b/packfile.h
index 6e8802e..9b647da 100644
--- a/packfile.h
+++ b/packfile.h
@@ -354,7 +354,7 @@ typedef int each_packed_object_fn(const struct object_id *oid,
 				  void *data);
 int for_each_object_in_pack(struct packed_git *p,
 			    each_packed_object_fn, void *data,
-			    unsigned flags);
+			    enum odb_for_each_object_flags flags);
 
 /*
  * Iterate through all packed objects in the given packfile store and invoke
diff --git a/path-walk.c b/path-walk.c
index 2aa3e7d..6e426af 100644
--- a/path-walk.c
+++ b/path-walk.c
@@ -174,7 +174,7 @@ static int add_tree_entries(struct path_walk_context *ctx,
 
 		if (!o) {
 			error(_("failed to find object %s"),
-			      oid_to_hex(&o->oid));
+			      oid_to_hex(&entry.oid));
 			return -1;
 		}
 
diff --git a/read-cache.c b/read-cache.c
index 5049f9b..38a04b8 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -2309,13 +2309,9 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
 	}
 	munmap((void *)mmap, mmap_size);
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_data_intmax("index", the_repository, "read/version",
+	trace2_data_intmax("index", istate->repo, "read/version",
 			   istate->version);
-	trace2_data_intmax("index", the_repository, "read/cache_nr",
+	trace2_data_intmax("index", istate->repo, "read/cache_nr",
 			   istate->cache_nr);
 
 	/*
@@ -2360,16 +2356,12 @@ int read_index_from(struct index_state *istate, const char *path,
 	if (istate->initialized)
 		return istate->cache_nr;
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_region_enter_printf("index", "do_read_index", the_repository,
+	trace2_region_enter_printf("index", "do_read_index", istate->repo,
 				   "%s", path);
 	trace_performance_enter();
 	ret = do_read_index(istate, path, 0);
 	trace_performance_leave("read cache %s", path);
-	trace2_region_leave_printf("index", "do_read_index", the_repository,
+	trace2_region_leave_printf("index", "do_read_index", istate->repo,
 				   "%s", path);
 
 	split_index = istate->split_index;
@@ -3096,13 +3088,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
 	istate->timestamp.nsec = ST_MTIME_NSEC(st);
 	trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_data_intmax("index", the_repository, "write/version",
+	trace2_data_intmax("index", istate->repo, "write/version",
 			   istate->version);
-	trace2_data_intmax("index", the_repository, "write/cache_nr",
+	trace2_data_intmax("index", istate->repo, "write/cache_nr",
 			   istate->cache_nr);
 
 	ret = 0;
@@ -3144,14 +3132,10 @@ static int do_write_locked_index(struct index_state *istate,
 		return ret;
 	}
 
-	/*
-	 * TODO trace2: replace "the_repository" with the actual repo instance
-	 * that is associated with the given "istate".
-	 */
-	trace2_region_enter_printf("index", "do_write_index", the_repository,
+	trace2_region_enter_printf("index", "do_write_index", istate->repo,
 				   "%s", get_lock_file_path(lock));
 	ret = do_write_index(istate, lock->tempfile, write_extensions, flags);
-	trace2_region_leave_printf("index", "do_write_index", the_repository,
+	trace2_region_leave_printf("index", "do_write_index", istate->repo,
 				   "%s", get_lock_file_path(lock));
 
 	if (was_full)
@@ -4049,6 +4033,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix,
 	rev.diffopt.format_callback = update_callback;
 	rev.diffopt.format_callback_data = &data;
 	rev.diffopt.flags.override_submodule_config = 1;
+	rev.diffopt.detect_rename = 0; /* staging worktree changes does not need renames */
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 
 	/*
diff --git a/reflog.c b/reflog.c
index 1460ae9..8233707 100644
--- a/reflog.c
+++ b/reflog.c
@@ -168,7 +168,7 @@ static int tree_is_complete(const struct object_id *oid)
 	complete = 1;
 	while (tree_entry(&desc, &entry)) {
 		if (!odb_has_object(the_repository->objects, &entry.oid,
-				HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) ||
+				ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR) ||
 		    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
 			tree->object.flags |= INCOMPLETE;
 			complete = 0;
diff --git a/refs.c b/refs.c
index 5d1d285..bfcb9c7 100644
--- a/refs.c
+++ b/refs.c
@@ -425,7 +425,7 @@ int ref_resolves_to_object(const char *refname,
 	if (flags & REF_ISBROKEN)
 		return 0;
 	if (!odb_has_object(repo->objects, oid,
-			    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		error(_("%s does not point to a valid object!"), refname);
 		return 0;
 	}
diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index b124404..daea30a 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -366,11 +366,6 @@ static int reftable_be_config(const char *var, const char *value,
 	return 0;
 }
 
-static int reftable_be_fsync(int fd)
-{
-	return fsync_component(FSYNC_COMPONENT_REFERENCE, fd);
-}
-
 static struct ref_store *reftable_be_init(struct repository *repo,
 					  const char *payload,
 					  const char *gitdir,
@@ -408,7 +403,6 @@ static struct ref_store *reftable_be_init(struct repository *repo,
 	refs->write_options.disable_auto_compact =
 		!git_env_bool("GIT_TEST_REFTABLE_AUTOCOMPACTION", 1);
 	refs->write_options.lock_timeout_ms = 100;
-	refs->write_options.fsync = reftable_be_fsync;
 
 	repo_config(the_repository, reftable_be_config, &refs->write_options);
 
diff --git a/reftable/blocksource.c b/reftable/blocksource.c
index 573c812..7f7441f 100644
--- a/reftable/blocksource.c
+++ b/reftable/blocksource.c
@@ -93,13 +93,12 @@ void block_source_from_buf(struct reftable_block_source *bs,
 }
 
 struct file_block_source {
-	uint64_t size;
-	unsigned char *data;
+	struct reftable_mmap mmap;
 };
 
 static uint64_t file_size(void *b)
 {
-	return ((struct file_block_source *)b)->size;
+	return ((struct file_block_source *)b)->mmap.size;
 }
 
 static void file_release_data(void *b REFTABLE_UNUSED, struct reftable_block_data *dest REFTABLE_UNUSED)
@@ -109,7 +108,7 @@ static void file_release_data(void *b REFTABLE_UNUSED, struct reftable_block_dat
 static void file_close(void *v)
 {
 	struct file_block_source *b = v;
-	munmap(b->data, b->size);
+	reftable_munmap(&b->mmap);
 	reftable_free(b);
 }
 
@@ -117,8 +116,8 @@ static ssize_t file_read_data(void *v, struct reftable_block_data *dest, uint64_
 			      uint32_t size)
 {
 	struct file_block_source *b = v;
-	assert(off + size <= b->size);
-	dest->data = b->data + off;
+	assert(off + size <= b->mmap.size);
+	dest->data = (unsigned char *) b->mmap.data + off;
 	dest->len = size;
 	return size;
 }
@@ -156,13 +155,9 @@ int reftable_block_source_from_file(struct reftable_block_source *bs,
 		goto out;
 	}
 
-	p->size = st.st_size;
-	p->data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-	if (p->data == MAP_FAILED) {
-		err = REFTABLE_IO_ERROR;
-		p->data = NULL;
+	err = reftable_mmap(&p->mmap, fd, st.st_size);
+	if (err < 0)
 		goto out;
-	}
 
 	assert(!bs->ops);
 	bs->ops = &file_vtable;
diff --git a/reftable/fsck.c b/reftable/fsck.c
index 26b9115..8e73fc8 100644
--- a/reftable/fsck.c
+++ b/reftable/fsck.c
@@ -63,7 +63,7 @@ static int table_check_name(struct reftable_table *table,
 
 static int table_checks(struct reftable_table *table,
 			reftable_fsck_report_fn report_fn,
-			reftable_fsck_verbose_fn verbose_fn UNUSED,
+			reftable_fsck_verbose_fn verbose_fn REFTABLE_UNUSED,
 			void *cb_data)
 {
 	table_check_fn table_check_fns[] = {
diff --git a/reftable/reftable-basics.h b/reftable/reftable-basics.h
index 6d73f19..dc86226 100644
--- a/reftable/reftable-basics.h
+++ b/reftable/reftable-basics.h
@@ -9,7 +9,7 @@
 #ifndef REFTABLE_BASICS_H
 #define REFTABLE_BASICS_H
 
-#include <stddef.h>
+#include "reftable-system.h"
 
 /* A buffer that contains arbitrary byte slices. */
 struct reftable_buf {
diff --git a/reftable/reftable-block.h b/reftable/reftable-block.h
index 0b05a8f..94c79b5 100644
--- a/reftable/reftable-block.h
+++ b/reftable/reftable-block.h
@@ -9,8 +9,7 @@
 #ifndef REFTABLE_BLOCK_H
 #define REFTABLE_BLOCK_H
 
-#include <stdint.h>
-
+#include "reftable-system.h"
 #include "reftable-basics.h"
 #include "reftable-blocksource.h"
 #include "reftable-iterator.h"
diff --git a/reftable/reftable-blocksource.h b/reftable/reftable-blocksource.h
index f5ba867..40c1e94 100644
--- a/reftable/reftable-blocksource.h
+++ b/reftable/reftable-blocksource.h
@@ -9,7 +9,7 @@
 #ifndef REFTABLE_BLOCKSOURCE_H
 #define REFTABLE_BLOCKSOURCE_H
 
-#include <stdint.h>
+#include "reftable-system.h"
 
 /*
  * Generic wrapper for a seekable readable file.
diff --git a/reftable/reftable-error.h b/reftable/reftable-error.h
index d100e0d..0535e14 100644
--- a/reftable/reftable-error.h
+++ b/reftable/reftable-error.h
@@ -9,6 +9,8 @@
 #ifndef REFTABLE_ERROR_H
 #define REFTABLE_ERROR_H
 
+#include "reftable-system.h"
+
 /*
  * Errors in reftable calls are signaled with negative integer return values. 0
  * means success.
diff --git a/reftable/reftable-fsck.h b/reftable/reftable-fsck.h
index 007a392..340fc77 100644
--- a/reftable/reftable-fsck.h
+++ b/reftable/reftable-fsck.h
@@ -1,6 +1,7 @@
 #ifndef REFTABLE_FSCK_H
 #define REFTABLE_FSCK_H
 
+#include "reftable-system.h"
 #include "reftable-stack.h"
 
 enum reftable_fsck_error {
diff --git a/reftable/reftable-iterator.h b/reftable/reftable-iterator.h
index af58202..a050cc1 100644
--- a/reftable/reftable-iterator.h
+++ b/reftable/reftable-iterator.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_ITERATOR_H
 #define REFTABLE_ITERATOR_H
 
+#include "reftable-system.h"
 #include "reftable-record.h"
 
 struct reftable_iterator_vtable;
diff --git a/reftable/reftable-merged.h b/reftable/reftable-merged.h
index e5af846..02a9966 100644
--- a/reftable/reftable-merged.h
+++ b/reftable/reftable-merged.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_MERGED_H
 #define REFTABLE_MERGED_H
 
+#include "reftable-system.h"
 #include "reftable-iterator.h"
 
 /*
diff --git a/reftable/reftable-record.h b/reftable/reftable-record.h
index 385a74c..e18c538 100644
--- a/reftable/reftable-record.h
+++ b/reftable/reftable-record.h
@@ -9,8 +9,8 @@
 #ifndef REFTABLE_RECORD_H
 #define REFTABLE_RECORD_H
 
+#include "reftable-system.h"
 #include "reftable-basics.h"
-#include <stdint.h>
 
 /*
  * Basic data types
diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h
index c2415cb..5f7be57 100644
--- a/reftable/reftable-stack.h
+++ b/reftable/reftable-stack.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_STACK_H
 #define REFTABLE_STACK_H
 
+#include "reftable-system.h"
 #include "reftable-writer.h"
 
 /*
diff --git a/reftable/reftable-system.h b/reftable/reftable-system.h
new file mode 100644
index 0000000..76f3e33
--- /dev/null
+++ b/reftable/reftable-system.h
@@ -0,0 +1,18 @@
+#ifndef REFTABLE_SYSTEM_H
+#define REFTABLE_SYSTEM_H
+
+/*
+ * This header defines the platform-specific bits required to compile the
+ * reftable library. It should provide an environment that bridges over the
+ * gaps between POSIX and your system, as well as the zlib interfaces. This
+ * header is expected to be changed by the individual project.
+ */
+
+#define MINGW_DONT_HANDLE_IN_USE_ERROR
+#include "compat/posix.h"
+#include "compat/zlib-compat.h"
+
+int reftable_fsync(int fd);
+#define fsync(fd) reftable_fsync(fd)
+
+#endif
diff --git a/reftable/reftable-table.h b/reftable/reftable-table.h
index 5f935d0..d7666b5 100644
--- a/reftable/reftable-table.h
+++ b/reftable/reftable-table.h
@@ -9,6 +9,7 @@
 #ifndef REFTABLE_TABLE_H
 #define REFTABLE_TABLE_H
 
+#include "reftable-system.h"
 #include "reftable-iterator.h"
 #include "reftable-block.h"
 #include "reftable-blocksource.h"
diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h
index 1e7003c..a66db41 100644
--- a/reftable/reftable-writer.h
+++ b/reftable/reftable-writer.h
@@ -9,11 +9,9 @@
 #ifndef REFTABLE_WRITER_H
 #define REFTABLE_WRITER_H
 
+#include "reftable-system.h"
 #include "reftable-record.h"
 
-#include <stdint.h>
-#include <unistd.h> /* ssize_t */
-
 /* Writing single reftables */
 
 /* reftable_write_options sets options for writing a single reftable. */
@@ -64,12 +62,6 @@ struct reftable_write_options {
 	long lock_timeout_ms;
 
 	/*
-	 * Optional callback used to fsync files to disk. Falls back to using
-	 * fsync(3P) when unset.
-	 */
-	int (*fsync)(int fd);
-
-	/*
 	 * Callback function to execute whenever the stack is being reloaded.
 	 * This can be used e.g. to discard cached information that relies on
 	 * the old stack's data. The payload data will be passed as argument to
diff --git a/reftable/stack.c b/reftable/stack.c
index 1c9f21d..1fba96d 100644
--- a/reftable/stack.c
+++ b/reftable/stack.c
@@ -29,13 +29,6 @@ static int stack_filename(struct reftable_buf *dest, struct reftable_stack *st,
 	return 0;
 }
 
-static int stack_fsync(const struct reftable_write_options *opts, int fd)
-{
-	if (opts->fsync)
-		return opts->fsync(fd);
-	return fsync(fd);
-}
-
 static ssize_t reftable_write_data(int fd, const void *data, size_t size)
 {
 	size_t total_written = 0;
@@ -69,7 +62,7 @@ static ssize_t fd_writer_write(void *arg, const void *data, size_t sz)
 static int fd_writer_flush(void *arg)
 {
 	struct fd_writer *writer = arg;
-	return stack_fsync(writer->opts, writer->fd);
+	return fsync(writer->fd);
 }
 
 static int fd_read_lines(int fd, char ***namesp)
@@ -372,45 +365,26 @@ static int reftable_stack_reload_once(struct reftable_stack *st,
 	return err;
 }
 
-/* return negative if a before b. */
-static int tv_cmp(struct timeval *a, struct timeval *b)
-{
-	time_t diff = a->tv_sec - b->tv_sec;
-	int udiff = a->tv_usec - b->tv_usec;
-
-	if (diff != 0)
-		return diff;
-
-	return udiff;
-}
-
 static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
 					     int reuse_open)
 {
 	char **names = NULL, **names_after = NULL;
-	struct timeval deadline;
+	uint64_t deadline;
 	int64_t delay = 0;
 	int tries = 0, err;
 	int fd = -1;
 
-	err = gettimeofday(&deadline, NULL);
-	if (err < 0)
-		goto out;
-	deadline.tv_sec += 3;
+	deadline = reftable_time_ms() + 3000;
 
 	while (1) {
-		struct timeval now;
-
-		err = gettimeofday(&now, NULL);
-		if (err < 0)
-			goto out;
+		uint64_t now = reftable_time_ms();
 
 		/*
 		 * Only look at deadlines after the first few times. This
 		 * simplifies debugging in GDB.
 		 */
 		tries++;
-		if (tries > 3 && tv_cmp(&now, &deadline) >= 0)
+		if (tries > 3 && now >= deadline)
 			goto out;
 
 		fd = open(st->list_file, O_RDONLY);
@@ -812,7 +786,7 @@ int reftable_addition_commit(struct reftable_addition *add)
 		goto done;
 	}
 
-	err = stack_fsync(&add->stack->opts, add->tables_list_lock.fd);
+	err = fsync(add->tables_list_lock.fd);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		goto done;
@@ -1480,7 +1454,7 @@ static int stack_compact_range(struct reftable_stack *st,
 		goto done;
 	}
 
-	err = stack_fsync(&st->opts, tables_list_lock.fd);
+	err = fsync(tables_list_lock.fd);
 	if (err < 0) {
 		err = REFTABLE_IO_ERROR;
 		unlink(new_table_path.buf);
diff --git a/reftable/system.c b/reftable/system.c
index 725a258..9063641 100644
--- a/reftable/system.c
+++ b/reftable/system.c
@@ -4,7 +4,9 @@
 #include "basics.h"
 #include "reftable-error.h"
 #include "../lockfile.h"
+#include "../trace.h"
 #include "../tempfile.h"
+#include "../write-or-die.h"
 
 uint32_t reftable_rand(void)
 {
@@ -131,3 +133,33 @@ int flock_commit(struct reftable_flock *l)
 
 	return 0;
 }
+
+int reftable_fsync(int fd)
+{
+	return fsync_component(FSYNC_COMPONENT_REFERENCE, fd);
+}
+
+uint64_t reftable_time_ms(void)
+{
+	return getnanotime() / 1000000;
+}
+
+int reftable_mmap(struct reftable_mmap *out, int fd, size_t len)
+{
+	void *data = xmmap_gently(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (data == MAP_FAILED)
+		return REFTABLE_IO_ERROR;
+
+	out->data = data;
+	out->size = len;
+
+	return 0;
+}
+
+int reftable_munmap(struct reftable_mmap *mmap)
+{
+	if (munmap(mmap->data, mmap->size) < 0)
+		return REFTABLE_IO_ERROR;
+	memset(mmap, 0, sizeof(*mmap));
+	return 0;
+}
diff --git a/reftable/system.h b/reftable/system.h
index c54ed4c..c0e2cbe 100644
--- a/reftable/system.h
+++ b/reftable/system.h
@@ -9,11 +9,14 @@
 #ifndef SYSTEM_H
 #define SYSTEM_H
 
-/* This header glues the reftable library to the rest of Git */
+/*
+ * This header defines the platform-agnostic interface that is to be
+ * implemented by the project to make it work on their respective supported
+ * systems, and to integrate it into the project itself. This header is not
+ * expected to be changed by the individual project.
+ */
 
-#define MINGW_DONT_HANDLE_IN_USE_ERROR
-#include "compat/posix.h"
-#include "compat/zlib-compat.h"
+#include "reftable-system.h"
 
 /*
  * Return a random 32 bit integer. This function is expected to return
@@ -108,4 +111,25 @@ int flock_release(struct reftable_flock *l);
  */
 int flock_commit(struct reftable_flock *l);
 
+/* Report the time in milliseconds. */
+uint64_t reftable_time_ms(void);
+
+struct reftable_mmap {
+       void *data;
+       size_t size;
+       void *priv;
+};
+
+/*
+ * Map the file into memory. Returns 0 on success, a reftable error code on
+ * error.
+ */
+int reftable_mmap(struct reftable_mmap *out, int fd, size_t len);
+
+/*
+ * Unmap the file from memory. Returns 0 on success, a reftable error code on
+ * error.
+ */
+int reftable_munmap(struct reftable_mmap *mmap);
+
 #endif
diff --git a/remote.c b/remote.c
index 7ca2a65..a664cd1 100644
--- a/remote.c
+++ b/remote.c
@@ -1723,7 +1723,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
 		if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) {
 			if (starts_with(ref->name, "refs/tags/"))
 				reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
-			else if (!odb_has_object(the_repository->objects, &ref->old_oid, HAS_OBJECT_RECHECK_PACKED))
+			else if (!odb_has_object(the_repository->objects, &ref->old_oid, ODB_HAS_OBJECT_RECHECK_PACKED))
 				reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
 			else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) ||
 				 !lookup_commit_reference_gently(the_repository, &ref->new_oid, 1))
diff --git a/replay.c b/replay.c
index cf1f0bc..f96f1f6 100644
--- a/replay.c
+++ b/replay.c
@@ -358,13 +358,15 @@ int replay_revisions(struct rev_info *revs,
 	struct commit *last_commit = NULL;
 	struct commit *commit;
 	struct commit *onto = NULL;
-	struct merge_options merge_opt;
+	struct merge_options merge_opt = { 0 };
 	struct merge_result result = {
 		.clean = 1,
 	};
 	bool detached_head;
 	char *advance;
 	char *revert;
+	const char *ref;
+	struct object_id old_oid;
 	enum replay_mode mode = REPLAY_MODE_PICK;
 	int ret;
 
@@ -375,6 +377,27 @@ int replay_revisions(struct rev_info *revs,
 	set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto,
 			   &detached_head, &advance, &revert, &onto, &update_refs);
 
+	if (opts->ref) {
+		struct object_id oid;
+
+		if (update_refs && strset_get_size(update_refs) > 1) {
+			ret = error(_("'--ref' cannot be used with multiple revision ranges"));
+			goto out;
+		}
+		if (check_refname_format(opts->ref, 0) || !starts_with(opts->ref, "refs/")) {
+			ret = error(_("'%s' is not a valid refname"), opts->ref);
+			goto out;
+		}
+		ref = opts->ref;
+		if (!refs_read_ref(get_main_ref_store(revs->repo), opts->ref, &oid))
+			oidcpy(&old_oid, &oid);
+		else
+			oidclr(&old_oid, revs->repo->hash_algo);
+	} else {
+		ref = advance ? advance : revert;
+		oidcpy(&old_oid, &onto->object.oid);
+	}
+
 	if (prepare_revision_walk(revs) < 0) {
 		ret = error(_("error preparing revisions"));
 		goto out;
@@ -406,7 +429,7 @@ int replay_revisions(struct rev_info *revs,
 		kh_value(replayed_commits, pos) = last_commit;
 
 		/* Update any necessary branches */
-		if (advance || revert)
+		if (ref)
 			continue;
 
 		for (decoration = get_name_decoration(&commit->object);
@@ -440,13 +463,9 @@ int replay_revisions(struct rev_info *revs,
 		goto out;
 	}
 
-	/* In --advance or --revert mode, update the target ref */
-	if (advance || revert) {
-		const char *ref = advance ? advance : revert;
-		replay_result_queue_update(out, ref,
-					   &onto->object.oid,
+	if (ref)
+		replay_result_queue_update(out, ref, &old_oid,
 					   &last_commit->object.oid);
-	}
 
 	ret = 0;
 
diff --git a/replay.h b/replay.h
index e916a5f..0ab74b9 100644
--- a/replay.h
+++ b/replay.h
@@ -25,6 +25,13 @@ struct replay_revisions_options {
 	const char *onto;
 
 	/*
+	 * Reference to update with the result of the replay. This will not
+	 * update any refs from `onto`, `advance`, or `revert`. Ignores
+	 * `contained`.
+	 */
+	const char *ref;
+
+	/*
 	 * Starting point at which to create revert commits; must be a branch
 	 * name. The branch will be updated to point to the revert commits.
 	 * This option is mutually exclusive with `onto` and `advance`.
diff --git a/revision.c b/revision.c
index fda405b..599b3a6 100644
--- a/revision.c
+++ b/revision.c
@@ -3129,6 +3129,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 		if (want_ancestry(revs))
 			revs->limited = 1;
 		revs->topo_order = 1;
+		if (!revs->diffopt.output_format)
+			revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 	}
 
 	if (revs->topo_order && !generation_numbers_enabled(the_repository))
diff --git a/shallow.c b/shallow.c
index 7a3dd56..a8ad92e 100644
--- a/shallow.c
+++ b/shallow.c
@@ -360,7 +360,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
 		return 0;
 	if (data->flags & QUICK) {
 		if (!odb_has_object(the_repository->objects, &graft->oid,
-				    HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				    ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			return 0;
 	} else if (data->flags & SEEN_ONLY) {
 		struct commit *c = lookup_commit(the_repository, &graft->oid);
@@ -528,7 +528,7 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
 	ALLOC_ARRAY(info->theirs, sa->nr);
 	for (size_t i = 0; i < sa->nr; i++) {
 		if (odb_has_object(the_repository->objects, sa->oid + i,
-				   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+				   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 			struct commit_graft *graft;
 			graft = lookup_commit_graft(the_repository,
 						    &sa->oid[i]);
@@ -567,7 +567,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
 		if (i != dst)
 			info->theirs[dst] = info->theirs[i];
 		if (odb_has_object(the_repository->objects, oid + info->theirs[i],
-				   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))
+				   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR))
 			dst++;
 	}
 	info->nr_theirs = dst;
diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh
index 96749fc..33decc6 100755
--- a/t/t1800-hook.sh
+++ b/t/t1800-hook.sh
@@ -6,16 +6,16 @@
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 setup_hooks () {
-	test_config hook.ghi.command "/path/ghi"
-	test_config hook.ghi.event pre-commit --add
-	test_config hook.ghi.event test-hook --add
-	test_config_global hook.def.command "/path/def"
+	test_config hook.ghi.command "/path/ghi" &&
+	test_config hook.ghi.event pre-commit --add &&
+	test_config hook.ghi.event test-hook --add &&
+	test_config_global hook.def.command "/path/def" &&
 	test_config_global hook.def.event pre-commit --add
 }
 
 setup_hookdir () {
-	mkdir .git/hooks
-	write_script .git/hooks/pre-commit <<-EOF
+	mkdir -p .git/hooks &&
+	write_script .git/hooks/pre-commit <<-EOF &&
 	echo \"Legacy Hook\"
 	EOF
 	test_when_finished rm -rf .git/hooks
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 06e83d3..0a96655 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -200,6 +200,44 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'add -u avoids rename pairing on unmerged paths' '
+	test_create_repo rename-crash &&
+	(
+		cd rename-crash &&
+		test_seq 1 100 |
+		sed "s/.*/line &: same text/" >conflict.txt &&
+		cp conflict.txt bystander.txt &&
+		git add conflict.txt bystander.txt &&
+		git commit -m "initial: two files with identical content" &&
+		main_branch=$(git symbolic-ref --short HEAD) &&
+		git checkout -b feature &&
+		sed "s/^line 50:.*/line 50: FEATURE/" \
+			conflict.txt >conflict.txt.tmp &&
+		mv conflict.txt.tmp conflict.txt &&
+		git add conflict.txt &&
+		git commit -m "feature: modify line 50" &&
+		git checkout "$main_branch" &&
+		sed "s/^line 50:.*/line 50: MAIN/" \
+			conflict.txt >conflict.txt.tmp &&
+		mv conflict.txt.tmp conflict.txt &&
+		git add conflict.txt &&
+		git commit -m "main: modify line 50 differently" &&
+		test_must_fail git merge feature &&
+		rm bystander.txt &&
+		git add -u >out &&
+		test_must_be_empty out &&
+		git ls-files -u >actual &&
+		test_must_be_empty actual &&
+		git ls-files bystander.txt conflict.txt >actual &&
+		cat >expect <<-\EOF &&
+		conflict.txt
+		EOF
+		test_cmp expect actual &&
+		git diff-files --name-only >actual &&
+		test_must_be_empty actual
+	)
+'
+
 test_expect_success '"add -u non-existent" should fail' '
 	test_must_fail git add -u non-existent &&
 	git ls-files >actual &&
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index 89819ad..3353bc4 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -499,4 +499,70 @@
 	test_grep "cannot be used together" error
 '
 
+test_expect_success 'using --onto with --ref' '
+	git branch test-ref-onto topic2 &&
+	test_when_finished "git branch -D test-ref-onto" &&
+
+	git replay --ref-action=print --onto=main --ref=refs/heads/test-ref-onto topic1..topic2 >result &&
+
+	test_line_count = 1 result &&
+	test_grep "^update refs/heads/test-ref-onto " result &&
+
+	git log --format=%s $(cut -f 3 -d " " result) >actual &&
+	test_write_lines E D M L B A >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success 'using --advance with --ref' '
+	git branch test-ref-advance main &&
+	git branch test-ref-target main &&
+	test_when_finished "git branch -D test-ref-advance test-ref-target" &&
+
+	git replay --ref-action=print --advance=test-ref-advance --ref=refs/heads/test-ref-target topic1..topic2 >result &&
+
+	test_line_count = 1 result &&
+	test_grep "^update refs/heads/test-ref-target " result
+'
+
+test_expect_success 'using --revert with --ref' '
+	git branch test-ref-revert topic4 &&
+	git branch test-ref-revert-target topic4 &&
+	test_when_finished "git branch -D test-ref-revert test-ref-revert-target" &&
+
+	git replay --ref-action=print --revert=test-ref-revert --ref=refs/heads/test-ref-revert-target topic4~1..topic4 >result &&
+
+	test_line_count = 1 result &&
+	test_grep "^update refs/heads/test-ref-revert-target " result
+'
+
+test_expect_success '--ref is incompatible with --contained' '
+	test_must_fail git replay --onto=main --ref=refs/heads/main --contained topic1..topic2 2>err &&
+	test_grep "cannot be used together" err
+'
+
+test_expect_success '--ref with nonexistent fully-qualified ref' '
+	test_when_finished "git update-ref -d refs/heads/new-branch" &&
+
+	git replay --onto=main --ref=refs/heads/new-branch topic1..topic2 &&
+
+	git log --format=%s -2 new-branch >actual &&
+	test_write_lines E D >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '--ref must be a valid refname' '
+	test_must_fail git replay --onto=main --ref="refs/heads/bad..ref" topic1..topic2 2>err &&
+	test_grep "is not a valid refname" err
+'
+
+test_expect_success '--ref requires fully qualified ref' '
+	test_must_fail git replay --onto=main --ref=main topic1..topic2 2>err &&
+	test_grep "is not a valid refname" err
+'
+
+test_expect_success '--onto with --ref rejects multiple revision ranges' '
+	test_must_fail git replay --onto=main --ref=refs/heads/topic2 ^topic1 topic2 topic4 2>err &&
+	test_grep "cannot be used with multiple revision ranges" err
+'
+
 test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 29ea7d4..205d86d 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -561,6 +561,22 @@
 	git config core.whitespace incomplete-line
 '
 
+test_expect_success 'no incomplete context line (not an error)' '
+	test_when_finished "rm -f sample*-i patch patch-new target" &&
+	test_write_lines 1 2 3 "" 4 5 >sample-i &&
+	test_write_lines 1 2 3 "" 0 5 >sample2-i &&
+	cat sample-i >target &&
+	git add target &&
+	cat sample2-i >target &&
+	git diff-files -p target >patch &&
+	sed -e "s/^ $//" <patch >patch-new &&
+
+	cat sample-i >target &&
+	git apply --whitespace=fix <patch-new 2>error &&
+	test_cmp sample2-i target &&
+	test_must_be_empty error
+'
+
 test_expect_success 'incomplete context line (not an error)' '
 	(test_write_lines 1 2 3 4 5 && printf 6) >sample-i &&
 	(test_write_lines 1 2 3 0 5 && printf 6) >sample2-i &&
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 0a7c3ca..aaf197d 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -129,7 +129,7 @@
 	git checkout parallel-change &&
 	git log --output=log -L :main:b.c >output &&
 	test_must_be_empty output &&
-	test_line_count = 70 log
+	test_line_count = 75 log
 '
 
 test_expect_success 'range_set_union' '
@@ -339,14 +339,106 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'setup for diff pipeline tests' '
+	git checkout parent-oids &&
+
+	head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
+	head_blob_new=$(git rev-parse --short HEAD:file.c) &&
+	root_blob=$(git rev-parse --short HEAD~4:file.c) &&
+	null_blob=$(test_oid zero | cut -c1-7) &&
+	head_blob_old_full=$(git rev-parse HEAD^:file.c) &&
+	head_blob_new_full=$(git rev-parse HEAD:file.c) &&
+	root_blob_full=$(git rev-parse HEAD~4:file.c) &&
+	null_blob_full=$(test_oid zero)
+'
+
+test_expect_success '-L diff output includes index and new file mode' '
+	git log -L:func2:file.c --format= >actual &&
+
+	# Output should contain index headers (not present in old code path)
+	grep "^index $head_blob_old\.\.$head_blob_new 100644" actual &&
+
+	# Root commit should show new file mode and null index
+	grep "^new file mode 100644" actual &&
+	grep "^index $null_blob\.\.$root_blob$" actual &&
+
+	# Hunk headers should include funcname context
+	grep "^@@ .* @@ int func1()" actual
+'
+
+test_expect_success '-L with --word-diff' '
+	cat >expect <<-\EOF &&
+
+	diff --git a/file.c b/file.c
+	--- a/file.c
+	+++ b/file.c
+	@@ -6,4 +6,4 @@ int func1()
+	int func2()
+	{
+	    return [-F2;-]{+F2 + 2;+}
+	}
+
+	diff --git a/file.c b/file.c
+	new file mode 100644
+	--- /dev/null
+	+++ b/file.c
+	@@ -0,0 +6,4 @@
+	{+int func2()+}
+	{+{+}
+	{+    return F2;+}
+	{+}+}
+	EOF
+	git log -L:func2:file.c --word-diff --format= >actual &&
+	grep -v "^index " actual >actual.filtered &&
+	grep -v "^index " expect >expect.filtered &&
+	test_cmp expect.filtered actual.filtered
+'
+
+test_expect_success '-L with --no-prefix' '
+	git log -L:func2:file.c --no-prefix --format= >actual &&
+	grep "^diff --git file.c file.c" actual &&
+	grep "^--- file.c" actual &&
+	! grep "^--- a/" actual
+'
+
+test_expect_success '-L with --full-index' '
+	git log -L:func2:file.c --full-index --format= >actual &&
+	grep "^index $head_blob_old_full\.\.$head_blob_new_full 100644" actual &&
+	grep "^index $null_blob_full\.\.$root_blob_full$" actual
+'
+
+test_expect_success 'setup -L with whitespace change' '
+	git checkout -b ws-change parent-oids &&
+	sed "s/    return F2 + 2;/	return F2 + 2;/" file.c >tmp &&
+	mv tmp file.c &&
+	git commit -a -m "Whitespace change in func2()"
+'
+
+test_expect_success '-L with --ignore-all-space suppresses whitespace-only diff' '
+	git log -L:func2:file.c --format= >without_w &&
+	git log -L:func2:file.c --format= -w >with_w &&
+
+	# Without -w: three commits produce diffs (whitespace, modify, root)
+	test $(grep -c "^diff --git" without_w) = 3 &&
+
+	# With -w: whitespace-only commit produces no hunk, so only two diffs
+	test $(grep -c "^diff --git" with_w) = 2
+'
+
 test_expect_success 'show line-log with graph' '
+	git checkout parent-oids &&
+	head_blob_old=$(git rev-parse --short HEAD^:file.c) &&
+	head_blob_new=$(git rev-parse --short HEAD:file.c) &&
+	root_blob=$(git rev-parse --short HEAD~4:file.c) &&
+	null_blob=$(test_oid zero | cut -c1-7) &&
 	qz_to_tab_space >expect <<-EOF &&
 	* $head_oid Modify func2() in file.c
 	|Z
 	| diff --git a/file.c b/file.c
+	| index $head_blob_old..$head_blob_new 100644
 	| --- a/file.c
 	| +++ b/file.c
-	| @@ -6,4 +6,4 @@
+	| @@ -6,4 +6,4 @@ int func1()
 	|  int func2()
 	|  {
 	| -    return F2;
@@ -355,6 +447,8 @@
 	* $root_oid Add func1() and func2() in file.c
 	ZZ
 	  diff --git a/file.c b/file.c
+	  new file mode 100644
+	  index $null_blob..$root_blob
 	  --- /dev/null
 	  +++ b/file.c
 	  @@ -0,0 +6,4 @@
@@ -367,4 +461,254 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'setup for -L with -G/-S/--find-object and a merge with rename' '
+	git checkout --orphan pickaxe-rename &&
+	git reset --hard &&
+
+	echo content >file &&
+	git add file &&
+	git commit -m "add file" &&
+
+	git checkout -b pickaxe-rename-side &&
+	git mv file renamed-file &&
+	git commit -m "rename file" &&
+
+	git checkout pickaxe-rename &&
+	git commit --allow-empty -m "diverge" &&
+	git merge --no-edit pickaxe-rename-side &&
+
+	git mv renamed-file file &&
+	git commit -m "rename back"
+'
+
+test_expect_success '-L -G does not crash with merge and rename' '
+	git log --format="%s" --no-patch -L 1,1:file -G "." >actual
+'
+
+test_expect_success '-L -S does not crash with merge and rename' '
+	git log --format="%s" --no-patch -L 1,1:file -S content >actual
+'
+
+test_expect_success '-L --find-object does not crash with merge and rename' '
+	git log --format="%s" --no-patch -L 1,1:file \
+		--find-object=$(git rev-parse HEAD:file) >actual
+'
+
+# Commit-level filtering with pickaxe does not yet work for -L.
+# show_log() prints the commit header before diffcore_std() runs
+# pickaxe, so commits cannot be suppressed even when no diff pairs
+# survive filtering.  Fixing this would require deferring show_log()
+# until after diffcore_std(), which is a larger restructuring of the
+# log-tree output pipeline.
+test_expect_failure '-L -G should filter commits by pattern' '
+	git log --format="%s" --no-patch -L 1,1:file -G "nomatch" >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_failure '-L -S should filter commits by pattern' '
+	git log --format="%s" --no-patch -L 1,1:file -S "nomatch" >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_failure '-L --find-object should filter commits by object' '
+	git log --format="%s" --no-patch -L 1,1:file \
+		--find-object=$ZERO_OID >actual &&
+	test_must_be_empty actual
+'
+
+test_expect_success '-L with --word-diff-regex' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --word-diff \
+		--word-diff-regex="[a-zA-Z0-9_]+" --format= >actual &&
+	# Word-diff markers must be present
+	grep "{+" actual &&
+	grep "+}" actual &&
+	# No line-level +/- markers (word-diff replaces them);
+	# exclude --- header lines from the check
+	! grep "^+[^+]" actual &&
+	! grep "^-[^-]" actual
+'
+
+test_expect_success '-L with --src-prefix and --dst-prefix' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --src-prefix=old/ --dst-prefix=new/ \
+		--format= >actual &&
+	grep "^diff --git old/file.c new/file.c" actual &&
+	grep "^--- old/file.c" actual &&
+	grep "^+++ new/file.c" actual &&
+	! grep "^--- a/" actual
+'
+
+test_expect_success '-L with --abbrev' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --abbrev=4 --format= -1 >actual &&
+	# 4-char abbreviated hashes on index line
+	grep "^index [0-9a-f]\{4\}\.\.[0-9a-f]\{4\}" actual
+'
+
+test_expect_success '-L with -b suppresses whitespace-only diff' '
+	git checkout ws-change &&
+	git log -L:func2:file.c --format= >without_b &&
+	git log -L:func2:file.c --format= -b >with_b &&
+	test $(grep -c "^diff --git" without_b) = 3 &&
+	test $(grep -c "^diff --git" with_b) = 2
+'
+
+test_expect_success '-L with --output-indicator-*' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c --output-indicator-new=">" \
+		--output-indicator-old="<" --output-indicator-context="|" \
+		--format= -1 >actual &&
+	grep "^>" actual &&
+	grep "^<" actual &&
+	grep "^|" actual &&
+	# No standard +/-/space content markers; exclude ---/+++ headers
+	! grep "^+[^+]" actual &&
+	! grep "^-[^-]" actual &&
+	! grep "^ " actual
+'
+
+test_expect_success '-L with -R reverses diff' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c -R --format= -1 >actual &&
+	grep "^diff --git b/file.c a/file.c" actual &&
+	grep "^--- b/file.c" actual &&
+	grep "^+++ a/file.c" actual &&
+	# The modification added "F2 + 2", so reversed it is removed
+	grep "^-.*F2 + 2" actual &&
+	grep "^+.*return F2;" actual
+'
+
+test_expect_success 'setup for color-moved test' '
+	git checkout -b color-moved-test parent-oids &&
+	cat >big.c <<-\EOF &&
+	int bigfunc()
+	{
+	    int a = 1;
+	    int b = 2;
+	    int c = 3;
+	    return a + b + c;
+	}
+	EOF
+	git add big.c &&
+	git commit -m "add bigfunc" &&
+	sed "s/    /	/" big.c >tmp && mv tmp big.c &&
+	git commit -a -m "reindent bigfunc"
+'
+
+test_expect_success '-L with --color-moved' '
+	git log -L:bigfunc:big.c --color-moved=zebra \
+		--color-moved-ws=ignore-all-space \
+		--color=always --format= -1 >actual.raw &&
+	test_decode_color <actual.raw >actual &&
+	# Old moved lines: bold magenta; new moved lines: bold cyan
+	grep "BOLD;MAGENTA" actual &&
+	grep "BOLD;CYAN" actual
+'
+
+test_expect_success 'setup for no-newline-at-eof tests' '
+	git checkout --orphan no-newline &&
+	git reset --hard &&
+	printf "int top()\n{\n    return 1;\n}\n\nint bot()\n{\n    return 2;\n}" >noeol.c &&
+	git add noeol.c &&
+	test_tick &&
+	git commit -m "add noeol.c (no trailing newline)" &&
+	sed "s/return 2/return 22/" noeol.c >tmp && mv tmp noeol.c &&
+	git commit -a -m "modify bot()" &&
+	printf "int top()\n{\n    return 1;\n}\n\nint bot()\n{\n    return 33;\n}\n" >noeol.c &&
+	git commit -a -m "modify bot() and add trailing newline"
+'
+
+# When the tracked function is at the end of a file with no trailing
+# newline, the "\ No newline at end of file" marker should appear.
+test_expect_success '-L no-newline-at-eof appears in tracked range' '
+	git log -L:bot:noeol.c --format= -1 HEAD~1 >actual &&
+	grep "No newline at end of file" actual
+'
+
+# When tracking a function that ends before the no-newline content,
+# the marker should not appear in the output.
+test_expect_success '-L no-newline-at-eof suppressed outside range' '
+	git log -L:top:noeol.c --format= >actual &&
+	! grep "No newline at end of file" actual
+'
+
+# When a commit removes a no-newline last line and replaces it with
+# a newline-terminated line, the marker should still appear (on the
+# old side of the diff).
+test_expect_success '-L no-newline-at-eof marker with deleted line' '
+	git log -L:bot:noeol.c --format= -1 >actual &&
+	grep "No newline at end of file" actual
+'
+
+test_expect_success 'setup for range boundary deletion test' '
+	git checkout --orphan range-boundary &&
+	git reset --hard &&
+	cat >boundary.c <<-\EOF &&
+	void above()
+	{
+	    return;
+	}
+
+	void tracked()
+	{
+	    int x = 1;
+	    int y = 2;
+	}
+
+	void below()
+	{
+	    return;
+	}
+	EOF
+	git add boundary.c &&
+	test_tick &&
+	git commit -m "add boundary.c" &&
+	cat >boundary.c <<-\EOF &&
+	void above()
+	{
+	    return;
+	}
+
+	void tracked()
+	{
+	    int x = 1;
+	    int y = 2;
+	}
+
+	void below_renamed()
+	{
+	    return 0;
+	}
+	EOF
+	git commit -a -m "modify below() only"
+'
+
+# When only a function below the tracked range is modified, the
+# tracked function should not produce a diff.
+test_expect_success '-L suppresses deletions outside tracked range' '
+	git log -L:tracked:boundary.c --format= >actual &&
+	test $(grep -c "^diff --git" actual) = 1
+'
+
+test_expect_success '-L with -S filters to string-count changes' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c -S "F2 + 2" --format= >actual &&
+	# -S searches the whole file, not just the tracked range;
+	# combined with the -L range walk, this selects commits that
+	# both touch func2 and change the count of "F2 + 2" in the file.
+	test $(grep -c "^diff --git" actual) = 1 &&
+	grep "F2 + 2" actual
+'
+
+test_expect_success '-L with -G filters to diff-text matches' '
+	git checkout parent-oids &&
+	git log -L:func2:file.c -G "F2 [+] 2" --format= >actual &&
+	# -G greps the whole-file diff text, not just the tracked range;
+	# combined with -L, this selects commits that both touch func2
+	# and have "F2 + 2" in their diff.
+	test $(grep -c "^diff --git" actual) = 1 &&
+	grep "F2 + 2" actual
+'
+
 test_done
diff --git a/t/t4211/sha1/expect.beginning-of-file b/t/t4211/sha1/expect.beginning-of-file
index 91b4054..52c90af 100644
--- a/t/t4211/sha1/expect.beginning-of-file
+++ b/t/t4211/sha1/expect.beginning-of-file
@@ -5,6 +5,7 @@
     change at very beginning
 
 diff --git a/a.c b/a.c
+index bdb2bb1..5e709a1 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,4 @@
@@ -20,6 +21,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,3 @@
@@ -35,6 +37,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +1,3 @@
diff --git a/t/t4211/sha1/expect.end-of-file b/t/t4211/sha1/expect.end-of-file
index bd25bb2..c400368 100644
--- a/t/t4211/sha1/expect.end-of-file
+++ b/t/t4211/sha1/expect.end-of-file
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,5 @@
+@@ -20,3 +20,5 @@ long f(long x)
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,3 @@
+@@ -20,3 +20,3 @@ int main ()
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -39,9 +41,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -19,3 +19,3 @@
+@@ -19,3 +19,3 @@ int f(int x)
 -	printf("%d\n", f(15));
 +	printf("%ld\n", f(15));
  	return 0;
@@ -54,6 +57,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +18,3 @@
diff --git a/t/t4211/sha1/expect.move-support-f b/t/t4211/sha1/expect.move-support-f
index c905e01..ead6500 100644
--- a/t/t4211/sha1/expect.move-support-f
+++ b/t/t4211/sha1/expect.move-support-f
@@ -5,6 +5,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 5de3ea4..bf79c2f 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,9 +4,9 @@
@@ -26,6 +27,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -47,6 +49,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -67,6 +70,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.multiple b/t/t4211/sha1/expect.multiple
index 1eee8a7..a41851a 100644
--- a/t/t4211/sha1/expect.multiple
+++ b/t/t4211/sha1/expect.multiple
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,6 +45,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -71,6 +74,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -91,6 +95,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.multiple-overlapping b/t/t4211/sha1/expect.multiple-overlapping
index d930b6e..0ec9990 100644
--- a/t/t4211/sha1/expect.multiple-overlapping
+++ b/t/t4211/sha1/expect.multiple-overlapping
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha1/expect.multiple-superset b/t/t4211/sha1/expect.multiple-superset
index d930b6e..0ec9990 100644
--- a/t/t4211/sha1/expect.multiple-superset
+++ b/t/t4211/sha1/expect.multiple-superset
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha1/expect.no-assertion-error b/t/t4211/sha1/expect.no-assertion-error
index 994c37d..54c568f 100644
--- a/t/t4211/sha1/expect.no-assertion-error
+++ b/t/t4211/sha1/expect.no-assertion-error
@@ -5,6 +5,7 @@
     move within the file
 
 diff --git a/b.c b/b.c
+index bf79c2f..27c829c 100644
 --- a/b.c
 +++ b/b.c
 @@ -25,0 +18,9 @@
@@ -25,9 +26,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -45,9 +47,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -63,9 +66,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -80,6 +84,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.parallel-change-f-to-main b/t/t4211/sha1/expect.parallel-change-f-to-main
index 052def8..65a8cc6 100644
--- a/t/t4211/sha1/expect.parallel-change-f-to-main
+++ b/t/t4211/sha1/expect.parallel-change-f-to-main
@@ -13,6 +13,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 5de3ea4..bf79c2f 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,14 +4,14 @@
@@ -39,6 +40,7 @@
     change on another line of history while rename happens
 
 diff --git a/a.c b/a.c
+index 5de3ea4..01b5b65 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,14 +4,14 @@
@@ -65,6 +67,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -91,6 +94,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -117,6 +121,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -142,6 +147,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha1/expect.simple-f b/t/t4211/sha1/expect.simple-f
index a1f5bc4..b24ae40 100644
--- a/t/t4211/sha1/expect.simple-f
+++ b/t/t4211/sha1/expect.simple-f
@@ -5,6 +5,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -26,6 +27,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -46,6 +48,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.simple-f-to-main b/t/t4211/sha1/expect.simple-f-to-main
index a475768..cd92100 100644
--- a/t/t4211/sha1/expect.simple-f-to-main
+++ b/t/t4211/sha1/expect.simple-f-to-main
@@ -5,6 +5,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index e51de13..bdb2bb1 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -31,6 +32,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -57,6 +59,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -82,6 +85,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha1/expect.simple-main b/t/t4211/sha1/expect.simple-main
index 39ce39b..ff31291 100644
--- a/t/t4211/sha1/expect.simple-main
+++ b/t/t4211/sha1/expect.simple-main
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,9 +43,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -58,6 +61,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.simple-main-to-end b/t/t4211/sha1/expect.simple-main-to-end
index 8480bd9..4bef21e 100644
--- a/t/t4211/sha1/expect.simple-main-to-end
+++ b/t/t4211/sha1/expect.simple-main-to-end
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,9 +45,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -60,6 +63,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha1/expect.two-ranges b/t/t4211/sha1/expect.two-ranges
index c5164f3..aed0152 100644
--- a/t/t4211/sha1/expect.two-ranges
+++ b/t/t4211/sha1/expect.two-ranges
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,6 +43,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 3233403..e51de13 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -69,6 +72,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 444e415..3233403 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -89,6 +93,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha1/expect.vanishes-early b/t/t4211/sha1/expect.vanishes-early
index 1f7cd06..a413ad3 100644
--- a/t/t4211/sha1/expect.vanishes-early
+++ b/t/t4211/sha1/expect.vanishes-early
@@ -5,11 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index 0b9cae5..5de3ea4 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +24,1 @@
--}
-\ No newline at end of file
+@@ -23,0 +24,1 @@ int main ()
 +/* incomplete lines are bad! */
 
 commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
@@ -19,9 +18,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index 5e709a1..0b9cae5 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +22,1 @@
+@@ -22,1 +22,1 @@ int main ()
 -}
 +}
 \ No newline at end of file
@@ -33,6 +33,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..444e415
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +20,1 @@
diff --git a/t/t4211/sha256/expect.beginning-of-file b/t/t4211/sha256/expect.beginning-of-file
index 5adfdfc..e8d6232 100644
--- a/t/t4211/sha256/expect.beginning-of-file
+++ b/t/t4211/sha256/expect.beginning-of-file
@@ -5,6 +5,7 @@
     change at very beginning
 
 diff --git a/a.c b/a.c
+index 3a78aaf..d325124 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,4 @@
@@ -20,6 +21,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -1,3 +1,3 @@
@@ -35,6 +37,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +1,3 @@
diff --git a/t/t4211/sha256/expect.end-of-file b/t/t4211/sha256/expect.end-of-file
index 03ab5c1..3b2e238 100644
--- a/t/t4211/sha256/expect.end-of-file
+++ b/t/t4211/sha256/expect.end-of-file
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,5 @@
+@@ -20,3 +20,5 @@ long f(long x)
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -20,3 +20,3 @@
+@@ -20,3 +20,3 @@ int main ()
  	printf("%ld\n", f(15));
  	return 0;
 -}
@@ -39,9 +41,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -19,3 +19,3 @@
+@@ -19,3 +19,3 @@ int f(int x)
 -	printf("%d\n", f(15));
 +	printf("%ld\n", f(15));
  	return 0;
@@ -54,6 +57,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +18,3 @@
diff --git a/t/t4211/sha256/expect.move-support-f b/t/t4211/sha256/expect.move-support-f
index 223b4ed..f49abce 100644
--- a/t/t4211/sha256/expect.move-support-f
+++ b/t/t4211/sha256/expect.move-support-f
@@ -5,6 +5,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 62c1fc2..69cb69c 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,9 +4,9 @@
@@ -26,6 +27,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -47,6 +49,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -67,6 +70,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.multiple b/t/t4211/sha256/expect.multiple
index dbd987b..0dee50f 100644
--- a/t/t4211/sha256/expect.multiple
+++ b/t/t4211/sha256/expect.multiple
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,6 +45,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -71,6 +74,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -91,6 +95,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.multiple-overlapping b/t/t4211/sha256/expect.multiple-overlapping
index 9015a45..b8c260e 100644
--- a/t/t4211/sha256/expect.multiple-overlapping
+++ b/t/t4211/sha256/expect.multiple-overlapping
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha256/expect.multiple-superset b/t/t4211/sha256/expect.multiple-superset
index 9015a45..b8c260e 100644
--- a/t/t4211/sha256/expect.multiple-superset
+++ b/t/t4211/sha256/expect.multiple-superset
@@ -5,6 +5,7 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,21 @@
@@ -39,6 +40,7 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,19 +4,19 @@
@@ -71,6 +73,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -102,6 +105,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,19 +3,19 @@
@@ -134,6 +138,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,18 +3,19 @@
@@ -164,6 +169,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,18 @@
diff --git a/t/t4211/sha256/expect.no-assertion-error b/t/t4211/sha256/expect.no-assertion-error
index 36ed12a..c25f2ce 100644
--- a/t/t4211/sha256/expect.no-assertion-error
+++ b/t/t4211/sha256/expect.no-assertion-error
@@ -5,6 +5,7 @@
     move within the file
 
 diff --git a/b.c b/b.c
+index 69cb69c..a0d566e 100644
 --- a/b.c
 +++ b/b.c
 @@ -25,0 +18,9 @@
@@ -25,9 +26,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -45,9 +47,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -63,9 +66,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -80,6 +84,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.parallel-change-f-to-main b/t/t4211/sha256/expect.parallel-change-f-to-main
index e68f892..3178989 100644
--- a/t/t4211/sha256/expect.parallel-change-f-to-main
+++ b/t/t4211/sha256/expect.parallel-change-f-to-main
@@ -13,6 +13,7 @@
     another simple change
 
 diff --git a/b.c b/b.c
+index 62c1fc2..69cb69c 100644
 --- a/b.c
 +++ b/b.c
 @@ -4,14 +4,14 @@
@@ -39,6 +40,7 @@
     change on another line of history while rename happens
 
 diff --git a/a.c b/a.c
+index 62c1fc2..e1e8475 100644
 --- a/a.c
 +++ b/a.c
 @@ -4,14 +4,14 @@
@@ -65,6 +67,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -91,6 +94,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -117,6 +121,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -142,6 +147,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha256/expect.simple-f b/t/t4211/sha256/expect.simple-f
index 65508d7..983c711 100644
--- a/t/t4211/sha256/expect.simple-f
+++ b/t/t4211/sha256/expect.simple-f
@@ -5,6 +5,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -26,6 +27,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -46,6 +48,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.simple-f-to-main b/t/t4211/sha256/expect.simple-f-to-main
index 77b721c..e67fa01 100644
--- a/t/t4211/sha256/expect.simple-f-to-main
+++ b/t/t4211/sha256/expect.simple-f-to-main
@@ -5,6 +5,7 @@
     touch comment
 
 diff --git a/a.c b/a.c
+index 75c0119..3a78aaf 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -31,6 +32,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,14 +3,14 @@
@@ -57,6 +59,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,13 +3,14 @@
@@ -82,6 +85,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,13 @@
diff --git a/t/t4211/sha256/expect.simple-main b/t/t4211/sha256/expect.simple-main
index d20708c..0792b27 100644
--- a/t/t4211/sha256/expect.simple-main
+++ b/t/t4211/sha256/expect.simple-main
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,9 +43,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -58,6 +61,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.simple-main-to-end b/t/t4211/sha256/expect.simple-main-to-end
index 617cdf3..d3bd7c7 100644
--- a/t/t4211/sha256/expect.simple-main-to-end
+++ b/t/t4211/sha256/expect.simple-main-to-end
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,7 @@
+@@ -18,5 +18,7 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -25,9 +26,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -43,9 +45,10 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
-@@ -17,5 +17,5 @@
+@@ -17,5 +17,5 @@ int f(int x)
  int main ()
  {
 -	printf("%d\n", f(15));
@@ -60,6 +63,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +16,5 @@
diff --git a/t/t4211/sha256/expect.two-ranges b/t/t4211/sha256/expect.two-ranges
index 6a94d3b..7735b19 100644
--- a/t/t4211/sha256/expect.two-ranges
+++ b/t/t4211/sha256/expect.two-ranges
@@ -5,9 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -23,9 +24,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -18,5 +18,5 @@
+@@ -18,5 +18,5 @@ long f(long x)
  int main ()
  {
  	printf("%ld\n", f(15));
@@ -41,6 +43,7 @@
     touch both functions
 
 diff --git a/a.c b/a.c
+index 7a296b9..75c0119 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,9 +3,9 @@
@@ -69,6 +72,7 @@
     change f()
 
 diff --git a/a.c b/a.c
+index 9f550c3..7a296b9 100644
 --- a/a.c
 +++ b/a.c
 @@ -3,8 +3,9 @@
@@ -89,6 +93,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +3,8 @@
diff --git a/t/t4211/sha256/expect.vanishes-early b/t/t4211/sha256/expect.vanishes-early
index 11ec9bd..bc33b96 100644
--- a/t/t4211/sha256/expect.vanishes-early
+++ b/t/t4211/sha256/expect.vanishes-early
@@ -5,11 +5,10 @@
     change back to complete line
 
 diff --git a/a.c b/a.c
+index e4fa1d8..62c1fc2 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +24,1 @@
--}
-\ No newline at end of file
+@@ -23,0 +24,1 @@ int main ()
 +/* incomplete lines are bad! */
 
 commit 29f32ac3141c48b22803e5c4127b719917b67d0f8ca8c5248bebfa2a19f7da10
@@ -19,9 +18,10 @@
     change to an incomplete line at end
 
 diff --git a/a.c b/a.c
+index d325124..e4fa1d8 100644
 --- a/a.c
 +++ b/a.c
-@@ -22,1 +22,1 @@
+@@ -22,1 +22,1 @@ int main ()
 -}
 +}
 \ No newline at end of file
@@ -33,6 +33,8 @@
     initial
 
 diff --git a/a.c b/a.c
+new file mode 100644
+index 0000000..9f550c3
 --- /dev/null
 +++ b/a.c
 @@ -0,0 +20,1 @@
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 98c6910..1c40f90 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -417,6 +417,26 @@
 	test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded
 '
 
+test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'overflow chunk when replacing commit-graph' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		cat >commit <<-EOF &&
+		tree $(test_oid empty_tree)
+		author Example <committer@example.com> 9223372036854775 +0000
+		committer Example <committer@example.com> 9223372036854775 +0000
+
+		Weird commit date
+		EOF
+		commit_id=$(git hash-object -t commit -w commit) &&
+		git reset --hard "$commit_id" &&
+		git commit-graph write --reachable &&
+		git commit-graph write --reachable --split=replace &&
+		git log
+	)
+'
+
 # the verify tests below expect the commit-graph to contain
 # exactly the commits reachable from the commits/8 branch.
 # If the file changes the set of commits in the list, then the
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 29e2f17..117cfa0 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -1792,6 +1792,7 @@
 '
 
 test_expect_success 'denyCurrentBranch and worktrees' '
+	test_when_finished "rm -fr cloned && git worktree remove --force new-wt" &&
 	git worktree add new-wt &&
 	git clone . cloned &&
 	test_commit -C cloned first &&
@@ -1816,6 +1817,20 @@
 	test_must_fail git push --delete bare.git wt
 '
 
+test_expect_success 'updateInstead with bare repository worktree and unborn bare HEAD' '
+	test_when_finished "rm -fr bare.git cloned" &&
+	git clone --bare . bare.git &&
+	git -C bare.git worktree add wt &&
+	git -C bare.git config receive.denyCurrentBranch updateInstead &&
+	git -C bare.git symbolic-ref HEAD refs/heads/unborn &&
+	test_must_fail git -C bare.git rev-parse -q --verify HEAD^{commit} &&
+	git clone . cloned &&
+	test_commit -C cloned mozzarella &&
+	git -C cloned push ../bare.git HEAD:wt &&
+	test_path_exists bare.git/wt/mozzarella.t &&
+	test "$(git -C cloned rev-parse HEAD)" = "$(git -C bare.git/wt rev-parse HEAD)"
+'
+
 test_expect_success 'refuse fetch to current branch of worktree' '
 	test_when_finished "git worktree remove --force wt && git branch -D wt" &&
 	git worktree add wt &&
diff --git a/t/t5620-backfill.sh b/t/t5620-backfill.sh
index 2c347a9..f3b5e39 100755
--- a/t/t5620-backfill.sh
+++ b/t/t5620-backfill.sh
@@ -175,6 +175,21 @@
 	test_line_count = 0 missing
 '
 
+test_expect_success 'backfill auto-detects sparse-checkout from config' '
+	git clone --sparse --filter=blob:none \
+		--single-branch --branch=main \
+		"file://$(pwd)/srv.bare" backfill-auto-sparse &&
+
+	git -C backfill-auto-sparse rev-list --quiet --objects --missing=print HEAD >missing &&
+	test_line_count = 44 missing &&
+
+	GIT_TRACE2_EVENT="$(pwd)/auto-sparse-trace" git \
+		-C backfill-auto-sparse backfill &&
+
+	test_trace2_data promisor fetch_count 4 <auto-sparse-trace &&
+	test_trace2_data path-walk paths 5 <auto-sparse-trace
+'
+
 test_expect_success 'backfill --sparse without cone mode (positive)' '
 	git clone --no-checkout --filter=blob:none		\
 		--single-branch --branch=main 		\
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index ce2ff2a..faf7d97 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -33,8 +33,10 @@
 '
 
 test_expect_success 'listing all tags in an empty tree should output nothing' '
-	test $(git tag -l | wc -l) -eq 0 &&
-	test $(git tag | wc -l) -eq 0
+	git tag -l >actual &&
+	test_must_be_empty actual &&
+	git tag >actual &&
+	test_must_be_empty actual
 '
 
 test_expect_success 'sort tags, ignore case' '
@@ -178,7 +180,8 @@
 '
 
 test_expect_success 'listing tags using a non-matching pattern should output nothing' '
-	test $(git tag -l xxx | wc -l) -eq 0
+	git tag -l xxx >actual &&
+	test_must_be_empty actual
 '
 
 # special cases for creating tags:
@@ -188,13 +191,15 @@
 '
 
 test_expect_success 'trying to create a tag with a non-valid name should fail' '
-	test $(git tag -l | wc -l) -eq 1 &&
+	git tag -l >actual &&
+	test_line_count = 1 actual &&
 	test_must_fail git tag "" &&
 	test_must_fail git tag .othertag &&
 	test_must_fail git tag "other tag" &&
 	test_must_fail git tag "othertag^" &&
 	test_must_fail git tag "other~tag" &&
-	test $(git tag -l | wc -l) -eq 1
+	git tag -l >actual &&
+	test_line_count = 1 actual
 '
 
 test_expect_success 'creating a tag using HEAD directly should succeed' '
diff --git a/t/t9305-fast-import-signatures.sh b/t/t9305-fast-import-signatures.sh
index 18707b3..5667693 100755
--- a/t/t9305-fast-import-signatures.sh
+++ b/t/t9305-fast-import-signatures.sh
@@ -103,7 +103,7 @@
 	test_line_count = 2 out
 '
 
-for mode in strip-if-invalid sign-if-invalid
+for mode in strip-if-invalid sign-if-invalid abort-if-invalid
 do
 	test_expect_success GPG "import commit with no signature with --signed-commits=$mode" '
 		git fast-export main >output &&
@@ -135,6 +135,14 @@
 		# corresponding `data <length>` command would have to be changed too.
 		sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified &&
 
+		if test "$mode" = abort-if-invalid
+		then
+			test_must_fail git -C new fast-import --quiet \
+				--signed-commits=$mode <modified >log 2>&1 &&
+			test_grep "aborting due to invalid signature" log &&
+			return 0
+		fi &&
+
 		git -C new fast-import --quiet --signed-commits=$mode <modified >log 2>&1 &&
 
 		IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
diff --git a/t/t9306-fast-import-signed-tags.sh b/t/t9306-fast-import-signed-tags.sh
index 363619e..ec2b241 100755
--- a/t/t9306-fast-import-signed-tags.sh
+++ b/t/t9306-fast-import-signed-tags.sh
@@ -77,4 +77,122 @@
 	test_grep ! "SSH SIGNATURE" out
 '
 
+for mode in strip-if-invalid sign-if-invalid abort-if-invalid
+do
+	test_expect_success GPG "import tag with no signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		test_must_be_empty log
+	'
+
+	test_expect_success GPG "keep valid OpenPGP signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim openpgp-signed >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
+		test $OPENPGP_SIGNED = $IMPORTED &&
+		git -C import cat-file tag "$IMPORTED" >actual &&
+		test_grep -E "^-----BEGIN PGP SIGNATURE-----" actual &&
+		test_must_be_empty log
+	'
+
+	test_expect_success GPG "handle signature invalidated by message change with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim openpgp-signed >output &&
+
+		# Change the tag message, which invalidates the signature. The tag
+		# message length should not change though, otherwise the corresponding
+		# `data <length>` command would have to be changed too.
+		sed "s/OpenPGP signed tag/OpenPGP forged tag/" output >modified &&
+
+		if test "$mode" = abort-if-invalid
+		then
+			test_must_fail git -C import fast-import --quiet \
+				--signed-tags=$mode <modified >log 2>&1 &&
+			test_grep "aborting due to invalid signature" log &&
+			return 0
+		fi &&
+
+		git -C import fast-import --quiet --signed-tags=$mode <modified >log 2>&1 &&
+
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
+		test $OPENPGP_SIGNED != $IMPORTED &&
+		git -C import cat-file tag "$IMPORTED" >actual &&
+
+		if test "$mode" = strip-if-invalid
+		then
+			test_grep ! -E "^-----BEGIN PGP SIGNATURE-----" actual
+		else
+			test_grep -E "^-----BEGIN PGP SIGNATURE-----" actual &&
+			git -C import verify-tag "$IMPORTED"
+		fi &&
+
+		test_must_be_empty log
+	'
+
+	test_expect_success GPGSM "keep valid X.509 signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		git fast-export --signed-tags=verbatim x509-signed >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/x509-signed) &&
+		test $X509_SIGNED = $IMPORTED &&
+		git -C import cat-file tag x509-signed >actual &&
+		test_grep -E "^-----BEGIN SIGNED MESSAGE-----" actual &&
+		test_must_be_empty log
+	'
+
+	test_expect_success GPGSSH "keep valid SSH signature with --signed-tags=$mode" '
+		test_when_finished rm -rf import &&
+		git init import &&
+
+		test_config -C import gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+
+		git fast-export --signed-tags=verbatim ssh-signed >output &&
+		git -C import fast-import --quiet --signed-tags=$mode <output >log 2>&1 &&
+		IMPORTED=$(git -C import rev-parse --verify refs/tags/ssh-signed) &&
+		test $SSH_SIGNED = $IMPORTED &&
+		git -C import cat-file tag ssh-signed >actual &&
+		test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual &&
+		test_must_be_empty log
+	'
+done
+
+test_expect_success GPGSSH 'sign invalid tag with explicit keyid' '
+	test_when_finished rm -rf import &&
+	git init import &&
+
+	git fast-export --signed-tags=verbatim ssh-signed >output &&
+
+	# Change the tag message, which invalidates the signature. The tag
+	# message length should not change though, otherwise the corresponding
+	# `data <length>` command would have to be changed too.
+	sed "s/SSH signed tag/SSH forged tag/" output >modified &&
+
+	# Configure the target repository with an invalid default signing key.
+	test_config -C import user.signingkey "not-a-real-key-id" &&
+	test_config -C import gpg.format ssh &&
+	test_config -C import gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+	test_must_fail git -C import fast-import --quiet \
+		--signed-tags=sign-if-invalid <modified >/dev/null 2>&1 &&
+
+	# Import using explicitly provided signing key.
+	git -C import fast-import --quiet \
+		--signed-tags=sign-if-invalid="${GPGSSH_KEY_PRIMARY}" <modified &&
+
+	IMPORTED=$(git -C import rev-parse --verify refs/tags/ssh-signed) &&
+	test $SSH_SIGNED != $IMPORTED &&
+	git -C import cat-file tag "$IMPORTED" >actual &&
+	test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual &&
+	git -C import verify-tag "$IMPORTED"
+'
+
 test_done
diff --git a/walker.c b/walker.c
index 9133253..e98eb6d 100644
--- a/walker.c
+++ b/walker.c
@@ -155,7 +155,7 @@ static int process(struct walker *walker, struct object *obj)
 	obj->flags |= SEEN;
 
 	if (odb_has_object(the_repository->objects, &obj->oid,
-			   HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) {
+			   ODB_HAS_OBJECT_RECHECK_PACKED | ODB_HAS_OBJECT_FETCH_PROMISOR)) {
 		/* We already have it, so we should scan it now. */
 		obj->flags |= TO_SCAN;
 	}