diff options
115 files changed, 1892 insertions, 672 deletions
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index b8670751f5..4992e52093 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -668,6 +668,18 @@ For C programs: 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 c692dddb4a..27dbfdc6a5 100644 --- a/Documentation/RelNotes/2.54.0.adoc +++ b/Documentation/RelNotes/2.54.0.adoc @@ -121,6 +121,9 @@ UI, Workflows & Features * 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 @@ Performance, Internal Implementation, Development Support etc. 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 @@ Fixes since v2.53 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 b3f42d4637..d68bc52b7e 100644 --- a/Documentation/git-fast-import.adoc +++ b/Documentation/git-fast-import.adoc @@ -66,11 +66,10 @@ fast-import stream! This option is enabled automatically for 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 @@ already trusted to run their own code. 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 997097e420..a32f72aead 100644 --- a/Documentation/git-replay.adoc +++ b/Documentation/git-replay.adoc @@ -9,7 +9,8 @@ git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos t 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 @@ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. 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 @@ When `--onto` is specified, the branch(es) in the revision range will be 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 @@ The history is replayed on top of the <branch> and <branch> is updated to 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 @@ incompatible with `--contained` (which is a modifier for `--onto` only). 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 @@ The default mode can be configured via the `replay.refAction` configuration vari <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 @@ EXAMPLES 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 @@ The refs are updated atomically and no output is produced on success. 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 @@ What if you have a stack of branches, one depending upon another, and 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 @@ commits to replay using the syntax `A..B`; any range expression will 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 @@ that they have in common, but that does not need to be the case. 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 @@ NOTE: For reverting an entire merge request as a single commit (rather than 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 c44ba05320..ecb2c79fb9 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 92ea811ae6..9c55beb496 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 4e28e5c187..f27edcbe8d 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); @@ -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 2c5ce56fb7..d794dd842f 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 cd13a3a89f..d9fbad5358 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 13621b0d6a..2eb43a28da 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 570fd048d7..82bc6dcc00 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 4795b2a13c..a22c319467 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 9bab32effe..248f8ff5a0 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 @@ out: } 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 568dc75ee7..9526938085 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 d1e47279a8..ca7784dc2c 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 7cf6e1230a..f40264a878 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 cb3656a034..dada55884a 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 3064f888b2..e3125bc61b 100644 --- a/builtin/refs.c +++ b/builtin/refs.c @@ -78,9 +78,9 @@ out: } 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 0fddaa1773..de989ea3ba 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 a0879b020f..39e3a86f6c 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 5d31acea7c..d508441632 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 6fc64e9e4b..e01cf6e360 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 60bcc07c3b..7881b42aa2 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 df4b4a125e..9abe62bd5a 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++; } @@ -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 9c0a0e7aaf..d75038d1b3 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 a32224ed02..c8fa0a609a 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, @@ -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); @@ -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 d517425034..dafd5371fa 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 a365586ce1..3d95f5ec14 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 9ae6062198..06c3acbb5d 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 e886e64866..1b6d496548 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 eeaf68454e..858a899cd2 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()) { + struct diff_options rename_opts; + + /* + * 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 */ - clear_pathspec(&opt->pathspec); diff_queue_clear(&diff_queued_diff); - - diff_tree_oid(parent_tree_oid, tree_oid, "", opt); + 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 e9dadbc1a5..04a6ea64d3 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 91b23e22f7..724d723c48 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; @@ -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 63408fc290..2acc9522df 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 @@ cleanup: 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 3686f182e4..5241b8dd5c 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, @@ -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); @@ -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 76797569de..b5abd20e97 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 a9d7d0b96f..f706e0608a 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 7378c80730..79992bb509 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; @@ -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 48c88748b6..b012d648ad 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 6e8802e2ed..9b647da7dd 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 2aa3e7d8a4..6e426af433 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 5049f9baca..38a04b8de3 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 */ /* @@ -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; @@ -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 b124404663..daea30a5b4 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 573c81287f..7f7441f751 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 26b9115b14..8e73fc83f2 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 6d73f19c85..dc8622682d 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 0b05a8f7e3..94c79b5c58 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 f5ba867bd6..40c1e94646 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 d100e0df92..0535e1478b 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 007a392cf9..340fc7762e 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 af582028c2..a050cc153b 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 e5af846b32..02a9966835 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 385a74cc86..e18c538238 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 c2415cbc6e..5f7be573fa 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 0000000000..76f3e33e90 --- /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 5f935d02e3..d7666b53a1 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 1e7003cd69..a66db415c8 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 1c9f21dfe1..1fba96ddb3 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 @@ done: 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 725a25844e..9063641f30 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 c54ed4cad6..c0e2cbe0ff 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 @@ -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)) @@ -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; @@ -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 fda405bf65..599b3a66c3 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)) @@ -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 96749fc06d..33decc66c0 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh @@ -6,16 +6,16 @@ test_description='git-hook command and config-managed multihooks' . "$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 06e83d3333..0a96655cfe 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -200,6 +200,44 @@ test_expect_success 'add -u resolves unmerged paths' ' 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 89819ad4d2..3353bc4a4d 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -499,4 +499,70 @@ test_expect_success 'git replay --revert incompatible with --advance' ' 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 29ea7d4268..205d86d05e 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh @@ -561,6 +561,22 @@ test_expect_success 'check incomplete lines (setup)' ' 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 0a7c3ca42f..aaf197d2ed 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -129,7 +129,7 @@ test_expect_success '-L with --output' ' 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_expect_success 'zero-width regex .* matches any function name' ' 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 @@ test_expect_success 'show line-log with graph' ' * $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_expect_success 'show line-log with graph' ' 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 91b4054898..52c90afb3a 100644 --- a/t/t4211/sha1/expect.beginning-of-file +++ b/t/t4211/sha1/expect.beginning-of-file @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:47:40 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 bd25bb2f59..c40036899a 100644 --- a/t/t4211/sha1/expect.end-of-file +++ b/t/t4211/sha1/expect.end-of-file @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 c905e01bc2..ead6500d4d 100644 --- a/t/t4211/sha1/expect.move-support-f +++ b/t/t4211/sha1/expect.move-support-f @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:49:50 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 1eee8a7801..a41851a51d 100644 --- a/t/t4211/sha1/expect.multiple +++ b/t/t4211/sha1/expect.multiple @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 d930b6eec4..0ec9990eab 100644 --- a/t/t4211/sha1/expect.multiple-overlapping +++ b/t/t4211/sha1/expect.multiple-overlapping @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 d930b6eec4..0ec9990eab 100644 --- a/t/t4211/sha1/expect.multiple-superset +++ b/t/t4211/sha1/expect.multiple-superset @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 994c37db1e..54c568f273 100644 --- a/t/t4211/sha1/expect.no-assertion-error +++ b/t/t4211/sha1/expect.no-assertion-error @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:50:24 2013 +0100 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 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 052def8074..65a8cc673a 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 @@ Date: Thu Feb 28 10:49:50 2013 +0100 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 @@ Date: Fri Apr 12 16:15:57 2013 +0200 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 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 a1f5bc49c8..b24ae40e03 100644 --- a/t/t4211/sha1/expect.simple-f +++ b/t/t4211/sha1/expect.simple-f @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 a475768710..cd92100dfc 100644 --- a/t/t4211/sha1/expect.simple-f-to-main +++ b/t/t4211/sha1/expect.simple-f-to-main @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 39ce39bebe..ff31291d34 100644 --- a/t/t4211/sha1/expect.simple-main +++ b/t/t4211/sha1/expect.simple-main @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 8480bd9cc4..4bef21e657 100644 --- a/t/t4211/sha1/expect.simple-main-to-end +++ b/t/t4211/sha1/expect.simple-main-to-end @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 c5164f3be3..aed01522e3 100644 --- a/t/t4211/sha1/expect.two-ranges +++ b/t/t4211/sha1/expect.two-ranges @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 1f7cd06941..a413ad3659 100644 --- a/t/t4211/sha1/expect.vanishes-early +++ b/t/t4211/sha1/expect.vanishes-early @@ -5,11 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 5adfdfc1a1..e8d62328cf 100644 --- a/t/t4211/sha256/expect.beginning-of-file +++ b/t/t4211/sha256/expect.beginning-of-file @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:47:40 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 03ab5c1784..3b2e2384da 100644 --- a/t/t4211/sha256/expect.end-of-file +++ b/t/t4211/sha256/expect.end-of-file @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 223b4ed2a0..f49abcea3e 100644 --- a/t/t4211/sha256/expect.move-support-f +++ b/t/t4211/sha256/expect.move-support-f @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:49:50 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 dbd987b74a..0dee50ffb7 100644 --- a/t/t4211/sha256/expect.multiple +++ b/t/t4211/sha256/expect.multiple @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 9015a45a25..b8c260e8ae 100644 --- a/t/t4211/sha256/expect.multiple-overlapping +++ b/t/t4211/sha256/expect.multiple-overlapping @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 9015a45a25..b8c260e8ae 100644 --- a/t/t4211/sha256/expect.multiple-superset +++ b/t/t4211/sha256/expect.multiple-superset @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 36ed12aa9c..c25f2ce19c 100644 --- a/t/t4211/sha256/expect.no-assertion-error +++ b/t/t4211/sha256/expect.no-assertion-error @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:50:24 2013 +0100 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 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 e68f8928ea..3178989253 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 @@ Date: Thu Feb 28 10:49:50 2013 +0100 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 @@ Date: Fri Apr 12 16:15:57 2013 +0200 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 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 65508d7c0b..983c711fe3 100644 --- a/t/t4211/sha256/expect.simple-f +++ b/t/t4211/sha256/expect.simple-f @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 77b721c196..e67fa017a7 100644 --- a/t/t4211/sha256/expect.simple-f-to-main +++ b/t/t4211/sha256/expect.simple-f-to-main @@ -5,6 +5,7 @@ Date: Thu Feb 28 10:45:41 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 d20708c9f9..0792b27cad 100644 --- a/t/t4211/sha256/expect.simple-main +++ b/t/t4211/sha256/expect.simple-main @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 617cdf3481..d3bd7c7bc6 100644 --- a/t/t4211/sha256/expect.simple-main-to-end +++ b/t/t4211/sha256/expect.simple-main-to-end @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 6a94d3b9cb..7735b19723 100644 --- a/t/t4211/sha256/expect.two-ranges +++ b/t/t4211/sha256/expect.two-ranges @@ -5,9 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:45:16 2013 +0100 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 @@ Date: Thu Feb 28 10:44:55 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 11ec9bdecf..bc33b963dc 100644 --- a/t/t4211/sha256/expect.vanishes-early +++ b/t/t4211/sha256/expect.vanishes-early @@ -5,11 +5,10 @@ Date: Thu Feb 28 10:48:43 2013 +0100 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 @@ Date: Thu Feb 28 10:48:10 2013 +0100 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 @@ Date: Thu Feb 28 10:44:48 2013 +0100 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 98c6910963..1c40f904f8 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -417,6 +417,26 @@ test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow ch 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 29e2f17608..117cfa051f 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1792,6 +1792,7 @@ test_expect_success 'updateInstead with push-to-checkout hook' ' ' 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_expect_success 'denyCurrentBranch and bare repository worktrees' ' 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 2c347a91fe..f3b5e39493 100755 --- a/t/t5620-backfill.sh +++ b/t/t5620-backfill.sh @@ -175,6 +175,21 @@ test_expect_success 'backfill --sparse' ' 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 ce2ff2a28a..faf7d97fc4 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 succeed' ' ' 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 succeed' ' ' 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 the name of one existing should ' 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 18707b3f6c..5667693afd 100755 --- a/t/t9305-fast-import-signatures.sh +++ b/t/t9305-fast-import-signatures.sh @@ -103,7 +103,7 @@ test_expect_success RUST,GPG 'strip both OpenPGP signatures with --signed-commit 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 @@ do # 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 363619e7d1..ec2b241cdb 100755 --- a/t/t9306-fast-import-signed-tags.sh +++ b/t/t9306-fast-import-signed-tags.sh @@ -77,4 +77,122 @@ test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=strip' ' 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 @@ -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; } |
