diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc index c692dddb4a1062..27dbfdc6a59e13 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:" 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 b3f42d46372a40..d68bc52b7e9cd7 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=`:: Specify how to handle signed tags. Behaves in the same way as - the `--signed-commits=` below, except that the - `strip-if-invalid` mode is not yet supported. Like for signed - commits, the default mode is `verbatim`. + the `--signed-commits=` below. Like for signed commits, + the default mode is `verbatim`. `--signed-commits=`:: Specify how to handle signed commits. The following 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 `` 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/line-range-options.adoc b/Documentation/line-range-options.adoc index c44ba05320f9bf..ecb2c79fb9bde8 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/add-patch.c b/add-patch.c index 4e28e5c18786e2..f27edcbe8d4151 100644 --- a/add-patch.c +++ b/add-patch.c @@ -558,8 +558,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) strvec_push(&args, /* could be on an unborn branch */ !strcmp("HEAD", s->revision) && - repo_get_oid(the_repository, "HEAD", &oid) ? - empty_tree_oid_hex(the_repository->hash_algo) : s->revision); + repo_get_oid(s->r, "HEAD", &oid) ? + empty_tree_oid_hex(s->r->hash_algo) : s->revision); } color_arg_index = args.nr; /* Use `--no-color` explicitly, just in case `diff.color = always`. */ @@ -1271,7 +1271,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk) "removed, then the edit is\n" "aborted and the hunk is left unchanged.\n")); - if (strbuf_edit_interactively(the_repository, &s->buf, + if (strbuf_edit_interactively(s->r, &s->buf, "addp-hunk-edit.diff", NULL) < 0) return -1; @@ -1679,7 +1679,7 @@ static size_t patch_update_file(struct add_p_state *s, if (file_diff->hunk_nr) { if (rendered_hunk_index != hunk_index) { if (use_pager) { - setup_pager(the_repository); + setup_pager(s->r); sigchain_push(SIGPIPE, SIG_IGN); } render_hunk(s, hunk, 0, colored, &s->buf); diff --git a/apply.c b/apply.c index 63d5e3c4609c11..4aa1694cfaa2f0 100644 --- a/apply.c +++ b/apply.c @@ -1840,8 +1840,16 @@ static int parse_fragment(struct apply_state *state, trailing++; check_old_for_crlf(patch, line, len); if (!state->apply_in_reverse && - state->ws_error_action == correct_ws_error) - check_whitespace(state, line, len, patch->ws_rule); + state->ws_error_action == correct_ws_error) { + const char *test_line = line; + int test_len = len; + if (*line == '\n') { + test_line = " \n"; + test_len = 2; + } + check_whitespace(state, test_line, test_len, + patch->ws_rule); + } break; case '-': if (!state->apply_in_reverse) diff --git a/builtin/backfill.c b/builtin/backfill.c index 2c5ce56fb7ddcb..d794dd842f65ce 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/fast-export.c b/builtin/fast-export.c index 13621b0d6a1552..2eb43a28da748e 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= 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=")); - case SIGN_SIGN_IF_INVALID: - die(_("'sign-if-invalid' is not a valid mode for " - "git fast-export with --signed-commits=")); 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= 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=")); - case SIGN_SIGN_IF_INVALID: - die(_("'sign-if-invalid' is not a valid mode for " - "git fast-export with --signed-tags=")); default: BUG("invalid signed_commit_mode value %d", signed_commit_mode); } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 570fd048d753f2..82bc6dcc003723 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= to handle it")); - case SIGN_STRIP_IF_INVALID: - die(_("'strip-if-invalid' is not a valid mode for " - "git fast-import with --signed-tags=")); - case SIGN_SIGN_IF_INVALID: - die(_("'sign-if-invalid' is not a valid mode for " - "git fast-import with --signed-tags=")); 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/fsck.c b/builtin/fsck.c index 9bab32effed7ec..99696604b8c32f 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: */ 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: */ 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: */ 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, + if (parent && !odb_has_object(options->repo->objects, &obj->oid, HAS_OBJECT_RECHECK_PACKED)) { printf_ln(_("broken link from %7s %s\n" " to %7s %s"), - printable_type(&parent->oid, parent->type), + printable_type(options->repo, &parent->oid, parent->type), describe_object(&parent->oid), - printable_type(&obj->oid, obj->type), + printable_type(options->repo, &obj->oid, obj->type), describe_object(&obj->oid)); errors_found |= ERROR_REACHABLE; } @@ -181,7 +180,7 @@ static int mark_object(struct object *obj, enum object_type type, static void mark_object_reachable(struct object *obj) { - mark_object(obj, OBJ_ANY, NULL, NULL); + mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options); } static int traverse_one_object(struct object *obj) @@ -195,13 +194,13 @@ static int traverse_one_object(struct object *obj) return result; } -static int traverse_reachable(void) +static int traverse_reachable(struct repository *repo) { struct progress *progress = NULL; unsigned int nr = 0; int result = 0; if (show_progress) - progress = start_delayed_progress(the_repository, + progress = start_delayed_progress(repo, _("Checking connectivity"), 0); while (pending.nr) { result |= traverse_one_object(object_array_pop(&pending)); @@ -222,10 +221,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED, static int mark_unreachable_referents(const struct object_id *oid, struct object_info *oi UNUSED, - void *data UNUSED) + void *data) { - struct fsck_options options = FSCK_OPTIONS_DEFAULT; - struct object *obj = lookup_object(the_repository, oid); + struct repository *repo = data; + struct fsck_options options; + struct object *obj = lookup_object(data, oid); if (!obj || !(obj->flags & HAS_OBJ)) return 0; /* not part of our original set */ @@ -237,12 +237,13 @@ static int mark_unreachable_referents(const struct object_id *oid, * (and we want to avoid parsing blobs). */ if (obj->type == OBJ_NONE) { - enum object_type type = odb_read_object_info(the_repository->objects, + enum object_type type = odb_read_object_info(repo->objects, &obj->oid, NULL); if (type > 0) object_as_type(obj, type, 0); } + fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT); options.walk = mark_used; fsck_walk(obj, NULL, &options); if (obj->type == OBJ_TREE) @@ -254,7 +255,7 @@ static int mark_unreachable_referents(const struct object_id *oid, /* * Check a single reachable object */ -static void check_reachable_object(struct object *obj) +static void check_reachable_object(struct repository *repo, struct object *obj) { /* * We obviously want the object to be parsed, @@ -262,12 +263,12 @@ static void check_reachable_object(struct object *obj) * do a full fsck */ if (!(obj->flags & HAS_OBJ)) { - if (is_promisor_object(the_repository, &obj->oid)) + if (is_promisor_object(repo, &obj->oid)) return; - if (has_object_pack(the_repository, &obj->oid)) + if (has_object_pack(repo, &obj->oid)) return; /* it is in pack - forget about it */ printf_ln(_("missing %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); errors_found |= ERROR_REACHABLE; return; @@ -277,7 +278,7 @@ static void check_reachable_object(struct object *obj) /* * Check a single unreachable object */ -static void check_unreachable_object(struct object *obj) +static void check_unreachable_object(struct repository *repo, struct object *obj) { /* * Missing unreachable object? Ignore it. It's not like @@ -294,7 +295,7 @@ static void check_unreachable_object(struct object *obj) */ if (show_unreachable) { printf_ln(_("unreachable %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); return; } @@ -314,22 +315,22 @@ static void check_unreachable_object(struct object *obj) if (!(obj->flags & USED)) { if (show_dangling) printf_ln(_("dangling %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); if (write_lost_and_found) { - char *filename = repo_git_path(the_repository, "lost-found/%s/%s", + char *filename = repo_git_path(repo, "lost-found/%s/%s", obj->type == OBJ_COMMIT ? "commit" : "other", describe_object(&obj->oid)); FILE *f; - if (safe_create_leading_directories_const(the_repository, filename)) { + if (safe_create_leading_directories_const(repo, filename)) { error(_("could not create lost-found")); free(filename); return; } f = xfopen(filename, "w"); if (obj->type == OBJ_BLOB) { - if (odb_stream_blob_to_fd(the_repository->objects, fileno(f), + if (odb_stream_blob_to_fd(repo->objects, fileno(f), &obj->oid, NULL, 1)) die_errno(_("could not write '%s'"), filename); } else @@ -349,23 +350,23 @@ static void check_unreachable_object(struct object *obj) */ } -static void check_object(struct object *obj) +static void check_object(struct repository *repo, struct object *obj) { if (verbose) fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid)); if (obj->flags & REACHABLE) - check_reachable_object(obj); + check_reachable_object(repo, obj); else - check_unreachable_object(obj); + check_unreachable_object(repo, obj); } -static void check_connectivity(void) +static void check_connectivity(struct repository *repo) { int i, max; /* Traverse the pending reachable objects */ - traverse_reachable(); + traverse_reachable(repo); /* * With --connectivity-only, we won't have actually opened and marked @@ -383,24 +384,25 @@ static void check_connectivity(void) * and ignore any that weren't present in our earlier * traversal. */ - odb_for_each_object(the_repository->objects, NULL, - mark_unreachable_referents, NULL, 0); + odb_for_each_object(repo->objects, NULL, + mark_unreachable_referents, repo, 0); } /* Look up all the requirements, warn about missing objects.. */ - max = get_max_object_index(the_repository); + max = get_max_object_index(repo); if (verbose) fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max); for (i = 0; i < max; i++) { - struct object *obj = get_indexed_object(the_repository, i); + struct object *obj = get_indexed_object(repo, i); if (obj) - check_object(obj); + check_object(repo, obj); } } -static int fsck_obj(struct object *obj, void *buffer, unsigned long size) +static int fsck_obj(struct repository *repo, + struct object *obj, void *buffer, unsigned long size) { int err; @@ -410,11 +412,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (verbose) fprintf_ln(stderr, _("Checking %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); if (fsck_walk(obj, NULL, &fsck_obj_options)) - objerror(obj, _("broken links")); + objerror(repo, obj, _("broken links")); err = fsck_object(obj, buffer, size, &fsck_obj_options); if (err) goto out; @@ -432,7 +434,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (show_tags && tag->tagged) { printf_ln(_("tagged %s %s (%s) in %s"), - printable_type(&tag->tagged->oid, tag->tagged->type), + printable_type(repo, &tag->tagged->oid, tag->tagged->type), describe_object(&tag->tagged->oid), tag->tag, describe_object(&tag->object.oid)); @@ -446,15 +448,16 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) } static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, - unsigned long size, void *buffer, int *eaten) + unsigned long size, void *buffer, int *eaten, void *cb_data) { + struct repository *repo = cb_data; + struct object *obj; + /* * Note, buffer may be NULL if type is OBJ_BLOB. See * verify_packfile(), data_valid variable for details. */ - struct object *obj; - obj = parse_object_buffer(the_repository, oid, type, size, buffer, - eaten); + obj = parse_object_buffer(repo, oid, type, size, buffer, eaten); if (!obj) { errors_found |= ERROR_OBJECT; return error(_("%s: object corrupt or missing"), @@ -462,18 +465,19 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - return fsck_obj(obj, buffer, size); + return fsck_obj(repo, obj, buffer, size); } static int default_refs; -static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, - timestamp_t timestamp) +static void fsck_handle_reflog_oid(struct repository *repo, + const char *refname, struct object_id *oid, + timestamp_t timestamp) { struct object *obj; if (!is_null_oid(oid)) { - obj = lookup_object(the_repository, oid); + obj = lookup_object(repo, oid); if (obj && (obj->flags & HAS_OBJ)) { if (timestamp) fsck_put_object_name(&fsck_walk_options, oid, @@ -481,7 +485,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, refname, timestamp); obj->flags |= USED; mark_object_reachable(obj); - } else if (!is_promisor_object(the_repository, oid)) { + } else if (!is_promisor_object(repo, oid)) { error(_("%s: invalid reflog entry %s"), refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; @@ -493,8 +497,10 @@ static int fsck_handle_reflog_ent(const char *refname, struct object_id *ooid, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp, int tz UNUSED, - const char *message UNUSED, void *cb_data UNUSED) + const char *message UNUSED, void *cb_data) { + struct repository *repo = cb_data; + if (now && timestamp > now) return 0; @@ -502,19 +508,20 @@ static int fsck_handle_reflog_ent(const char *refname, fprintf_ln(stderr, _("Checking reflog %s->%s"), oid_to_hex(ooid), oid_to_hex(noid)); - fsck_handle_reflog_oid(refname, ooid, 0); - fsck_handle_reflog_oid(refname, noid, timestamp); + fsck_handle_reflog_oid(repo, refname, ooid, 0); + fsck_handle_reflog_oid(repo, refname, noid, timestamp); return 0; } static int fsck_handle_reflog(const char *logname, void *cb_data) { struct strbuf refname = STRBUF_INIT; + struct worktree *wt = cb_data; - strbuf_worktree_ref(cb_data, &refname, logname); - refs_for_each_reflog_ent(get_main_ref_store(the_repository), + strbuf_worktree_ref(wt, &refname, logname); + refs_for_each_reflog_ent(get_main_ref_store(wt->repo), refname.buf, fsck_handle_reflog_ent, - NULL); + wt->repo); strbuf_release(&refname); return 0; } @@ -532,14 +539,20 @@ struct snapshot { /* TODO: Consider also snapshotting the index of each worktree. */ }; +struct snapshot_ref_data { + struct repository *repo; + struct snapshot *snap; +}; + static int snapshot_ref(const struct reference *ref, void *cb_data) { - struct snapshot *snap = cb_data; + struct snapshot_ref_data *data = cb_data; + struct snapshot *snap = data->snap; struct object *obj; - obj = parse_object(the_repository, ref->oid); + obj = parse_object(data->repo, ref->oid); if (!obj) { - if (is_promisor_object(the_repository, ref->oid)) { + if (is_promisor_object(data->repo, ref->oid)) { /* * Increment default_refs anyway, because this is a * valid ref. @@ -567,11 +580,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data) return 0; } -static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) +static int fsck_handle_ref(const struct reference *ref, void *cb_data) { + struct repository *repo = cb_data; struct object *obj; - obj = parse_object(the_repository, ref->oid); + obj = parse_object(repo, ref->oid); obj->flags |= USED; fsck_put_object_name(&fsck_walk_options, ref->oid, "%s", ref->name); @@ -580,11 +594,16 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) return 0; } -static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) +static void snapshot_refs(struct repository *repo, + struct snapshot *snap, int argc, const char **argv) { struct refs_for_each_ref_options opts = { .flags = REFS_FOR_EACH_INCLUDE_BROKEN, }; + struct snapshot_ref_data data = { + .repo = repo, + .snap = snap, + }; struct worktree **worktrees, **p; const char *head_points_at; struct object_id head_oid; @@ -592,13 +611,13 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) for (int i = 0; i < argc; i++) { const char *arg = argv[i]; struct object_id oid; - if (!repo_get_oid(the_repository, arg, &oid)) { + if (!repo_get_oid(repo, arg, &oid)) { struct reference ref = { .name = arg, .oid = &oid, }; - snapshot_ref(&ref, snap); + snapshot_ref(&ref, &data); continue; } error(_("invalid parameter: expected sha1, got '%s'"), arg); @@ -610,8 +629,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) return; } - refs_for_each_ref_ext(get_main_ref_store(the_repository), - snapshot_ref, snap, &opts); + refs_for_each_ref_ext(get_main_ref_store(repo), + snapshot_ref, &data, &opts); worktrees = get_worktrees(); for (p = worktrees; *p; p++) { @@ -620,7 +639,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) strbuf_worktree_ref(wt, &refname, "HEAD"); - head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo), refname.buf, 0, &head_oid, NULL); if (head_points_at && !is_null_oid(&head_oid)) { @@ -629,7 +648,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) .oid = &head_oid, }; - snapshot_ref(&ref, snap); + snapshot_ref(&ref, &data); } strbuf_release(&refname); @@ -653,7 +672,7 @@ static void free_snapshot_refs(struct snapshot *snap) free(snap->ref); } -static void process_refs(struct snapshot *snap) +static void process_refs(struct repository *repo, struct snapshot *snap) { struct worktree **worktrees, **p; @@ -662,7 +681,7 @@ static void process_refs(struct snapshot *snap) .name = snap->ref[i].refname, .oid = &snap->ref[i].oid, }; - fsck_handle_ref(&ref, NULL); + fsck_handle_ref(&ref, repo); } if (include_reflogs) { @@ -694,27 +713,28 @@ static void process_refs(struct snapshot *snap) } } -struct for_each_loose_cb -{ +struct for_each_loose_cb { + struct repository *repo; struct progress *progress; }; static int fsck_loose(const struct object_id *oid, const char *path, - void *data UNUSED) + void *cb_data) { + struct for_each_loose_cb *data = cb_data; struct object *obj; enum object_type type = OBJ_NONE; unsigned long size; void *contents = NULL; int eaten; struct object_info oi = OBJECT_INFO_INIT; - struct object_id real_oid = *null_oid(the_hash_algo); + struct object_id real_oid = *null_oid(data->repo->hash_algo); int err = 0; oi.sizep = &size; oi.typep = &type; - if (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) { + if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) { if (contents && !oideq(&real_oid, oid)) err = error(_("%s: hash-path mismatch, found at: %s"), oid_to_hex(&real_oid), path); @@ -731,7 +751,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, if (!contents && type != OBJ_BLOB) BUG("read_loose_object streamed a non-blob"); - obj = parse_object_buffer(the_repository, oid, type, size, + obj = parse_object_buffer(data->repo, oid, type, size, contents, &eaten); if (!obj) { @@ -745,7 +765,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - if (fsck_obj(obj, contents, size)) + if (fsck_obj(data->repo, obj, contents, size)) errors_found |= ERROR_OBJECT; if (!eaten) @@ -769,10 +789,11 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) return 0; } -static void fsck_source(struct odb_source *source) +static void fsck_source(struct repository *repo, struct odb_source *source) { struct progress *progress = NULL; struct for_each_loose_cb cb_data = { + .repo = source->odb->repo, .progress = progress, }; @@ -780,7 +801,7 @@ static void fsck_source(struct odb_source *source) fprintf_ln(stderr, _("Checking object directory")); if (show_progress) - progress = start_progress(the_repository, + progress = start_progress(repo, _("Checking object directories"), 256); for_each_loose_file_in_source(source, fsck_loose, @@ -789,7 +810,7 @@ static void fsck_source(struct odb_source *source) stop_progress(&progress); } -static int fsck_cache_tree(struct cache_tree *it, const char *index_path) +static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path) { int i; int err = 0; @@ -798,7 +819,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path) fprintf_ln(stderr, _("Checking cache tree of %s"), index_path); if (0 <= it->entry_count) { - struct object *obj = parse_object(the_repository, &it->oid); + struct object *obj = parse_object(repo, &it->oid); if (!obj) { error(_("%s: invalid sha1 pointer in cache-tree of %s"), oid_to_hex(&it->oid), index_path); @@ -809,10 +830,10 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path) fsck_put_object_name(&fsck_walk_options, &it->oid, ":"); mark_object_reachable(obj); if (obj->type != OBJ_TREE) - err |= objerror(obj, _("non-tree in cache-tree")); + err |= objerror(repo, obj, _("non-tree in cache-tree")); } for (i = 0; i < it->subtree_nr; i++) - err |= fsck_cache_tree(it->down[i]->cache_tree, index_path); + err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path); return err; } @@ -838,7 +859,7 @@ static int fsck_resolve_undo(struct index_state *istate, if (!ru->mode[i] || !S_ISREG(ru->mode[i])) continue; - obj = parse_object(the_repository, &ru->oid[i]); + obj = parse_object(istate->repo, &ru->oid[i]); if (!obj) { error(_("%s: invalid sha1 pointer in resolve-undo of %s"), oid_to_hex(&ru->oid[i]), @@ -870,7 +891,7 @@ static void fsck_index(struct index_state *istate, const char *index_path, mode = istate->cache[i]->ce_mode; if (S_ISGITLINK(mode)) continue; - blob = lookup_blob(the_repository, + blob = lookup_blob(istate->repo, &istate->cache[i]->oid); if (!blob) continue; @@ -883,15 +904,16 @@ static void fsck_index(struct index_state *istate, const char *index_path, mark_object_reachable(obj); } if (istate->cache_tree) - fsck_cache_tree(istate->cache_tree, index_path); + fsck_cache_tree(istate->repo, istate->cache_tree, index_path); fsck_resolve_undo(istate, index_path); } static int mark_object_for_connectivity(const struct object_id *oid, struct object_info *oi UNUSED, - void *cb_data UNUSED) + void *cb_data) { - struct object *obj = lookup_unknown_object(the_repository, oid); + struct repository *repo = cb_data; + struct object *obj = lookup_unknown_object(repo, oid); obj->flags |= HAS_OBJ; return 0; } @@ -906,7 +928,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress) if (show_progress) { repo_for_each_pack(r, p) pack_count++; - progress = start_delayed_progress(the_repository, + progress = start_delayed_progress(r, "Verifying reverse pack-indexes", pack_count); pack_count = 0; } @@ -986,7 +1008,7 @@ static struct option fsck_opts[] = { int cmd_fsck(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { struct odb_source *source; struct snapshot snap = { @@ -1004,7 +1026,10 @@ int cmd_fsck(int argc, argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); + fsck_options_init(&fsck_walk_options, repo, FSCK_OPTIONS_DEFAULT); fsck_walk_options.walk = mark_object; + + fsck_options_init(&fsck_obj_options, repo, FSCK_OPTIONS_DEFAULT); fsck_obj_options.walk = mark_used; fsck_obj_options.error_func = fsck_objects_error_func; if (check_strict) @@ -1023,11 +1048,11 @@ int cmd_fsck(int argc, if (name_objects) fsck_enable_object_names(&fsck_walk_options); - repo_config(the_repository, git_fsck_config, &fsck_obj_options); - prepare_repo_settings(the_repository); + repo_config(repo, git_fsck_config, &fsck_obj_options); + prepare_repo_settings(repo); if (check_references) - fsck_refs(the_repository); + fsck_refs(repo); /* * Take a snapshot of the refs before walking objects to avoid looking @@ -1035,18 +1060,18 @@ int cmd_fsck(int argc, * objects. We can still walk over new objects that are added during the * execution of fsck but won't miss any objects that were reachable. */ - snapshot_refs(&snap, argc, argv); + snapshot_refs(repo, &snap, argc, argv); /* Ensure we get a "fresh" view of the odb */ - odb_reprepare(the_repository->objects); + odb_reprepare(repo->objects); if (connectivity_only) { - odb_for_each_object(the_repository->objects, NULL, - mark_object_for_connectivity, NULL, 0); + odb_for_each_object(repo->objects, NULL, + mark_object_for_connectivity, repo, 0); } else { - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) - fsck_source(source); + odb_prepare_alternates(repo->objects); + for (source = repo->objects->sources; source; source = source->next) + fsck_source(repo, source); if (check_full) { struct packed_git *p; @@ -1054,20 +1079,20 @@ int cmd_fsck(int argc, struct progress *progress = NULL; if (show_progress) { - repo_for_each_pack(the_repository, p) { + repo_for_each_pack(repo, p) { if (open_pack_index(p)) continue; total += p->num_objects; } - progress = start_progress(the_repository, + progress = start_progress(repo, _("Checking objects"), total); } - repo_for_each_pack(the_repository, p) { + repo_for_each_pack(repo, p) { /* verify gives error messages itself */ - if (verify_pack(the_repository, - p, fsck_obj_buffer, + if (verify_pack(repo, + p, fsck_obj_buffer, repo, progress, count)) errors_found |= ERROR_PACK; count += p->num_objects; @@ -1080,7 +1105,7 @@ int cmd_fsck(int argc, } /* Process the snapshotted refs and the reflogs. */ - process_refs(&snap); + process_refs(repo, &snap); /* If not given any explicit objects, process index files too. */ if (!argc) @@ -1100,7 +1125,7 @@ int cmd_fsck(int argc, for (p = worktrees; *p; p++) { struct worktree *wt = *p; struct index_state istate = - INDEX_STATE_INIT(the_repository); + INDEX_STATE_INIT(repo); char *path, *wt_gitdir; /* @@ -1121,17 +1146,17 @@ int cmd_fsck(int argc, free_worktrees(worktrees); } - errors_found |= check_pack_rev_indexes(the_repository, show_progress); - if (verify_bitmap_files(the_repository)) + errors_found |= check_pack_rev_indexes(repo, show_progress); + if (verify_bitmap_files(repo)) errors_found |= ERROR_BITMAP; - check_connectivity(); + check_connectivity(repo); - if (the_repository->settings.core_commit_graph) { + if (repo->settings.core_commit_graph) { struct child_process commit_graph_verify = CHILD_PROCESS_INIT; - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) { + odb_prepare_alternates(repo->objects); + for (source = repo->objects->sources; source; source = source->next) { child_process_init(&commit_graph_verify); commit_graph_verify.git_cmd = 1; strvec_pushl(&commit_graph_verify.args, "commit-graph", @@ -1145,11 +1170,11 @@ int cmd_fsck(int argc, } } - if (the_repository->settings.core_multi_pack_index) { + if (repo->settings.core_multi_pack_index) { struct child_process midx_verify = CHILD_PROCESS_INIT; - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) { + odb_prepare_alternates(repo->objects); + for (source = repo->objects->sources; source; source = source->next) { child_process_init(&midx_verify); midx_verify.git_cmd = 1; strvec_pushl(&midx_verify.args, "multi-pack-index", diff --git a/builtin/history.c b/builtin/history.c index 568dc75ee7b1a2..952693808574b7 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_(""), - 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_(""), - 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 d1e47279a8c7c9..e4129bd605e89c 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; @@ -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 7cf6e1230ac450..f40264a87876f4 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 cb3656a034d335..49090c4d4c7516 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); diff --git a/builtin/refs.c b/builtin/refs.c index 3064f888b24304..e3125bc61b20e0 100644 --- a/builtin/refs.c +++ b/builtin/refs.c @@ -78,9 +78,9 @@ static int cmd_refs_migrate(int argc, const char **argv, const char *prefix, } static int cmd_refs_verify(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { - struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT; + struct fsck_options fsck_refs_options; struct worktree **worktrees; const char * const verify_usage[] = { REFS_VERIFY_USAGE, @@ -93,6 +93,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix, }; int ret = 0; + fsck_options_init(&fsck_refs_options, repo, FSCK_OPTIONS_REFS); + argc = parse_options(argc, argv, prefix, options, verify_usage, 0); if (argc) usage(_("'git refs verify' takes no arguments")); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 6fc64e9e4b8d5a..d863912b24fae5 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; /* @@ -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/commit-graph.c b/commit-graph.c index df4b4a125e977b..9abe62bd5a278a 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f, return 0; } +/* + * Compute the generation offset between the commit date and its generation. + * This is what's ultimately stored as generation number in the commit graph. + * + * Note that the computation of the commit date is more involved than you might + * think. Instead of using the full commit date, we're in fact masking bits so + * that only the 34 lowest bits are considered. This results from the fact that + * commit graphs themselves only ever store 34 bits of the commit date + * themselves. + * + * This means that if we have a commit date that exceeds 34 bits we'll end up + * in situations where depending on whether the commit has been parsed from the + * object database or the commit graph we'll have different dates, where the + * ones parsed from the object database would have full 64 bit precision. + * + * But ultimately, we only ever want the offset to be relative to what we + * actually end up storing on disk, and hence we have to mask all the other + * bits. + */ +static timestamp_t compute_generation_offset(struct commit *c) +{ + timestamp_t masked_date; + + if (sizeof(timestamp_t) > 4) + masked_date = c->date & (((timestamp_t) 1 << 34) - 1); + else + masked_date = c->date; + + return commit_graph_data_at(c)->generation - masked_date; +} + static int write_graph_chunk_generation_data(struct hashfile *f, void *data) { @@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f, struct commit *c = ctx->commits.items[i]; timestamp_t offset; repo_parse_commit(ctx->r, c); - offset = commit_graph_data_at(c)->generation - c->date; + offset = compute_generation_offset(c); display_progress(ctx->progress, ++ctx->progress_cnt); if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) { @@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f, int i; for (i = 0; i < ctx->commits.nr; i++) { struct commit *c = ctx->commits.items[i]; - timestamp_t offset = commit_graph_data_at(c)->generation - c->date; + timestamp_t offset = compute_generation_offset(c); display_progress(ctx->progress, ++ctx->progress_cnt); if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) { @@ -1741,7 +1772,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx) for (i = 0; i < ctx->commits.nr; i++) { struct commit *c = ctx->commits.items[i]; - timestamp_t offset = commit_graph_data_at(c)->generation - c->date; + timestamp_t offset = compute_generation_offset(c); if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) ctx->num_generation_data_overflows++; } diff --git a/diff.c b/diff.c index e87847fa4bdfe8..397e38b41cc6fa 100644 --- a/diff.c +++ b/diff.c @@ -608,6 +608,52 @@ struct emit_callback { struct strbuf *header; }; +/* + * State for the line-range callback wrappers that sit between + * xdi_diff_outf() and fn_out_consume(). xdiff produces a normal, + * unfiltered diff; the wrappers intercept each hunk header and line, + * track post-image position, and forward only lines that fall within + * the requested ranges. Contiguous in-range lines are collected into + * range hunks and flushed with a synthetic @@ header so that + * fn_out_consume() sees well-formed unified-diff fragments. + * + * Removal lines ('-') cannot be classified by post-image position, so + * they are buffered in pending_rm until the next '+' or ' ' line + * reveals whether they precede an in-range line (flush into range hunk) or + * an out-of-range line (discard). + */ +struct line_range_callback { + xdiff_emit_line_fn orig_line_fn; + void *orig_cb_data; + const struct range_set *ranges; /* 0-based [start, end) */ + unsigned int cur_range; /* index into the range_set */ + + /* Post/pre-image line counters (1-based, set from hunk headers) */ + long lno_post; + long lno_pre; + + /* + * Function name from most recent xdiff hunk header; + * size matches struct func_line.buf in xdiff/xemit.c. + */ + char func[80]; + long funclen; + + /* Range hunk being accumulated for the current range */ + struct strbuf rhunk; + long rhunk_old_begin, rhunk_old_count; + long rhunk_new_begin, rhunk_new_count; + int rhunk_active; + int rhunk_has_changes; /* any '+' or '-' lines? */ + + /* Removal lines not yet known to be in-range */ + struct strbuf pending_rm; + int pending_rm_count; + long pending_rm_pre_begin; /* pre-image line of first pending */ + + int ret; /* latched error from orig_line_fn */ +}; + static int count_lines(const char *data, int size) { int count, ch, completely_empty = 1, nl_just_seen = 0; @@ -2493,6 +2539,188 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED return 1; } +static void discard_pending_rm(struct line_range_callback *s) +{ + strbuf_reset(&s->pending_rm); + s->pending_rm_count = 0; +} + +static void flush_rhunk(struct line_range_callback *s) +{ + struct strbuf hdr = STRBUF_INIT; + const char *p, *end; + + if (!s->rhunk_active || s->ret) + return; + + /* Drain any pending removal lines into the range hunk */ + if (s->pending_rm_count) { + strbuf_addbuf(&s->rhunk, &s->pending_rm); + s->rhunk_old_count += s->pending_rm_count; + s->rhunk_has_changes = 1; + discard_pending_rm(s); + } + + /* + * Suppress context-only hunks: they contain no actual changes + * and would just be noise. This can happen when the inflated + * ctxlen causes xdiff to emit context covering a range that + * has no changes in this commit. + */ + if (!s->rhunk_has_changes) { + s->rhunk_active = 0; + strbuf_reset(&s->rhunk); + return; + } + + strbuf_addf(&hdr, "@@ -%ld,%ld +%ld,%ld @@", + s->rhunk_old_begin, s->rhunk_old_count, + s->rhunk_new_begin, s->rhunk_new_count); + if (s->funclen > 0) { + strbuf_addch(&hdr, ' '); + strbuf_add(&hdr, s->func, s->funclen); + } + strbuf_addch(&hdr, '\n'); + + s->ret = s->orig_line_fn(s->orig_cb_data, hdr.buf, hdr.len); + strbuf_release(&hdr); + + /* + * Replay buffered lines one at a time through fn_out_consume. + * The cast discards const because xdiff_emit_line_fn takes + * char *, though fn_out_consume does not modify the buffer. + */ + p = s->rhunk.buf; + end = p + s->rhunk.len; + while (!s->ret && p < end) { + const char *eol = memchr(p, '\n', end - p); + unsigned long line_len = eol ? (unsigned long)(eol - p + 1) + : (unsigned long)(end - p); + s->ret = s->orig_line_fn(s->orig_cb_data, (char *)p, line_len); + p += line_len; + } + + s->rhunk_active = 0; + strbuf_reset(&s->rhunk); +} + +static void line_range_hunk_fn(void *data, + long old_begin, long old_nr UNUSED, + long new_begin, long new_nr UNUSED, + const char *func, long funclen) +{ + struct line_range_callback *s = data; + + /* + * When count > 0, begin is 1-based. When count == 0, begin is + * adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of + * that type will arrive, so the value is unused. + * + * Any pending removal lines from the previous xdiff hunk are + * intentionally left in pending_rm: the line callback will + * flush or discard them when the next content line reveals + * whether the removals precede in-range content. + */ + s->lno_post = new_begin; + s->lno_pre = old_begin; + + if (funclen > 0) { + if (funclen > (long)sizeof(s->func)) + funclen = sizeof(s->func); + memcpy(s->func, func, funclen); + } + s->funclen = funclen; +} + +static int line_range_line_fn(void *priv, char *line, unsigned long len) +{ + struct line_range_callback *s = priv; + const struct range *cur; + long lno_0, cur_pre; + + if (s->ret) + return s->ret; + + if (line[0] == '-') { + if (!s->pending_rm_count) + s->pending_rm_pre_begin = s->lno_pre; + s->lno_pre++; + strbuf_add(&s->pending_rm, line, len); + s->pending_rm_count++; + return s->ret; + } + + if (line[0] == '\\') { + if (s->pending_rm_count) + strbuf_add(&s->pending_rm, line, len); + else if (s->rhunk_active) + strbuf_add(&s->rhunk, line, len); + /* otherwise outside tracked range; drop silently */ + return s->ret; + } + + if (line[0] != '+' && line[0] != ' ') + BUG("unexpected diff line type '%c'", line[0]); + + lno_0 = s->lno_post - 1; + cur_pre = s->lno_pre; /* save before advancing for context lines */ + s->lno_post++; + if (line[0] == ' ') + s->lno_pre++; + + /* Advance past ranges we've passed */ + while (s->cur_range < s->ranges->nr && + lno_0 >= s->ranges->ranges[s->cur_range].end) { + if (s->rhunk_active) + flush_rhunk(s); + discard_pending_rm(s); + s->cur_range++; + } + + /* Past all ranges */ + if (s->cur_range >= s->ranges->nr) { + discard_pending_rm(s); + return s->ret; + } + + cur = &s->ranges->ranges[s->cur_range]; + + /* Before current range */ + if (lno_0 < cur->start) { + discard_pending_rm(s); + return s->ret; + } + + /* In range so start a new range hunk if needed */ + if (!s->rhunk_active) { + s->rhunk_active = 1; + s->rhunk_has_changes = 0; + s->rhunk_new_begin = lno_0 + 1; + s->rhunk_old_begin = s->pending_rm_count + ? s->pending_rm_pre_begin : cur_pre; + s->rhunk_old_count = 0; + s->rhunk_new_count = 0; + strbuf_reset(&s->rhunk); + } + + /* Flush pending removals into range hunk */ + if (s->pending_rm_count) { + strbuf_addbuf(&s->rhunk, &s->pending_rm); + s->rhunk_old_count += s->pending_rm_count; + s->rhunk_has_changes = 1; + discard_pending_rm(s); + } + + strbuf_add(&s->rhunk, line, len); + s->rhunk_new_count++; + if (line[0] == '+') + s->rhunk_has_changes = 1; + else + s->rhunk_old_count++; + + return s->ret; +} + static void pprint_rename(struct strbuf *name, const char *a, const char *b) { const char *old_name = a; @@ -3592,7 +3820,8 @@ static void builtin_diff(const char *name_a, const char *xfrm_msg, int must_show_header, struct diff_options *o, - int complete_rewrite) + int complete_rewrite, + const struct range_set *line_ranges) { mmfile_t mf1, mf2; const char *lbl[2]; @@ -3833,6 +4062,52 @@ static void builtin_diff(const char *name_a, */ xdi_diff_outf(&mf1, &mf2, NULL, quick_consume, &ecbdata, &xpp, &xecfg); + } else if (line_ranges) { + struct line_range_callback lr_state; + unsigned int i; + long max_span = 0; + + memset(&lr_state, 0, sizeof(lr_state)); + lr_state.orig_line_fn = fn_out_consume; + lr_state.orig_cb_data = &ecbdata; + lr_state.ranges = line_ranges; + strbuf_init(&lr_state.rhunk, 0); + strbuf_init(&lr_state.pending_rm, 0); + + /* + * Inflate ctxlen so that all changes within + * any single range are merged into one xdiff + * hunk and the inter-change context is emitted. + * The callback clips back to range boundaries. + * + * The optimal ctxlen depends on where changes + * fall within the range, which is only known + * after xdiff runs; the max range span is the + * upper bound that guarantees correctness in a + * single pass. + */ + for (i = 0; i < line_ranges->nr; i++) { + long span = line_ranges->ranges[i].end - + line_ranges->ranges[i].start; + if (span > max_span) + max_span = span; + } + if (max_span > xecfg.ctxlen) + xecfg.ctxlen = max_span; + + if (xdi_diff_outf(&mf1, &mf2, + line_range_hunk_fn, + line_range_line_fn, + &lr_state, &xpp, &xecfg)) + die("unable to generate diff for %s", + one->path); + + flush_rhunk(&lr_state); + if (lr_state.ret) + die("unable to generate diff for %s", + one->path); + strbuf_release(&lr_state.rhunk); + strbuf_release(&lr_state.pending_rm); } else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume, &ecbdata, &xpp, &xecfg)) die("unable to generate diff for %s", one->path); @@ -4674,7 +4949,7 @@ static void run_diff_cmd(const struct external_diff *pgm, builtin_diff(name, other ? other : name, one, two, xfrm_msg, must_show_header, - o, complete_rewrite); + o, complete_rewrite, p->line_ranges); if (p->status == DIFF_STATUS_COPIED || p->status == DIFF_STATUS_RENAMED) o->found_changes = 1; diff --git a/diffcore.h b/diffcore.h index 9c0a0e7aaff85a..d75038d1b3a1b2 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 a32224ed026592..7b580920a39a3b 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; @@ -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; } diff --git a/fsck.c b/fsck.c index 0f02cf8f77bf1a..b72200c352d663 100644 --- a/fsck.c +++ b/fsck.c @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "date.h" #include "dir.h" @@ -207,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values) if (equal == len) die("skiplist requires a path"); oidset_parse_file(&options->skip_oids, buf + equal + 1, - the_repository->hash_algo); + options->repo->hash_algo); buf += len + 1; continue; } @@ -360,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op int res = 0; const char *name; - if (repo_parse_tree(the_repository, tree)) + if (repo_parse_tree(options->repo, tree)) return -1; name = fsck_get_object_name(options, &tree->object.oid); @@ -375,14 +373,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op continue; if (S_ISDIR(entry.mode)) { - obj = (struct object *)lookup_tree(the_repository, &entry.oid); + obj = (struct object *)lookup_tree(options->repo, &entry.oid); if (name && obj) fsck_put_object_name(options, &entry.oid, "%s%s/", name, entry.path); result = options->walk(obj, OBJ_TREE, data, options); } else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) { - obj = (struct object *)lookup_blob(the_repository, &entry.oid); + obj = (struct object *)lookup_blob(options->repo, &entry.oid); if (name && obj) fsck_put_object_name(options, &entry.oid, "%s%s", name, entry.path); @@ -409,7 +407,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio int result; const char *name; - if (repo_parse_commit(the_repository, commit)) + if (repo_parse_commit(options->repo, commit)) return -1; name = fsck_get_object_name(options, &commit->object.oid); @@ -417,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio fsck_put_object_name(options, get_commit_tree_oid(commit), "%s:", name); - result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit), + result = options->walk((struct object *) repo_get_commit_tree(options->repo, commit), OBJ_TREE, data, options); if (result < 0) return result; @@ -474,7 +472,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio { const char *name = fsck_get_object_name(options, &tag->object.oid); - if (parse_tag(the_repository, tag)) + if (parse_tag(options->repo, tag)) return -1; if (name) fsck_put_object_name(options, &tag->tagged->oid, "%s", name); @@ -487,7 +485,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options) return -1; if (obj->type == OBJ_NONE) - parse_object(the_repository, &obj->oid); + parse_object(options->repo, &obj->oid); switch (obj->type) { case OBJ_BLOB: @@ -970,14 +968,14 @@ static int fsck_commit(const struct object_id *oid, if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer)) return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); - if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') { + if (parse_oid_hex_algop(buffer, &tree_oid, &p, options->repo->hash_algo) || *p != '\n') { err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); if (err) return err; } buffer = p + 1; while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) { - if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') { + if (parse_oid_hex_algop(buffer, &parent_oid, &p, options->repo->hash_algo) || *p != '\n') { err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); if (err) return err; @@ -1044,7 +1042,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer, ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line"); goto done; } - if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') { + if (parse_oid_hex_algop(buffer, tagged_oid, &p, options->repo->hash_algo) || *p != '\n') { ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); if (ret) goto done; @@ -1336,9 +1334,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done, if (oidset_contains(blobs_done, oid)) continue; - buf = odb_read_object(the_repository->objects, oid, &type, &size); + buf = odb_read_object(options->repo->objects, oid, &type, &size); if (!buf) { - if (is_promisor_object(the_repository, oid)) + if (is_promisor_object(options->repo, oid)) continue; ret |= report(options, oid, OBJ_BLOB, msg_missing, @@ -1380,6 +1378,54 @@ bool fsck_has_queued_checks(struct fsck_options *options) !oidset_equal(&options->gitattributes_found, &options->gitattributes_done); } +void fsck_options_init(struct fsck_options *options, + struct repository *repo, + enum fsck_options_type type) +{ + static const struct fsck_options defaults[] = { + [FSCK_OPTIONS_DEFAULT] = { + .skip_oids = OIDSET_INIT, + .gitmodules_found = OIDSET_INIT, + .gitmodules_done = OIDSET_INIT, + .gitattributes_found = OIDSET_INIT, + .gitattributes_done = OIDSET_INIT, + .error_func = fsck_objects_error_function + }, + [FSCK_OPTIONS_STRICT] = { + .strict = 1, + .gitmodules_found = OIDSET_INIT, + .gitmodules_done = OIDSET_INIT, + .gitattributes_found = OIDSET_INIT, + .gitattributes_done = OIDSET_INIT, + .error_func = fsck_objects_error_function, + }, + [FSCK_OPTIONS_MISSING_GITMODULES] = { + .strict = 1, + .gitmodules_found = OIDSET_INIT, + .gitmodules_done = OIDSET_INIT, + .gitattributes_found = OIDSET_INIT, + .gitattributes_done = OIDSET_INIT, + .error_func = fsck_objects_error_cb_print_missing_gitmodules, + }, + [FSCK_OPTIONS_REFS] = { + .error_func = fsck_refs_error_function, + }, + }; + + switch (type) { + case FSCK_OPTIONS_DEFAULT: + case FSCK_OPTIONS_STRICT: + case FSCK_OPTIONS_MISSING_GITMODULES: + case FSCK_OPTIONS_REFS: + memcpy(options, &defaults[type], sizeof(*options)); + break; + default: + BUG("unknown fsck options type %d", type); + } + + options->repo = repo; +} + void fsck_options_clear(struct fsck_options *options) { free(options->msg_type); diff --git a/fsck.h b/fsck.h index 65ecbb7fe194ab..e77935c8a9ff6e 100644 --- a/fsck.h +++ b/fsck.h @@ -166,7 +166,10 @@ struct fsck_ref_report { const char *path; }; +struct repository; + struct fsck_options { + struct repository *repo; fsck_walk_func walk; fsck_error error_func; unsigned strict; @@ -180,34 +183,6 @@ struct fsck_options { kh_oid_map_t *object_names; }; -#define FSCK_OPTIONS_DEFAULT { \ - .skip_oids = OIDSET_INIT, \ - .gitmodules_found = OIDSET_INIT, \ - .gitmodules_done = OIDSET_INIT, \ - .gitattributes_found = OIDSET_INIT, \ - .gitattributes_done = OIDSET_INIT, \ - .error_func = fsck_objects_error_function \ -} -#define FSCK_OPTIONS_STRICT { \ - .strict = 1, \ - .gitmodules_found = OIDSET_INIT, \ - .gitmodules_done = OIDSET_INIT, \ - .gitattributes_found = OIDSET_INIT, \ - .gitattributes_done = OIDSET_INIT, \ - .error_func = fsck_objects_error_function, \ -} -#define FSCK_OPTIONS_MISSING_GITMODULES { \ - .strict = 1, \ - .gitmodules_found = OIDSET_INIT, \ - .gitmodules_done = OIDSET_INIT, \ - .gitattributes_found = OIDSET_INIT, \ - .gitattributes_done = OIDSET_INIT, \ - .error_func = fsck_objects_error_cb_print_missing_gitmodules, \ -} -#define FSCK_REFS_OPTIONS_DEFAULT { \ - .error_func = fsck_refs_error_function, \ -} - /* descend in all linked child objects * the return value is: * -1 error in processing the object @@ -255,6 +230,17 @@ int fsck_finish(struct fsck_options *options); */ bool fsck_has_queued_checks(struct fsck_options *options); +enum fsck_options_type { + FSCK_OPTIONS_DEFAULT, + FSCK_OPTIONS_STRICT, + FSCK_OPTIONS_MISSING_GITMODULES, + FSCK_OPTIONS_REFS, +}; + +void fsck_options_init(struct fsck_options *options, + struct repository *repo, + enum fsck_options_type type); + /* * Clear the fsck_options struct, freeing any allocated memory. */ diff --git a/gpg-interface.c b/gpg-interface.c index d517425034ee6c..dafd5371fa806d 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 a365586ce1e755..3d95f5ec14bd0c 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= and --signed-commits= options. */ enum sign_mode { SIGN_ABORT, + SIGN_ABORT_IF_INVALID, SIGN_WARN_VERBATIM, SIGN_VERBATIM, SIGN_WARN_STRIP, diff --git a/line-log.c b/line-log.c index eeaf68454e2246..858a899cd2a61d 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 e9dadbc1a58e2c..04a6ea64d3d68f 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/object-file.c b/object-file.c index 4f77ce0982625d..9e57eb6c27f44a 100644 --- a/object-file.c +++ b/object-file.c @@ -1282,8 +1282,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)) diff --git a/pack-check.c b/pack-check.c index 7378c80730d537..79992bb509f473 100644 --- a/pack-check.c +++ b/pack-check.c @@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r, struct packed_git *p, struct pack_window **w_curs, verify_fn fn, + void *fn_data, struct progress *progress, uint32_t base_count) { @@ -161,7 +162,7 @@ static int verify_packfile(struct repository *r, oid_to_hex(&oid), p->pack_name); else if (fn) { int eaten = 0; - err |= fn(&oid, type, size, data, &eaten); + err |= fn(&oid, type, size, data, &eaten, fn_data); if (eaten) data = NULL; } @@ -192,7 +193,7 @@ int verify_pack_index(struct packed_git *p) return err; } -int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, +int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data, struct progress *progress, uint32_t base_count) { int err = 0; @@ -202,7 +203,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, if (!p->index_data) return -1; - err |= verify_packfile(r, p, &w_curs, fn, progress, base_count); + err |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count); unuse_pack(&w_curs); return err; diff --git a/pack.h b/pack.h index ec76472e49b052..1cde92082be903 100644 --- a/pack.h +++ b/pack.h @@ -85,7 +85,11 @@ struct pack_idx_entry { struct progress; /* Note, the data argument could be NULL if object type is blob */ -typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*); +typedef int (*verify_fn)(const struct object_id *oid, + enum object_type type, + unsigned long size, + void *buffer, int *eaten, + void *fn_data); const char *write_idx_file(struct repository *repo, const char *index_name, @@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo, const unsigned char *sha1); int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr); int verify_pack_index(struct packed_git *); -int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t); +int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data, + struct progress *, uint32_t); off_t write_pack_header(struct hashfile *f, uint32_t); void fixup_pack_header_footer(const struct git_hash_algo *, int, unsigned char *, const char *, uint32_t, diff --git a/path-walk.c b/path-walk.c index 2aa3e7d8a4133d..6e426af4330893 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 5049f9baca9c5e..b1074fbf061b64 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) diff --git a/revision.c b/revision.c index fda405bf65326a..599b3a66c369ca 100644 --- a/revision.c +++ b/revision.c @@ -3129,6 +3129,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (want_ancestry(revs)) revs->limited = 1; revs->topo_order = 1; + if (!revs->diffopt.output_format) + revs->diffopt.output_format = DIFF_FORMAT_PATCH; } if (revs->topo_order && !generation_numbers_enabled(the_repository)) diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 29ea7d4268eca4..205d86d05edfec 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-new && + + cat sample-i >target && + git apply --whitespace=fix 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 0a7c3ca42f80fa..aaf197d2edc4d8 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 && + # 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 91b405489892bd..52c90afb3a7758 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 bd25bb2f591f0d..c40036899a6ec0 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 c905e01bc25c3e..ead6500d4d44d8 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 1eee8a7801055d..a41851a51d2a9f 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 d930b6eec4c469..0ec9990eab32c6 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 d930b6eec4c469..0ec9990eab32c6 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 994c37db1efec4..54c568f273a6d2 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 052def8074da29..65a8cc673a6fca 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 a1f5bc49c879e4..b24ae40e03cbd3 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 a475768710b5a6..cd92100dfc468d 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 39ce39bebed75b..ff31291d348e33 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 8480bd9cc45bc7..4bef21e6578f11 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 c5164f3be3a287..aed01522e3970d 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 1f7cd06941495a..a413ad36598ddf 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 5adfdfc1a12076..e8d62328cf87c5 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 03ab5c1784e289..3b2e2384da7b7f 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 223b4ed2a0cfca..f49abcea3e7899 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 dbd987b74a451f..0dee50ffb7c694 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 9015a45a256fdc..b8c260e8aea049 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 9015a45a256fdc..b8c260e8aea049 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 36ed12aa9cb8b9..c25f2ce19c05d9 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 e68f8928ea70af..3178989253a885 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 65508d7c0bc6ae..983c711fe3d6e5 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 77b721c196b191..e67fa017a7b3bc 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 d20708c9f9a0f4..0792b27cad89ac 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 617cdf34819e5e..d3bd7c7bc625b5 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 6a94d3b9cba2dd..7735b1972380be 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 11ec9bdecfcc8d..bc33b963dc8570 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 98c69109632c2d..1c40f904f8bb31 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 9223372036854775 +0000 + committer Example 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 29e2f176081561..117cfa051f33e2 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 2c347a91fe6d9f..f3b5e39493677b 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 output && @@ -135,6 +135,14 @@ do # corresponding `data ` 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 log 2>&1 && + test_grep "aborting due to invalid signature" log && + return 0 + fi && + git -C new fast-import --quiet --signed-commits=$mode 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 363619e7d1abf1..ec2b241cdb82ec 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 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 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 ` 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 log 2>&1 && + test_grep "aborting due to invalid signature" log && + return 0 + fi && + + git -C import fast-import --quiet --signed-tags=$mode 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 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 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 ` 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 /dev/null 2>&1 && + + # Import using explicitly provided signing key. + git -C import fast-import --quiet \ + --signed-tags=sign-if-invalid="${GPGSSH_KEY_PRIMARY}" actual && + test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual && + git -C import verify-tag "$IMPORTED" +' + test_done