diff options
52 files changed, 719 insertions, 460 deletions
diff --git a/Documentation/RelNotes/2.43.0.txt b/Documentation/RelNotes/2.43.0.txt index 76deb1a7bc..a8f9461163 100644 --- a/Documentation/RelNotes/2.43.0.txt +++ b/Documentation/RelNotes/2.43.0.txt @@ -57,10 +57,21 @@ UI, Workflows & Features * The command-line complation support (in contrib/) learned to complete "git commit --trailer=" for possible trailer keys. - * "git update-index" learns "--show-index-version" to inspect the index format version used by the on-disk index file. + * "git diff" learned diff.statNameWidth configuration variable, to + give the default width for the name part in the "--stat" output. + + * "git range-diff --notes=foo" compared "log --notes=foo --notes" of + the two ranges, instead of using just the specified notes tree. + + * The command line completion script (in contrib/) can be told to + complete aliases by including ": git <cmd> ;" in the alias to tell + it that the alias should be completed similar to how "git <cmd>" is + completed. The parsing code for the alias as been loosened to + allow ';' without an extra space before it. + Performance, Internal Implementation, Development Support etc. @@ -74,18 +85,21 @@ Performance, Internal Implementation, Development Support etc. * Tests that are known to pass with LSan are now marked as such. (merge 5fafe8c95f tb/mark-more-tests-as-leak-free later to maint). - * Flakey "git p4" tests, as well as "git svn" tests, are now skipped + * Flaky "git p4" tests, as well as "git svn" tests, are now skipped in the (rather expensive) sanitizer CI job. (merge 6ba913629f js/ci-san-skip-p4-and-svn-tests later to maint). * Tests with LSan from time to time seem to emit harmless message - that makes our tests unnecessarily flakey; we work it around by + that makes our tests unnecessarily flaky; we work it around by filtering the uninteresting output. (merge 370ef7e40d jk/test-lsan-denoise-output later to maint). * Unused parameters to functions are marked as such, and/or removed, in order to bring us closer to -Wunused-parameter clean. + * The code to keep track of existing packs in the repository while + repacking has been refactored. + Fixes since v2.42 ----------------- @@ -131,7 +145,7 @@ Fixes since v2.42 pathnames recorded in tree objects. (merge 4d5693ba05 jk/tree-name-and-depth-limit later to maint). - * Various fixes to the behaviour of "rebase -i" when the command got + * Various fixes to the behavior of "rebase -i" when the command got interrupted by conflicting changes. (merge 203573b024 pw/rebase-i-after-failure later to maint). @@ -152,7 +166,7 @@ Fixes since v2.42 which has been corrected. (merge 48944f214c pw/diff-no-index-from-named-pipes later to maint). - * Update "git maintainance" timers' implementation based on systemd + * Update "git maintenance" timers' implementation based on systemd timers to work with WSL. (merge 5e8515e8e8 js/systemd-timers-wsl-fix later to maint). @@ -166,6 +180,19 @@ Fixes since v2.42 work. (merge 4333267995 pb/completion-aliases-doc later to maint). + * HTTP Header redaction code has been adjusted for a newer version of + cURL library that shows its traces differently from earlier + versions. + (merge 0763c3a2c4 jk/redact-h2h3-headers-fix later to maint). + + * An error message given by "git send-email" when given a malformed + address did not give correct information, which has been corrected. + (merge 12288cc44e tb/send-email-extract-valid-address-error-message-fix later to maint). + + * UBSan options were not propagated through the test framework to git + run via the httpd, unlike ASan options, which has been corrected. + (merge 252d693797 jk/test-pass-ubsan-options-to-http-test later to maint). + * Other code cleanup, docfix, build fix, etc. (merge fd3ba590d8 ws/git-push-doc-grammofix later to maint). (merge 5f33a843de ds/upload-pack-error-sequence-fix later to maint). @@ -182,3 +209,7 @@ Fixes since v2.42 (merge 078c42531e rs/name-rev-use-opt-hidden-bool later to maint). (merge 63642d58b4 ob/sequencer-remove-dead-code later to maint). (merge 8aae489756 ob/t3404-typofix later to maint). + (merge 58be11432e eg/config-type-path-docfix later to maint). + (merge 563f339d98 ch/clean-docfix later to maint). + (merge 4fbe83fcd9 hy/doc-show-is-like-log-not-diff-tree later to maint). + (merge 43abaaf008 ob/am-msgfix later to maint). diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt index 35a7bf86d7..9391c77e55 100644 --- a/Documentation/config/diff.txt +++ b/Documentation/config/diff.txt @@ -52,6 +52,10 @@ directories with less than 10% of the total amount of changed files, and accumulating child directory counts in the parent directories: `files,10,cumulative`. +diff.statNameWidth:: + Limit the width of the filename part in --stat output. If set, applies + to all commands generating --stat output except format-patch. + diff.statGraphWidth:: Limit the width of the graph part in --stat output. If set, applies to all commands generating --stat output except format-patch. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index c07488b123..35fae7c87c 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -204,14 +204,15 @@ have to use `--diff-algorithm=default` option. part. Maximum width defaults to terminal width, or 80 columns if not connected to a terminal, and can be overridden by `<width>`. The width of the filename part can be limited by - giving another width `<name-width>` after a comma. The width - of the graph part can be limited by using - `--stat-graph-width=<width>` (affects all commands generating - a stat graph) or by setting `diff.statGraphWidth=<width>` - (does not affect `git format-patch`). - By giving a third parameter `<count>`, you can limit the - output to the first `<count>` lines, followed by `...` if - there are more. + giving another width `<name-width>` after a comma or by setting + `diff.statNameWidth=<width>`. The width of the graph part can be + limited by using `--stat-graph-width=<width>` or by setting + `diff.statGraphWidth=<width>`. Using `--stat` or + `--stat-graph-width` affects all commands generating a stat graph, + while setting `diff.statNameWidth` or `diff.statGraphWidth` + does not affect `git format-patch`. + By giving a third parameter `<count>`, you can limit the output to + the first `<count>` lines, followed by `...` if there are more. + These parameters can also be set individually with `--stat-width=<width>`, `--stat-name-width=<name-width>` and `--stat-count=<count>`. diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 4af0904f47..a30e3ebc51 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -12,8 +12,10 @@ SYNOPSIS 'git checkout' [-q] [-f] [-m] --detach [<branch>] 'git checkout' [-q] [-f] [-m] [--detach] <commit> 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new-branch>] [<start-point>] -'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>... -'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul] +'git checkout' [-f] <tree-ish> [--] <pathspec>... +'git checkout' [-f] <tree-ish> --pathspec-from-file=<file> [--pathspec-file-nul] +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [--] <pathspec>... +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] --pathspec-from-file=<file> [--pathspec-file-nul] 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...] DESCRIPTION @@ -260,7 +262,8 @@ and mark the resolved paths with `git add` (or `git rm` if the merge should result in deletion of the path). + When checking out paths from the index, this option lets you recreate -the conflicted merge in the specified paths. +the conflicted merge in the specified paths. This option cannot be +used when checking out paths from a tree-ish. + When switching branches with `--merge`, staged changes may be lost. diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index 160d08b86b..5e1a3d5148 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -127,7 +127,7 @@ ask each:: quit:: - This lets you quit without do cleaning. + This lets you quit without doing any cleaning. help:: diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 7a2bcb2f6c..b1caac887a 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -201,7 +201,7 @@ Valid `<type>`'s include: 1073741824 upon input. - 'bool-or-int': canonicalize according to either 'bool' or 'int', as described above. -- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and +- 'path': canonicalize by expanding a leading `~` to the value of `$HOME` and `~user` to the home directory for the specified user. This specifier has no effect when setting the value (but you can use `git config section.variable ~/` from the command line to let your shell do the expansion.) diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt index 5964810caa..c70444705b 100644 --- a/Documentation/git-restore.txt +++ b/Documentation/git-restore.txt @@ -78,6 +78,8 @@ all modified paths. --theirs:: When restoring files in the working tree from the index, use stage #2 ('ours') or #3 ('theirs') for unmerged paths. + This option cannot be used when checking out paths from a + tree-ish (i.e. with the `--source` option). + Note that during `git rebase` and `git pull --rebase`, 'ours' and 'theirs' may appear swapped. See the explanation of the same options @@ -87,6 +89,8 @@ in linkgit:git-checkout[1] for details. --merge:: When restoring files on the working tree from the index, recreate the conflicted merge in the unmerged paths. + This option cannot be used when checking out paths from a + tree-ish (i.e. with the `--source` option). --conflict=<style>:: The same as `--merge` option above, but changes the way the diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt index 2b1bc7288d..03c0634518 100644 --- a/Documentation/git-show.txt +++ b/Documentation/git-show.txt @@ -26,7 +26,7 @@ with --name-only). For plain blobs, it shows the plain contents. -The command takes options applicable to the 'git diff-tree' command to +Some options that 'git log' command understands can be used to control how the changes the commit introduces are shown. This manual page describes only the most frequently used options. diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index dc685be363..335395b727 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -87,6 +87,10 @@ being displayed. Examples: "--notes=foo" will show only notes from "--notes --notes=foo --no-notes --notes=bar" will only show notes from "refs/notes/bar". +--show-notes-by-default:: + Show the default notes unless options for displaying specific + notes are given. + --show-notes[=<ref>]:: --[no-]standard-notes:: These options are deprecated. Use the above --notes/--no-notes diff --git a/builtin/am.c b/builtin/am.c index 202040b62e..6655059a57 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2303,7 +2303,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar if (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode) return error(_("options '%s=%s' and '%s=%s' " "cannot be used together"), - "--show-current-patch", "--show-current-patch", arg, valid_modes[resume->sub_mode]); + "--show-current-patch", arg, + "--show-current-patch", valid_modes[resume->sub_mode]); resume->mode = RESUME_SHOW_PATCH; resume->sub_mode = new_value; diff --git a/builtin/checkout.c b/builtin/checkout.c index f53612f468..f02434bc15 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -523,6 +523,15 @@ static int checkout_paths(const struct checkout_opts *opts, "--merge", "--conflict", "--staged"); } + /* + * recreating unmerged index entries and writing out data from + * unmerged index entries would make no sense when checking out + * of a tree-ish. + */ + if ((opts->merge || opts->writeout_stage) && opts->source_tree) + die(_("'%s', '%s', or '%s' cannot be used when checking out of a tree"), + "--merge", "--ours", "--theirs"); + if (opts->patch_mode) { enum add_p_mode patch_mode; const char *rev = new_branch_info->name; @@ -560,6 +569,8 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->source_tree) read_tree_some(opts->source_tree, &opts->pathspec); + if (opts->merge) + unmerge_index(&the_index, &opts->pathspec, CE_MATCHED); ps_matched = xcalloc(opts->pathspec.nr, 1); @@ -583,10 +594,6 @@ static int checkout_paths(const struct checkout_opts *opts, } free(ps_matched); - /* "checkout -m path" to recreate conflicted state */ - if (opts->merge) - unmerge_marked_index(&the_index); - /* Any unmerged paths? */ for (pos = 0; pos < the_index.cache_nr; pos++) { const struct cache_entry *ce = the_index.cache[pos]; diff --git a/builtin/diff.c b/builtin/diff.c index 0b313549c7..c0f564273a 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -475,6 +475,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) /* Set up defaults that will apply to both no-index and regular diffs. */ rev.diffopt.stat_width = -1; + rev.diffopt.stat_name_width = -1; rev.diffopt.stat_graph_width = -1; rev.diffopt.flags.allow_external = 1; rev.diffopt.flags.allow_textconv = 1; diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 7e99c4d61b..5d01db5c02 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -129,8 +129,9 @@ struct fsmonitor_cookie_item { enum fsmonitor_cookie_item_result result; }; -static int cookies_cmp(const void *data, const struct hashmap_entry *he1, - const struct hashmap_entry *he2, const void *keydata) +static int cookies_cmp(const void *data UNUSED, + const struct hashmap_entry *he1, + const struct hashmap_entry *he2, const void *keydata) { const struct fsmonitor_cookie_item *a = container_of(he1, const struct fsmonitor_cookie_item, entry); @@ -1412,7 +1413,7 @@ done: return err; } -static int try_to_run_foreground_daemon(int detach_console) +static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED) { /* * Technically, we don't need to probe for an existing daemon @@ -1442,7 +1443,8 @@ static int try_to_run_foreground_daemon(int detach_console) static start_bg_wait_cb bg_wait_cb; -static int bg_wait_cb(const struct child_process *cp, void *cb_data) +static int bg_wait_cb(const struct child_process *cp UNUSED, + void *cb_data UNUSED) { enum ipc_active_state s = fsmonitor_ipc__get_state(); diff --git a/builtin/log.c b/builtin/log.c index b085417942..80e1be1645 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -178,6 +178,7 @@ static void cmd_log_init_defaults(struct rev_info *rev) rev->verbose_header = 1; rev->diffopt.flags.recursive = 1; rev->diffopt.stat_width = -1; /* use full terminal width */ + rev->diffopt.stat_name_width = -1; /* respect statNameWidth config */ rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; diff --git a/builtin/merge.c b/builtin/merge.c index 545da0c8a1..fd21c0d4f4 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -467,6 +467,7 @@ static void finish(struct commit *head_commit, struct diff_options opts; repo_diff_setup(the_repository, &opts); opts.stat_width = -1; /* use full terminal width */ + opts.stat_name_width = -1; /* respect statNameWidth config */ opts.stat_graph_width = -1; /* respect statGraphWidth config */ opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff --git a/builtin/rebase.c b/builtin/rebase.c index 50cb85751f..ed15accec9 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1804,6 +1804,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* We want color (if set), but no pager */ repo_diff_setup(the_repository, &opts); opts.stat_width = -1; /* use full terminal width */ + opts.stat_name_width = -1; /* respect statNameWidth config */ opts.stat_graph_width = -1; /* respect statGraphWidth config */ opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff --git a/builtin/repack.c b/builtin/repack.c index 6943c5ba11..529e13120d 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -27,7 +27,6 @@ #define PACK_CRUFT 4 #define DELETE_PACK 1 -#define CRUFT_PACK 2 static int pack_everything; static int delta_base_offset = 1; @@ -95,14 +94,106 @@ static int repack_config(const char *var, const char *value, return git_default_config(var, value, ctx, cb); } +struct existing_packs { + struct string_list kept_packs; + struct string_list non_kept_packs; + struct string_list cruft_packs; +}; + +#define EXISTING_PACKS_INIT { \ + .kept_packs = STRING_LIST_INIT_DUP, \ + .non_kept_packs = STRING_LIST_INIT_DUP, \ + .cruft_packs = STRING_LIST_INIT_DUP, \ +} + +static int has_existing_non_kept_packs(const struct existing_packs *existing) +{ + return existing->non_kept_packs.nr || existing->cruft_packs.nr; +} + +static void pack_mark_for_deletion(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util | DELETE_PACK); +} + +static int pack_is_marked_for_deletion(struct string_list_item *item) +{ + return (uintptr_t)item->util & DELETE_PACK; +} + +static void mark_packs_for_deletion_1(struct string_list *names, + struct string_list *list) +{ + struct string_list_item *item; + const int hexsz = the_hash_algo->hexsz; + + for_each_string_list_item(item, list) { + char *sha1; + size_t len = strlen(item->string); + if (len < hexsz) + continue; + sha1 = item->string + len - hexsz; + /* + * Mark this pack for deletion, which ensures that this + * pack won't be included in a MIDX (if `--write-midx` + * was given) and that we will actually delete this pack + * (if `-d` was given). + */ + if (!string_list_has_string(names, sha1)) + pack_mark_for_deletion(item); + } +} + +static void mark_packs_for_deletion(struct existing_packs *existing, + struct string_list *names) + +{ + mark_packs_for_deletion_1(names, &existing->non_kept_packs); + mark_packs_for_deletion_1(names, &existing->cruft_packs); +} + +static void remove_redundant_pack(const char *dir_name, const char *base_name) +{ + struct strbuf buf = STRBUF_INIT; + struct multi_pack_index *m = get_local_multi_pack_index(the_repository); + strbuf_addf(&buf, "%s.pack", base_name); + if (m && midx_contains_pack(m, buf.buf)) + clear_midx_file(the_repository); + strbuf_insertf(&buf, 0, "%s/", dir_name); + unlink_pack_path(buf.buf, 1); + strbuf_release(&buf); +} + +static void remove_redundant_packs_1(struct string_list *packs) +{ + struct string_list_item *item; + for_each_string_list_item(item, packs) { + if (!pack_is_marked_for_deletion(item)) + continue; + remove_redundant_pack(packdir, item->string); + } +} + +static void remove_redundant_existing_packs(struct existing_packs *existing) +{ + remove_redundant_packs_1(&existing->non_kept_packs); + remove_redundant_packs_1(&existing->cruft_packs); +} + +static void existing_packs_release(struct existing_packs *existing) +{ + string_list_clear(&existing->kept_packs, 0); + string_list_clear(&existing->non_kept_packs, 0); + string_list_clear(&existing->cruft_packs, 0); +} + /* - * Adds all packs hex strings (pack-$HASH) to either fname_nonkept_list - * or fname_kept_list based on whether each pack has a corresponding + * Adds all packs hex strings (pack-$HASH) to either packs->non_kept + * or packs->kept based on whether each pack has a corresponding * .keep file or not. Packs without a .keep file are not to be kept * if we are going to pack everything into one file. */ -static void collect_pack_filenames(struct string_list *fname_nonkept_list, - struct string_list *fname_kept_list, +static void collect_pack_filenames(struct existing_packs *existing, const struct string_list *extra_keep) { struct packed_git *p; @@ -126,28 +217,14 @@ static void collect_pack_filenames(struct string_list *fname_nonkept_list, strbuf_strip_suffix(&buf, ".pack"); if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep) - string_list_append(fname_kept_list, buf.buf); - else { - struct string_list_item *item; - item = string_list_append(fname_nonkept_list, buf.buf); - if (p->is_cruft) - item->util = (void*)(uintptr_t)CRUFT_PACK; - } + string_list_append(&existing->kept_packs, buf.buf); + else if (p->is_cruft) + string_list_append(&existing->cruft_packs, buf.buf); + else + string_list_append(&existing->non_kept_packs, buf.buf); } - string_list_sort(fname_kept_list); - strbuf_release(&buf); -} - -static void remove_redundant_pack(const char *dir_name, const char *base_name) -{ - struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_local_multi_pack_index(the_repository); - strbuf_addf(&buf, "%s.pack", base_name); - if (m && midx_contains_pack(m, buf.buf)) - clear_midx_file(the_repository); - strbuf_insertf(&buf, 0, "%s/", dir_name); - unlink_pack_path(buf.buf, 1); + string_list_sort(&existing->kept_packs); strbuf_release(&buf); } @@ -327,7 +404,7 @@ static int geometry_cmp(const void *va, const void *vb) } static void init_pack_geometry(struct pack_geometry *geometry, - struct string_list *existing_kept_packs, + struct existing_packs *existing, const struct pack_objects_args *args) { struct packed_git *p; @@ -344,23 +421,24 @@ static void init_pack_geometry(struct pack_geometry *geometry, if (!pack_kept_objects) { /* - * Any pack that has its pack_keep bit set will appear - * in existing_kept_packs below, but this saves us from - * doing a more expensive check. + * Any pack that has its pack_keep bit set will + * appear in existing->kept_packs below, but + * this saves us from doing a more expensive + * check. */ if (p->pack_keep) continue; /* - * The pack may be kept via the --keep-pack option; - * check 'existing_kept_packs' to determine whether to - * ignore it. + * The pack may be kept via the --keep-pack + * option; check 'existing->kept_packs' to + * determine whether to ignore it. */ strbuf_reset(&buf); strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); - if (string_list_has_string(existing_kept_packs, buf.buf)) + if (string_list_has_string(&existing->kept_packs, buf.buf)) continue; } if (p->is_cruft) @@ -494,6 +572,32 @@ static struct packed_git *get_preferred_pack(struct pack_geometry *geometry) return NULL; } +static void geometry_remove_redundant_packs(struct pack_geometry *geometry, + struct string_list *names, + struct existing_packs *existing) +{ + struct strbuf buf = STRBUF_INIT; + uint32_t i; + + for (i = 0; i < geometry->split; i++) { + struct packed_git *p = geometry->pack[i]; + if (string_list_has_string(names, hash_to_hex(p->hash))) + continue; + + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(p)); + strbuf_strip_suffix(&buf, ".pack"); + + if ((p->pack_keep) || + (string_list_has_string(&existing->kept_packs, buf.buf))) + continue; + + remove_redundant_pack(packdir, buf.buf); + } + + strbuf_release(&buf); +} + static void free_pack_geometry(struct pack_geometry *geometry) { if (!geometry) @@ -565,14 +669,13 @@ static void midx_snapshot_refs(struct tempfile *f) } static void midx_included_packs(struct string_list *include, - struct string_list *existing_nonkept_packs, - struct string_list *existing_kept_packs, + struct existing_packs *existing, struct string_list *names, struct pack_geometry *geometry) { struct string_list_item *item; - for_each_string_list_item(item, existing_kept_packs) + for_each_string_list_item(item, &existing->kept_packs) string_list_insert(include, xstrfmt("%s.idx", item->string)); for_each_string_list_item(item, names) string_list_insert(include, xstrfmt("pack-%s.idx", item->string)); @@ -599,24 +702,32 @@ static void midx_included_packs(struct string_list *include, string_list_insert(include, strbuf_detach(&buf, NULL)); } - - for_each_string_list_item(item, existing_nonkept_packs) { - if (!((uintptr_t)item->util & CRUFT_PACK)) { - /* - * no need to check DELETE_PACK, since we're not - * doing an ALL_INTO_ONE repack - */ - continue; - } - string_list_insert(include, xstrfmt("%s.idx", item->string)); - } } else { - for_each_string_list_item(item, existing_nonkept_packs) { - if ((uintptr_t)item->util & DELETE_PACK) + for_each_string_list_item(item, &existing->non_kept_packs) { + if (pack_is_marked_for_deletion(item)) continue; string_list_insert(include, xstrfmt("%s.idx", item->string)); } } + + for_each_string_list_item(item, &existing->cruft_packs) { + /* + * When doing a --geometric repack, there is no need to check + * for deleted packs, since we're by definition not doing an + * ALL_INTO_ONE repack (hence no packs will be deleted). + * Otherwise we must check for and exclude any packs which are + * enqueued for deletion. + * + * So we could omit the conditional below in the --geometric + * case, but doing so is unnecessary since no packs are marked + * as pending deletion (since we only call + * `mark_packs_for_deletion()` when doing an all-into-one + * repack). + */ + if (pack_is_marked_for_deletion(item)) + continue; + string_list_insert(include, xstrfmt("%s.idx", item->string)); + } } static int write_midx_included_packs(struct string_list *include, @@ -700,8 +811,7 @@ static int write_cruft_pack(const struct pack_objects_args *args, const char *pack_prefix, const char *cruft_expiration, struct string_list *names, - struct string_list *existing_packs, - struct string_list *existing_kept_packs) + struct existing_packs *existing) { struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf line = STRBUF_INIT; @@ -743,9 +853,11 @@ static int write_cruft_pack(const struct pack_objects_args *args, in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) fprintf(in, "%s-%s.pack\n", pack_prefix, item->string); - for_each_string_list_item(item, existing_packs) + for_each_string_list_item(item, &existing->non_kept_packs) + fprintf(in, "-%s.pack\n", item->string); + for_each_string_list_item(item, &existing->cruft_packs) fprintf(in, "-%s.pack\n", item->string); - for_each_string_list_item(item, existing_kept_packs) + for_each_string_list_item(item, &existing->kept_packs) fprintf(in, "%s.pack\n", item->string); fclose(in); @@ -777,8 +889,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; struct string_list names = STRING_LIST_INIT_DUP; - struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP; - struct string_list existing_kept_packs = STRING_LIST_INIT_DUP; + struct existing_packs existing = EXISTING_PACKS_INIT; struct pack_geometry geometry = { 0 }; struct strbuf line = STRBUF_INIT; struct tempfile *refs_snapshot = NULL; @@ -914,13 +1025,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid()); packtmp = mkpathdup("%s/%s", packdir, packtmp_name); - collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs, - &keep_pack_list); + collect_pack_filenames(&existing, &keep_pack_list); if (geometry.split_factor) { if (pack_everything) die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a"); - init_pack_geometry(&geometry, &existing_kept_packs, &po_args); + init_pack_geometry(&geometry, &existing, &po_args); split_pack_geometry(&geometry); } @@ -964,7 +1074,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (pack_everything & ALL_INTO_ONE) { repack_promisor_objects(&po_args, &names); - if (existing_nonkept_packs.nr && delete_redundant && + if (has_existing_non_kept_packs(&existing) && + delete_redundant && !(pack_everything & PACK_CRUFT)) { for_each_string_list_item(item, &names) { strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack", @@ -1055,8 +1166,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix, cruft_expiration, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; @@ -1087,8 +1197,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) pack_prefix, NULL, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; } @@ -1132,29 +1241,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } /* End of pack replacement. */ - if (delete_redundant && pack_everything & ALL_INTO_ONE) { - const int hexsz = the_hash_algo->hexsz; - for_each_string_list_item(item, &existing_nonkept_packs) { - char *sha1; - size_t len = strlen(item->string); - if (len < hexsz) - continue; - sha1 = item->string + len - hexsz; - /* - * Mark this pack for deletion, which ensures that this - * pack won't be included in a MIDX (if `--write-midx` - * was given) and that we will actually delete this pack - * (if `-d` was given). - */ - if (!string_list_has_string(&names, sha1)) - item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK); - } - } + if (delete_redundant && pack_everything & ALL_INTO_ONE) + mark_packs_for_deletion(&existing, &names); if (write_midx) { struct string_list include = STRING_LIST_INIT_NODUP; - midx_included_packs(&include, &existing_nonkept_packs, - &existing_kept_packs, &names, &geometry); + midx_included_packs(&include, &existing, &names, &geometry); ret = write_midx_included_packs(&include, &geometry, refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, @@ -1173,35 +1265,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant) { int opts = 0; - for_each_string_list_item(item, &existing_nonkept_packs) { - if (!((uintptr_t)item->util & DELETE_PACK)) - continue; - remove_redundant_pack(packdir, item->string); - } + remove_redundant_existing_packs(&existing); - if (geometry.split_factor) { - struct strbuf buf = STRBUF_INIT; - - uint32_t i; - for (i = 0; i < geometry.split; i++) { - struct packed_git *p = geometry.pack[i]; - if (string_list_has_string(&names, - hash_to_hex(p->hash))) - continue; - - strbuf_reset(&buf); - strbuf_addstr(&buf, pack_basename(p)); - strbuf_strip_suffix(&buf, ".pack"); - - if ((p->pack_keep) || - (string_list_has_string(&existing_kept_packs, - buf.buf))) - continue; - - remove_redundant_pack(packdir, buf.buf); - } - strbuf_release(&buf); - } + if (geometry.split_factor) + geometry_remove_redundant_packs(&geometry, &names, + &existing); if (show_progress) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); @@ -1225,8 +1293,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cleanup: string_list_clear(&names, 1); - string_list_clear(&existing_nonkept_packs, 0); - string_list_clear(&existing_kept_packs, 0); + existing_packs_release(&existing); free_pack_geometry(&geometry); return ret; diff --git a/builtin/update-index.c b/builtin/update-index.c index 97617c587e..7bcaa1476c 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -609,9 +609,6 @@ static const char * const update_index_usage[] = { NULL }; -static struct object_id head_oid; -static struct object_id merge_head_oid; - static struct cache_entry *read_one_ent(const char *which, struct object_id *ent, const char *path, int namelen, int stage) @@ -642,84 +639,17 @@ static struct cache_entry *read_one_ent(const char *which, static int unresolve_one(const char *path) { - int namelen = strlen(path); - int pos; - int ret = 0; - struct cache_entry *ce_2 = NULL, *ce_3 = NULL; - - /* See if there is such entry in the index. */ - pos = index_name_pos(&the_index, path, namelen); - if (0 <= pos) { - /* already merged */ - pos = unmerge_index_entry_at(&the_index, pos); - if (pos < the_index.cache_nr) { - const struct cache_entry *ce = the_index.cache[pos]; - if (ce_stage(ce) && - ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) - return 0; - } - /* no resolve-undo information; fall back */ - } else { - /* If there isn't, either it is unmerged, or - * resolved as "removed" by mistake. We do not - * want to do anything in the former case. - */ - pos = -pos-1; - if (pos < the_index.cache_nr) { - const struct cache_entry *ce = the_index.cache[pos]; - if (ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) { - fprintf(stderr, - "%s: skipping still unmerged path.\n", - path); - goto free_return; - } - } - } - - /* Grab blobs from given path from HEAD and MERGE_HEAD, - * stuff HEAD version in stage #2, - * stuff MERGE_HEAD version in stage #3. - */ - ce_2 = read_one_ent("our", &head_oid, path, namelen, 2); - ce_3 = read_one_ent("their", &merge_head_oid, path, namelen, 3); - - if (!ce_2 || !ce_3) { - ret = -1; - goto free_return; - } - if (oideq(&ce_2->oid, &ce_3->oid) && - ce_2->ce_mode == ce_3->ce_mode) { - fprintf(stderr, "%s: identical in both, skipping.\n", - path); - goto free_return; - } - - remove_file_from_index(&the_index, path); - if (add_index_entry(&the_index, ce_2, ADD_CACHE_OK_TO_ADD)) { - error("%s: cannot add our version to the index.", path); - ret = -1; - goto free_return; - } - if (!add_index_entry(&the_index, ce_3, ADD_CACHE_OK_TO_ADD)) - return 0; - error("%s: cannot add their version to the index.", path); - ret = -1; - free_return: - discard_cache_entry(ce_2); - discard_cache_entry(ce_3); - return ret; -} - -static void read_head_pointers(void) -{ - if (read_ref("HEAD", &head_oid)) - die("No HEAD -- no initial commit yet?"); - if (read_ref("MERGE_HEAD", &merge_head_oid)) { - fprintf(stderr, "Not in the middle of a merge.\n"); - exit(0); - } + struct string_list_item *item; + int res = 0; + + if (!the_index.resolve_undo) + return res; + item = string_list_lookup(the_index.resolve_undo, path); + if (!item) + return res; /* no resolve-undo record for the path */ + res = unmerge_index_entry(&the_index, path, item->util, 0); + FREE_AND_NULL(item->util); + return res; } static int do_unresolve(int ac, const char **av, @@ -728,11 +658,6 @@ static int do_unresolve(int ac, const char **av, int i; int err = 0; - /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we - * are not doing a merge, so exit with success status. - */ - read_head_pointers(); - for (i = 1; i < ac; i++) { const char *arg = av[i]; char *p = prefix_path(prefix, prefix_length, arg); @@ -751,6 +676,7 @@ static int do_reupdate(const char **paths, int pos; int has_head = 1; struct pathspec pathspec; + struct object_id head_oid; parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD, diff --git a/compat/fsmonitor/fsm-health-darwin.c b/compat/fsmonitor/fsm-health-darwin.c index 5b1709d63f..c2afcbe6c8 100644 --- a/compat/fsmonitor/fsm-health-darwin.c +++ b/compat/fsmonitor/fsm-health-darwin.c @@ -4,21 +4,21 @@ #include "fsm-health.h" #include "fsmonitor--daemon.h" -int fsm_health__ctor(struct fsmonitor_daemon_state *state) +int fsm_health__ctor(struct fsmonitor_daemon_state *state UNUSED) { return 0; } -void fsm_health__dtor(struct fsmonitor_daemon_state *state) +void fsm_health__dtor(struct fsmonitor_daemon_state *state UNUSED) { return; } -void fsm_health__loop(struct fsmonitor_daemon_state *state) +void fsm_health__loop(struct fsmonitor_daemon_state *state UNUSED) { return; } -void fsm_health__stop_async(struct fsmonitor_daemon_state *state) +void fsm_health__stop_async(struct fsmonitor_daemon_state *state UNUSED) { } diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c index 8928fa93ce..41984ea48e 100644 --- a/compat/fsmonitor/fsm-ipc-win32.c +++ b/compat/fsmonitor/fsm-ipc-win32.c @@ -6,6 +6,6 @@ const char *fsmonitor_ipc__get_path(struct repository *r) { static char *ret; if (!ret) - ret = git_pathdup("fsmonitor--daemon.ipc"); + ret = repo_git_path(r, "fsmonitor--daemon.ipc"); return ret; } diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c index 36c7e13281..11b56d3ef1 100644 --- a/compat/fsmonitor/fsm-listen-darwin.c +++ b/compat/fsmonitor/fsm-listen-darwin.c @@ -191,12 +191,12 @@ static void my_add_path(struct fsmonitor_batch *batch, const char *path) } -static void fsevent_callback(ConstFSEventStreamRef streamRef, +static void fsevent_callback(ConstFSEventStreamRef streamRef UNUSED, void *ctx, size_t num_of_events, void *event_paths, const FSEventStreamEventFlags event_flags[], - const FSEventStreamEventId event_ids[]) + const FSEventStreamEventId event_ids[] UNUSED) { struct fsmonitor_daemon_state *state = ctx; struct fsm_listen_data *data = state->listen_data; diff --git a/compat/fsmonitor/fsm-listen-win32.c b/compat/fsmonitor/fsm-listen-win32.c index a361a7db20..90a2412284 100644 --- a/compat/fsmonitor/fsm-listen-win32.c +++ b/compat/fsmonitor/fsm-listen-win32.c @@ -289,8 +289,7 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state) SetEvent(state->listen_data->hListener[LISTENER_SHUTDOWN]); } -static struct one_watch *create_watch(struct fsmonitor_daemon_state *state, - const char *path) +static struct one_watch *create_watch(const char *path) { struct one_watch *watch = NULL; DWORD desired_access = FILE_LIST_DIRECTORY; @@ -361,8 +360,7 @@ static void destroy_watch(struct one_watch *watch) free(watch); } -static int start_rdcw_watch(struct fsm_listen_data *data, - struct one_watch *watch) +static int start_rdcw_watch(struct one_watch *watch) { DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME | @@ -735,11 +733,11 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) state->listen_error_code = 0; - if (start_rdcw_watch(data, data->watch_worktree) == -1) + if (start_rdcw_watch(data->watch_worktree) == -1) goto force_error_stop; if (data->watch_gitdir && - start_rdcw_watch(data, data->watch_gitdir) == -1) + start_rdcw_watch(data->watch_gitdir) == -1) goto force_error_stop; for (;;) { @@ -755,7 +753,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) } if (result == -2) { /* retryable error */ - if (start_rdcw_watch(data, data->watch_worktree) == -1) + if (start_rdcw_watch(data->watch_worktree) == -1) goto force_error_stop; continue; } @@ -763,7 +761,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) /* have data */ if (process_worktree_events(state) == LISTENER_SHUTDOWN) goto force_shutdown; - if (start_rdcw_watch(data, data->watch_worktree) == -1) + if (start_rdcw_watch(data->watch_worktree) == -1) goto force_error_stop; continue; } @@ -776,7 +774,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) } if (result == -2) { /* retryable error */ - if (start_rdcw_watch(data, data->watch_gitdir) == -1) + if (start_rdcw_watch(data->watch_gitdir) == -1) goto force_error_stop; continue; } @@ -784,7 +782,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) /* have data */ if (process_gitdir_events(state) == LISTENER_SHUTDOWN) goto force_shutdown; - if (start_rdcw_watch(data, data->watch_gitdir) == -1) + if (start_rdcw_watch(data->watch_gitdir) == -1) goto force_error_stop; continue; } @@ -821,16 +819,14 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state) data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL); - data->watch_worktree = create_watch(state, - state->path_worktree_watch.buf); + data->watch_worktree = create_watch(state->path_worktree_watch.buf); if (!data->watch_worktree) goto failed; check_for_shortnames(data->watch_worktree); if (state->nr_paths_watching > 1) { - data->watch_gitdir = create_watch(state, - state->path_gitdir_watch.buf); + data->watch_gitdir = create_watch(state->path_gitdir_watch.buf); if (!data->watch_gitdir) goto failed; } diff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c index c8a3e9dcdb..f4f9cc1f33 100644 --- a/compat/fsmonitor/fsm-path-utils-win32.c +++ b/compat/fsmonitor/fsm-path-utils-win32.c @@ -132,7 +132,8 @@ int fsmonitor__is_fs_remote(const char *path) /* * No-op for now. */ -int fsmonitor__get_alias(const char *path, struct alias_info *info) +int fsmonitor__get_alias(const char *path UNUSED, + struct alias_info *info UNUSED) { return 0; } @@ -140,8 +141,8 @@ int fsmonitor__get_alias(const char *path, struct alias_info *info) /* * No-op for now. */ -char *fsmonitor__resolve_alias(const char *path, - const struct alias_info *info) +char *fsmonitor__resolve_alias(const char *path UNUSED, + const struct alias_info *info UNUSED) { return NULL; } diff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c index b6f6744494..0f2aa321f6 100644 --- a/compat/fsmonitor/fsm-settings-win32.c +++ b/compat/fsmonitor/fsm-settings-win32.c @@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r) return FSMONITOR_REASON_OK; } -enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc) +enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc UNUSED) { enum fsmonitor_reason reason; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 47fd664ea5..477ef8157a 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -28,7 +28,8 @@ # completion style. For example '!f() { : git commit ; ... }; f' will # tell the completion to use commit completion. This also works with aliases # of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '". -# Be sure to add a space between the command name and the ';'. +# Note that "git" is optional --- '!f() { : commit; ...}; f' would complete +# just like the 'git commit' command. # # If you have a command that is not part of git, but you would still # like completion, you can use __git_complete: @@ -1183,7 +1184,7 @@ __git_aliased_command () :) : skip null command ;; \'*) : skip opening quote after sh -c ;; *) - cur="$word" + cur="${word%;}" break esac done @@ -65,6 +65,7 @@ int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static int diff_no_prefix; static int diff_relative; +static int diff_stat_name_width; static int diff_stat_graph_width; static int diff_dirstat_permille_default = 30; static struct diff_options default_diff_options; @@ -410,6 +411,10 @@ int git_diff_ui_config(const char *var, const char *value, diff_relative = git_config_bool(var, value); return 0; } + if (!strcmp(var, "diff.statnamewidth")) { + diff_stat_name_width = git_config_int(var, value, ctx->kvi); + return 0; + } if (!strcmp(var, "diff.statgraphwidth")) { diff_stat_graph_width = git_config_int(var, value, ctx->kvi); return 0; @@ -2704,12 +2709,14 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) number_width = decimal_width(max_change) > number_width ? decimal_width(max_change) : number_width; + if (options->stat_name_width == -1) + options->stat_name_width = diff_stat_name_width; if (options->stat_graph_width == -1) options->stat_graph_width = diff_stat_graph_width; /* - * Guarantee 3/8*16==6 for the graph part - * and 5/8*16==10 for the filename part + * Guarantee 3/8*16 == 6 for the graph part + * and 5/8*16 == 10 for the filename part */ if (width < 16 + 6 + number_width) width = 16 + 6 + number_width; diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c index 88575aa54c..153918cf76 100644 --- a/fsmonitor-ipc.c +++ b/fsmonitor-ipc.c @@ -20,7 +20,7 @@ int fsmonitor_ipc__is_supported(void) return 0; } -const char *fsmonitor_ipc__get_path(struct repository *r) +const char *fsmonitor_ipc__get_path(struct repository *r UNUSED) { return NULL; } @@ -30,14 +30,14 @@ enum ipc_active_state fsmonitor_ipc__get_state(void) return IPC_STATE__OTHER_ERROR; } -int fsmonitor_ipc__send_query(const char *since_token, - struct strbuf *answer) +int fsmonitor_ipc__send_query(const char *since_token UNUSED, + struct strbuf *answer UNUSED) { return -1; } -int fsmonitor_ipc__send_command(const char *command, - struct strbuf *answer) +int fsmonitor_ipc__send_command(const char *command UNUSED, + struct strbuf *answer UNUSED) { return -1; } diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index b62acf44ae..a6a9e6bc19 100644 --- a/fsmonitor-settings.c +++ b/fsmonitor-settings.c @@ -62,7 +62,8 @@ static enum fsmonitor_reason check_remote(struct repository *r) } #endif -static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc) +static enum fsmonitor_reason check_for_incompatible(struct repository *r, + int ipc MAYBE_UNUSED) { if (!r->worktree) { /* diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 8bc8892c40..3e5907a460 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -118,7 +118,7 @@ proc sanitize_command_line {command_line from_index} { set i $from_index while {$i < [llength $command_line]} { set cmd [lindex $command_line $i] - if {[file pathtype $cmd] ne "absolute"} { + if {[llength [file split $cmd]] < 2} { set fullpath [_which $cmd] if {$fullpath eq ""} { throw {NOT-FOUND} "$cmd not found in PATH" @@ -661,31 +661,8 @@ proc git_write {args} { } proc githook_read {hook_name args} { - set pchook [gitdir hooks $hook_name] - lappend args 2>@1 - - # On Windows [file executable] might lie so we need to ask - # the shell if the hook is executable. Yes that's annoying. - # - if {[is_Windows]} { - upvar #0 _sh interp - if {![info exists interp]} { - set interp [_which sh] - } - if {$interp eq {}} { - error "hook execution requires sh (not in PATH)" - } - - set scr {if test -x "$1";then exec "$@";fi} - set sh_c [list $interp -c $scr $interp $pchook] - return [_open_stdout_stderr [concat $sh_c $args]] - } - - if {[file executable $pchook]} { - return [_open_stdout_stderr [concat [list $pchook] $args]] - } - - return {} + set cmd [concat git hook run --ignore-missing $hook_name -- $args 2>@1] + return [_open_stdout_stderr $cmd] } proc kill_file_process {fd} { diff --git a/git-send-email.perl b/git-send-email.perl index 897cea6564..288ea1ae80 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1166,10 +1166,10 @@ sub extract_valid_address { sub extract_valid_address_or_die { my $address = shift; - $address = extract_valid_address($address); + my $valid_address = extract_valid_address($address); die sprintf(__("error: unable to extract a valid address from: %s\n"), $address) - if !$address; - return $address; + if !$valid_address; + return $valid_address; } sub validate_address { @@ -339,7 +339,6 @@ void graph_setup_line_prefix(struct diff_options *diffopt) diffopt->output_prefix = diff_output_prefix_callback; } - struct git_graph *graph_init(struct rev_info *opt) { struct git_graph *graph = xmalloc(sizeof(struct git_graph)); @@ -738,18 +738,43 @@ static int redact_sensitive_header(struct strbuf *header, size_t offset) return ret; } +static int match_curl_h2_trace(const char *line, const char **out) +{ + const char *p; + + /* + * curl prior to 8.1.0 gives us: + * + * h2h3 [<header-name>: <header-val>] + * + * Starting in 8.1.0, the first token became just "h2". + */ + if (skip_iprefix(line, "h2h3 [", out) || + skip_iprefix(line, "h2 [", out)) + return 1; + + /* + * curl 8.3.0 uses: + * [HTTP/2] [<stream-id>] [<header-name>: <header-val>] + * where <stream-id> is numeric. + */ + if (skip_iprefix(line, "[HTTP/2] [", &p)) { + while (isdigit(*p)) + p++; + if (skip_prefix(p, "] [", out)) + return 1; + } + + return 0; +} + /* Redact headers in info */ static void redact_sensitive_info_header(struct strbuf *header) { const char *sensitive_header; - /* - * curl's h2h3 prints headers in info, e.g.: - * h2h3 [<header-name>: <header-val>] - */ if (trace_curl_redact && - (skip_iprefix(header->buf, "h2h3 [", &sensitive_header) || - skip_iprefix(header->buf, "h2 [", &sensitive_header))) { + match_curl_h2_trace(header->buf, &sensitive_header)) { if (redact_sensitive_header(header, sensitive_header - header->buf)) { /* redaction ate our closing bracket */ strbuf_addch(header, ']'); diff --git a/merge-ort.c b/merge-ort.c index 8631c99700..7857ce9fbd 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -721,23 +721,6 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti, renames->callback_data_nr = renames->callback_data_alloc = 0; } -__attribute__((format (printf, 2, 3))) -static int err(struct merge_options *opt, const char *err, ...) -{ - va_list params; - struct strbuf sb = STRBUF_INIT; - - strbuf_addstr(&sb, "error: "); - va_start(params, err); - strbuf_vaddf(&sb, err, params); - va_end(params); - - error("%s", sb.buf); - strbuf_release(&sb); - - return -1; -} - static void format_commit(struct strbuf *sb, int indent, struct repository *repo, @@ -2122,13 +2105,12 @@ static int handle_content_merge(struct merge_options *opt, &result_buf); if ((merge_status < 0) || !result_buf.ptr) - ret = err(opt, _("Failed to execute internal merge")); + ret = error(_("failed to execute internal merge")); if (!ret && write_object_file(result_buf.ptr, result_buf.size, OBJ_BLOB, &result->oid)) - ret = err(opt, _("Unable to add %s to database"), - path); + ret = error(_("unable to add %s to database"), path); free(result_buf.ptr); if (ret) @@ -3342,10 +3324,7 @@ static int collect_renames(struct merge_options *opt, return clean; } -static int detect_and_process_renames(struct merge_options *opt, - struct tree *merge_base, - struct tree *side1, - struct tree *side2) +static int detect_and_process_renames(struct merge_options *opt) { struct diff_queue_struct combined = { 0 }; struct rename_info *renames = &opt->priv->renames; @@ -3509,8 +3488,7 @@ static int sort_dirs_next_to_their_children(const char *one, const char *two) return c1 - c2; } -static int read_oid_strbuf(struct merge_options *opt, - const struct object_id *oid, +static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst) { void *buf; @@ -3518,10 +3496,10 @@ static int read_oid_strbuf(struct merge_options *opt, unsigned long size; buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) - return err(opt, _("cannot read object %s"), oid_to_hex(oid)); + return error(_("cannot read object %s"), oid_to_hex(oid)); if (type != OBJ_BLOB) { free(buf); - return err(opt, _("object %s is not a blob"), oid_to_hex(oid)); + return error(_("object %s is not a blob"), oid_to_hex(oid)); } strbuf_attach(dst, buf, size, size + 1); return 0; @@ -3545,8 +3523,8 @@ static int blob_unchanged(struct merge_options *opt, if (oideq(&base->oid, &side->oid)) return 1; - if (read_oid_strbuf(opt, &base->oid, &basebuf) || - read_oid_strbuf(opt, &side->oid, &sidebuf)) + if (read_oid_strbuf(&base->oid, &basebuf) || + read_oid_strbuf(&side->oid, &sidebuf)) goto error_return; /* * Note: binary | is used so that both renormalizations are @@ -4902,8 +4880,7 @@ static void merge_start(struct merge_options *opt, struct merge_result *result) trace2_region_leave("merge", "allocate/init", opt->repo); } -static void merge_check_renames_reusable(struct merge_options *opt, - struct merge_result *result, +static void merge_check_renames_reusable(struct merge_result *result, struct tree *merge_base, struct tree *side1, struct tree *side2) @@ -4973,7 +4950,7 @@ redo: * TRANSLATORS: The %s arguments are: 1) tree hash of a merge * base, and 2-3) the trees for the two trees we're merging. */ - err(opt, _("collecting merge info failed for trees %s, %s, %s"), + error(_("collecting merge info failed for trees %s, %s, %s"), oid_to_hex(&merge_base->object.oid), oid_to_hex(&side1->object.oid), oid_to_hex(&side2->object.oid)); @@ -4983,8 +4960,7 @@ redo: trace2_region_leave("merge", "collect_merge_info", opt->repo); trace2_region_enter("merge", "renames", opt->repo); - result->clean = detect_and_process_renames(opt, merge_base, - side1, side2); + result->clean = detect_and_process_renames(opt); trace2_region_leave("merge", "renames", opt->repo); if (opt->priv->renames.redo_after_renames == 2) { trace2_region_enter("merge", "reset_maps", opt->repo); @@ -5106,7 +5082,7 @@ void merge_incore_nonrecursive(struct merge_options *opt, trace2_region_enter("merge", "merge_start", opt->repo); assert(opt->ancestor != NULL); - merge_check_renames_reusable(opt, result, merge_base, side1, side2); + merge_check_renames_reusable(result, merge_base, side1, side2); merge_start(opt, result); /* * Record the trees used in this merge, so if there's a next merge in diff --git a/merge-recursive.c b/merge-recursive.c index 6a4081bb0f..0d7e57e2df 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1383,12 +1383,12 @@ static int merge_mode_and_contents(struct merge_options *opt, extra_marker_size); if ((merge_status < 0) || !result_buf.ptr) - ret = err(opt, _("Failed to execute internal merge")); + ret = err(opt, _("failed to execute internal merge")); if (!ret && write_object_file(result_buf.ptr, result_buf.size, OBJ_BLOB, &result->blob.oid)) - ret = err(opt, _("Unable to add %s to database"), + ret = err(opt, _("unable to add %s to database"), a->path); free(result_buf.ptr); diff --git a/range-diff.c b/range-diff.c index ca5493984a..c45b6d849c 100644 --- a/range-diff.c +++ b/range-diff.c @@ -60,7 +60,7 @@ static int read_patches(const char *range, struct string_list *list, "--output-indicator-context=#", "--no-abbrev-commit", "--pretty=medium", - "--notes", + "--show-notes-by-default", NULL); strvec_push(&cp.args, range); if (other_arg) @@ -1112,7 +1112,7 @@ int rerere_forget(struct repository *r, struct pathspec *pathspec) * recover the original conflicted state and then * find the conflicted paths. */ - unmerge_index(r->index, pathspec); + unmerge_index(r->index, pathspec, 0); find_conflict(r, &conflict); for (i = 0; i < conflict.nr; i++) { struct string_list_item *it = &conflict.items[i]; diff --git a/resolve-undo.c b/resolve-undo.c index 7817f5d6db..cd02dc9928 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -117,86 +117,59 @@ void resolve_undo_clear_index(struct index_state *istate) istate->cache_changed |= RESOLVE_UNDO_CHANGED; } -int unmerge_index_entry_at(struct index_state *istate, int pos) +int unmerge_index_entry(struct index_state *istate, const char *path, + struct resolve_undo_info *ru, unsigned ce_flags) { - const struct cache_entry *ce; - struct string_list_item *item; - struct resolve_undo_info *ru; - int i, err = 0, matched; - char *name; - - if (!istate->resolve_undo) - return pos; - - ce = istate->cache[pos]; - if (ce_stage(ce)) { - /* already unmerged */ - while ((pos < istate->cache_nr) && - ! strcmp(istate->cache[pos]->name, ce->name)) - pos++; - return pos - 1; /* return the last entry processed */ + int i = index_name_pos(istate, path, strlen(path)); + + if (i < 0) { + /* unmerged? */ + i = -i - 1; + if (i < istate->cache_nr && + !strcmp(istate->cache[i]->name, path)) + /* yes, it is already unmerged */ + return 0; + /* fallthru: resolved to removal */ + } else { + /* merged - remove it to replace it with unmerged entries */ + remove_index_entry_at(istate, i); } - item = string_list_lookup(istate->resolve_undo, ce->name); - if (!item) - return pos; - ru = item->util; - if (!ru) - return pos; - matched = ce->ce_flags & CE_MATCHED; - name = xstrdup(ce->name); - remove_index_entry_at(istate, pos); + for (i = 0; i < 3; i++) { - struct cache_entry *nce; + struct cache_entry *ce; if (!ru->mode[i]) continue; - nce = make_cache_entry(istate, - ru->mode[i], - &ru->oid[i], - name, i + 1, 0); - if (matched) - nce->ce_flags |= CE_MATCHED; - if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) { - err = 1; - error("cannot unmerge '%s'", name); - } + ce = make_cache_entry(istate, ru->mode[i], &ru->oid[i], + path, i + 1, 0); + ce->ce_flags |= ce_flags; + if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD)) + return error("cannot unmerge '%s'", path); } - free(name); - if (err) - return pos; - free(ru); - item->util = NULL; - return unmerge_index_entry_at(istate, pos); + return 0; } -void unmerge_marked_index(struct index_state *istate) +void unmerge_index(struct index_state *istate, const struct pathspec *pathspec, + unsigned ce_flags) { - int i; + struct string_list_item *item; if (!istate->resolve_undo) return; /* TODO: audit for interaction with sparse-index. */ ensure_full_index(istate); - for (i = 0; i < istate->cache_nr; i++) { - const struct cache_entry *ce = istate->cache[i]; - if (ce->ce_flags & CE_MATCHED) - i = unmerge_index_entry_at(istate, i); - } -} -void unmerge_index(struct index_state *istate, const struct pathspec *pathspec) -{ - int i; - - if (!istate->resolve_undo) - return; - - /* TODO: audit for interaction with sparse-index. */ - ensure_full_index(istate); - for (i = 0; i < istate->cache_nr; i++) { - const struct cache_entry *ce = istate->cache[i]; - if (!ce_path_match(istate, ce, pathspec, NULL)) + for_each_string_list_item(item, istate->resolve_undo) { + const char *path = item->string; + struct resolve_undo_info *ru = item->util; + if (!item->util) + continue; + if (!match_pathspec(istate, pathspec, + item->string, strlen(item->string), + 0, NULL, 0)) continue; - i = unmerge_index_entry_at(istate, i); + unmerge_index_entry(istate, path, ru, ce_flags); + free(ru); + item->util = NULL; } } diff --git a/resolve-undo.h b/resolve-undo.h index c5deafc92f..f3f8462751 100644 --- a/resolve-undo.h +++ b/resolve-undo.h @@ -17,8 +17,7 @@ void record_resolve_undo(struct index_state *, struct cache_entry *); void resolve_undo_write(struct strbuf *, struct string_list *); struct string_list *resolve_undo_read(const char *, unsigned long); void resolve_undo_clear_index(struct index_state *); -int unmerge_index_entry_at(struct index_state *, int); -void unmerge_index(struct index_state *, const struct pathspec *); -void unmerge_marked_index(struct index_state *); +int unmerge_index_entry(struct index_state *, const char *, struct resolve_undo_info *, unsigned); +void unmerge_index(struct index_state *, const struct pathspec *, unsigned); #endif diff --git a/revision.c b/revision.c index 2f4c53ea20..49d385257a 100644 --- a/revision.c +++ b/revision.c @@ -2484,6 +2484,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg revs->break_bar = xstrdup(optarg); revs->track_linear = 1; revs->track_first_time = 1; + } else if (!strcmp(arg, "--show-notes-by-default")) { + revs->show_notes_by_default = 1; } else if (skip_prefix(arg, "--show-notes=", &optarg) || skip_prefix(arg, "--notes=", &optarg)) { if (starts_with(arg, "--show-notes=") && @@ -3054,6 +3056,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->expand_tabs_in_log < 0) revs->expand_tabs_in_log = revs->expand_tabs_in_log_default; + if (!revs->show_notes_given && revs->show_notes_by_default) { + enable_default_display_notes(&revs->notes_opt, &revs->show_notes); + revs->show_notes_given = 1; + } + return left; } diff --git a/revision.h b/revision.h index 82ab400139..50091bbd13 100644 --- a/revision.h +++ b/revision.h @@ -253,6 +253,7 @@ struct rev_info { shown_dashes:1, show_merge:1, show_notes_given:1, + show_notes_by_default:1, show_signature:1, pretty_given:1, abbrev_commit:1, diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c index 3d1436da59..941ae7e3bc 100644 --- a/t/helper/test-simple-ipc.c +++ b/t/helper/test-simple-ipc.c @@ -278,7 +278,8 @@ static int daemon__run_server(void) static start_bg_wait_cb bg_wait_cb; -static int bg_wait_cb(const struct child_process *cp, void *cb_data) +static int bg_wait_cb(const struct child_process *cp UNUSED, + void *cb_data UNUSED) { int s = ipc_get_active_state(cl_args.path); diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index a22d138605..022276a6b9 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -92,6 +92,7 @@ PassEnv GIT_VALGRIND_OPTIONS PassEnv GNUPGHOME PassEnv ASAN_OPTIONS PassEnv LSAN_OPTIONS +PassEnv UBSAN_OPTIONS PassEnv GIT_TRACE PassEnv GIT_CONFIG_NOSYSTEM PassEnv GIT_TEST_SIDEBAND_ALL diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index 2d8c70b03a..3eda385ca2 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh @@ -37,11 +37,17 @@ prime_resolve_undo () { git checkout second^0 && test_tick && test_must_fail git merge third^0 && - echo merge does not leave anything && check_resolve_undo empty && - echo different >fi/le && - git add fi/le && - echo resolving records && + + # how should the conflict be resolved? + case "$1" in + remove) + rm -f file/le && git rm fi/le + ;; + *) # modify + echo different >fi/le && git add fi/le + ;; + esac check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le } @@ -122,6 +128,37 @@ test_expect_success 'add records checkout -m undoes' ' test_expect_success 'unmerge with plumbing' ' prime_resolve_undo && git update-index --unresolve fi/le && + git ls-files --resolve-undo fi/le >actual && + test_must_be_empty actual && + git ls-files -u >actual && + test_line_count = 3 actual +' + +test_expect_success 'unmerge can be done even after committing' ' + prime_resolve_undo && + git commit -m "record to nuke MERGE_HEAD" && + git update-index --unresolve fi/le && + git ls-files --resolve-undo fi/le >actual && + test_must_be_empty actual && + git ls-files -u >actual && + test_line_count = 3 actual +' + +test_expect_success 'unmerge removal' ' + prime_resolve_undo remove && + git update-index --unresolve fi/le && + git ls-files --resolve-undo fi/le >actual && + test_must_be_empty actual && + git ls-files -u >actual && + test_line_count = 3 actual +' + +test_expect_success 'unmerge removal after committing' ' + prime_resolve_undo remove && + git commit -m "record to nuke MERGE_HEAD" && + git update-index --unresolve fi/le && + git ls-files --resolve-undo fi/le >actual && + test_must_be_empty actual && git ls-files -u >actual && test_line_count = 3 actual ' diff --git a/t/t2070-restore.sh b/t/t2070-restore.sh index c5d19dd973..16d6348b69 100755 --- a/t/t2070-restore.sh +++ b/t/t2070-restore.sh @@ -137,11 +137,78 @@ test_expect_success 'restore --staged invalidates cache tree for deletions' ' test_must_fail git rev-parse HEAD:new1 ' -test_expect_success 'restore with merge options rejects --staged' ' +test_expect_success 'restore --merge to unresolve' ' + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + { + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" + } | git update-index --index-info && + echo nothing >file && + git restore --worktree --merge file && + cat >expect <<-\EOF && + <<<<<<< ours + ourside + ======= + theirside + >>>>>>> theirs + EOF + test_cmp expect file +' + +test_expect_success 'restore --merge to unresolve after (mistaken) resolution' ' + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + { + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" + } | git update-index --index-info && + echo nothing >file && + git add file && + git restore --worktree --merge file && + cat >expect <<-\EOF && + <<<<<<< ours + ourside + ======= + theirside + >>>>>>> theirs + EOF + test_cmp expect file +' + +test_expect_success 'restore --merge to unresolve after (mistaken) resolution' ' + O=$(echo original | git hash-object -w --stdin) && + A=$(echo ourside | git hash-object -w --stdin) && + B=$(echo theirside | git hash-object -w --stdin) && + { + echo "100644 $O 1 file" && + echo "100644 $A 2 file" && + echo "100644 $B 3 file" + } | git update-index --index-info && + git rm -f file && + git restore --worktree --merge file && + cat >expect <<-\EOF && + <<<<<<< ours + ourside + ======= + theirside + >>>>>>> theirs + EOF + test_cmp expect file +' + +test_expect_success 'restore with merge options are incompatible with certain options' ' for opts in \ "--staged --ours" \ "--staged --theirs" \ "--staged --merge" \ + "--source=HEAD --ours" \ + "--source=HEAD --theirs" \ + "--source=HEAD --merge" \ "--staged --conflict=diff3" \ "--staged --worktree --ours" \ "--staged --worktree --theirs" \ @@ -149,7 +216,7 @@ test_expect_success 'restore with merge options rejects --staged' ' "--staged --worktree --conflict=zdiff3" do test_must_fail git restore $opts . 2>err && - grep "cannot be used with --staged" err || return + grep "cannot be used" err || return done ' diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh index b5f4d6a653..b33afa1c6a 100755 --- a/t/t3206-range-diff.sh +++ b/t/t3206-range-diff.sh @@ -662,6 +662,20 @@ test_expect_success 'range-diff with multiple --notes' ' test_cmp expect actual ' +# `range-diff` should act like `log` with regards to notes +test_expect_success 'range-diff with --notes=custom does not show default notes' ' + git notes add -m "topic note" topic && + git notes add -m "unmodified note" unmodified && + git notes --ref=custom add -m "topic note" topic && + git notes --ref=custom add -m "unmodified note" unmodified && + test_when_finished git notes remove topic unmodified && + test_when_finished git notes --ref=custom remove topic unmodified && + git range-diff --notes=custom main..topic main..unmodified \ + >actual && + ! grep "## Notes ##" actual && + grep "## Notes (custom) ##" actual +' + test_expect_success 'format-patch --range-diff does not compare notes by default' ' git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && @@ -679,6 +693,20 @@ test_expect_success 'format-patch --range-diff does not compare notes by default ! grep "note" 0000-* ' +test_expect_success 'format-patch --notes=custom --range-diff only compares custom notes' ' + git notes add -m "topic note" topic && + git notes --ref=custom add -m "topic note (custom)" topic && + git notes add -m "unmodified note" unmodified && + git notes --ref=custom add -m "unmodified note (custom)" unmodified && + test_when_finished git notes remove topic unmodified && + test_when_finished git notes --ref=custom remove topic unmodified && + git format-patch --notes=custom --cover-letter --range-diff=$prev \ + main..unmodified >actual && + test_when_finished "rm 000?-*" && + grep "## Notes (custom) ##" 0000-* && + ! grep "## Notes ##" 0000-* +' + test_expect_success 'format-patch --range-diff with --no-notes' ' git notes add -m "topic note" topic && git notes add -m "unmodified note" unmodified && diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh index 3ee27e277d..beb2ec2a55 100755 --- a/t/t4052-stat-output.sh +++ b/t/t4052-stat-output.sh @@ -49,12 +49,41 @@ log -1 --stat EOF cat >expect.60 <<-'EOF' - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF cat >expect.6030 <<-'EOF' ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF -cat >expect2.60 <<-'EOF' +while read verb expect cmd args +do + # No width limit applied when statNameWidth is ignored + case "$expect" in expect72|expect.6030) + test_expect_success "$cmd $verb statNameWidth config with long name" ' + git -c diff.statNameWidth=30 $cmd $args >output && + grep " | " output >actual && + test_cmp $expect actual + ';; + esac + # Maximum width limit still applied when statNameWidth is ignored + case "$expect" in expect.60|expect.6030) + test_expect_success "$cmd --stat=width $verb statNameWidth config with long name" ' + git -c diff.statNameWidth=30 $cmd $args --stat=60 >output && + grep " | " output >actual && + test_cmp $expect actual + ';; + esac +done <<\EOF +ignores expect72 format-patch -1 --stdout +ignores expect.60 format-patch -1 --stdout +respects expect.6030 diff HEAD^ HEAD --stat +respects expect.6030 show --stat +respects expect.6030 log -1 --stat +EOF + +cat >expect.40 <<-'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + +EOF +cat >expect2.40 <<-'EOF' ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF @@ -67,16 +96,16 @@ do test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" ' git $cmd $args --stat=40 >output && grep " | " output >actual && - test_cmp $expect.60 actual + test_cmp $expect.40 actual ' test_expect_success "$cmd --stat-width=width with long name" ' git $cmd $args --stat-width=40 >output && grep " | " output >actual && - test_cmp $expect.60 actual + test_cmp $expect.40 actual ' - test_expect_success "$cmd --stat=...,name-width with long name" ' + test_expect_success "$cmd --stat=width,name-width with long name" ' git $cmd $args --stat=60,30 >output && grep " | " output >actual && test_cmp $expect.6030 actual @@ -94,7 +123,6 @@ expect show --stat expect log -1 --stat EOF - test_expect_success 'preparation for big change tests' ' >abcd && git add abcd && @@ -207,7 +235,6 @@ respects expect40 show --stat respects expect40 log -1 --stat EOF - cat >expect <<'EOF' abcd | 1000 ++++++++++++++++++++++++++ EOF diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh index 9677180a5b..72f8c1722f 100755 --- a/t/t6406-merge-attr.sh +++ b/t/t6406-merge-attr.sh @@ -179,7 +179,8 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' >./please-abort && echo "* merge=custom" >.gitattributes && - test_must_fail git merge main && + test_must_fail git merge main 2>err && + grep "^error: failed to execute internal merge" err && git ls-files -u >output && git diff --name-only HEAD >>output && test_must_be_empty output diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 35b9e6ed6b..ebf273e843 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -497,6 +497,11 @@ test_expect_success 'checkout unmerged stage' ' test ztheirside = "z$(cat file)" ' +test_expect_success 'checkout path with --merge from tree-ish is a no-no' ' + setup_conflicting_index && + test_must_fail git checkout -m HEAD -- file +' + test_expect_success 'checkout with --merge' ' setup_conflicting_index && echo "none of the above" >sample && @@ -517,6 +522,48 @@ test_expect_success 'checkout with --merge' ' test_cmp merged file ' +test_expect_success 'checkout -m works after (mistaken) resolution' ' + setup_conflicting_index && + echo "none of the above" >sample && + cat sample >fild && + cat sample >file && + cat sample >filf && + # resolve to something + git add file && + git checkout --merge -- fild file filf && + { + echo "<<<<<<< ours" && + echo ourside && + echo "=======" && + echo theirside && + echo ">>>>>>> theirs" + } >merged && + test_cmp expect fild && + test_cmp expect filf && + test_cmp merged file +' + +test_expect_success 'checkout -m works after (mistaken) resolution to remove' ' + setup_conflicting_index && + echo "none of the above" >sample && + cat sample >fild && + cat sample >file && + cat sample >filf && + # resolve to remove + git rm file && + git checkout --merge -- fild file filf && + { + echo "<<<<<<< ours" && + echo ourside && + echo "=======" && + echo theirside && + echo ">>>>>>> theirs" + } >merged && + test_cmp expect fild && + test_cmp expect filf && + test_cmp merged file +' + test_expect_success 'checkout with --merge, in diff3 -m style' ' git config merge.conflictstyle diff3 && setup_conflicting_index && diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 47e20fb8b1..a7c3b4eb63 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -2464,6 +2464,24 @@ test_expect_success 'completion used <cmd> completion for alias: !f() { : git <c EOF ' +test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd> ; ... }' ' + test_config alias.co "!f() { : checkout ; if ... } f" && + test_completion "git co m" <<-\EOF + main Z + mybranch Z + mytag Z + EOF +' + +test_expect_success 'completion used <cmd> completion for alias: !f() { : <cmd>; ... }' ' + test_config alias.co "!f() { : checkout; if ... } f" && + test_completion "git co m" <<-\EOF + main Z + mybranch Z + mytag Z + EOF +' + test_expect_success 'completion without explicit _git_xxx function' ' test_completion "git version --" <<-\EOF --build-options Z diff --git a/t/test-lib.sh b/t/test-lib.sh index 5ea5d1d62a..1656c9eed0 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -89,6 +89,9 @@ prepend_var LSAN_OPTIONS : $GIT_SAN_OPTIONS prepend_var LSAN_OPTIONS : fast_unwind_on_malloc=0 export LSAN_OPTIONS +prepend_var UBSAN_OPTIONS : $GIT_SAN_OPTIONS +export UBSAN_OPTIONS + if test ! -f "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS then echo >&2 'error: GIT-BUILD-OPTIONS missing (has Git been built?).' @@ -711,30 +711,35 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val, list_add_tail(&new_item->list, arg_head); } -static void process_command_line_args(struct list_head *arg_head, - struct list_head *new_trailer_head) +static void parse_trailers_from_config(struct list_head *config_head) { struct arg_item *item; - struct strbuf tok = STRBUF_INIT; - struct strbuf val = STRBUF_INIT; - const struct conf_info *conf; struct list_head *pos; - /* - * In command-line arguments, '=' is accepted (in addition to the - * separators that are defined). - */ - char *cl_separators = xstrfmt("=%s", separators); - /* Add an arg item for each configured trailer with a command */ list_for_each(pos, &conf_head) { item = list_entry(pos, struct arg_item, list); if (item->conf.command) - add_arg_item(arg_head, + add_arg_item(config_head, xstrdup(token_from_item(item, NULL)), xstrdup(""), &item->conf, NULL); } +} + +static void parse_trailers_from_command_line_args(struct list_head *arg_head, + struct list_head *new_trailer_head) +{ + struct strbuf tok = STRBUF_INIT; + struct strbuf val = STRBUF_INIT; + const struct conf_info *conf; + struct list_head *pos; + + /* + * In command-line arguments, '=' is accepted (in addition to the + * separators that are defined). + */ + char *cl_separators = xstrfmt("=%s", separators); /* Add an arg item for each trailer on the command line */ list_for_each(pos, new_trailer_head) { @@ -961,28 +966,24 @@ static void unfold_value(struct strbuf *val) strbuf_release(&out); } -static size_t process_input_file(FILE *outfile, - const char *str, - struct list_head *head, - const struct process_trailer_options *opts) +/* + * Parse trailers in "str", populating the trailer info and "head" + * linked list structure. + */ +static void parse_trailers(struct trailer_info *info, + const char *str, + struct list_head *head, + const struct process_trailer_options *opts) { - struct trailer_info info; struct strbuf tok = STRBUF_INIT; struct strbuf val = STRBUF_INIT; size_t i; - trailer_info_get(&info, str, opts); - - /* Print lines before the trailers as is */ - if (!opts->only_trailers) - fwrite(str, 1, info.trailer_start - str, outfile); + trailer_info_get(info, str, opts); - if (!opts->only_trailers && !info.blank_line_before_trailer) - fprintf(outfile, "\n"); - - for (i = 0; i < info.trailer_nr; i++) { + for (i = 0; i < info->trailer_nr; i++) { int separator_pos; - char *trailer = info.trailers[i]; + char *trailer = info->trailers[i]; if (trailer[0] == comment_line_char) continue; separator_pos = find_separator(trailer, separators); @@ -1002,10 +1003,6 @@ static size_t process_input_file(FILE *outfile, strbuf_detach(&val, NULL)); } } - - trailer_info_release(&info); - - return info.trailer_end - str; } static void free_all(struct list_head *head) @@ -1054,6 +1051,7 @@ void process_trailers(const char *file, { LIST_HEAD(head); struct strbuf sb = STRBUF_INIT; + struct trailer_info info; size_t trailer_end; FILE *outfile = stdout; @@ -1064,18 +1062,30 @@ void process_trailers(const char *file, if (opts->in_place) outfile = create_in_place_tempfile(file); + parse_trailers(&info, sb.buf, &head, opts); + trailer_end = info.trailer_end - sb.buf; + /* Print the lines before the trailers */ - trailer_end = process_input_file(outfile, sb.buf, &head, opts); + if (!opts->only_trailers) + fwrite(sb.buf, 1, info.trailer_start - sb.buf, outfile); + + if (!opts->only_trailers && !info.blank_line_before_trailer) + fprintf(outfile, "\n"); + if (!opts->only_input) { + LIST_HEAD(config_head); LIST_HEAD(arg_head); - process_command_line_args(&arg_head, new_trailer_head); + parse_trailers_from_config(&config_head); + parse_trailers_from_command_line_args(&arg_head, new_trailer_head); + list_splice(&config_head, &arg_head); process_trailers_lists(&head, &arg_head); } print_all(outfile, &head, opts); free_all(&head); + trailer_info_release(&info); /* Print the lines after the trailers as is */ if (!opts->only_trailers) @@ -1220,14 +1230,14 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg) strbuf_init(&iter->key, 0); strbuf_init(&iter->val, 0); opts.no_divider = 1; - trailer_info_get(&iter->info, msg, &opts); - iter->cur = 0; + trailer_info_get(&iter->internal.info, msg, &opts); + iter->internal.cur = 0; } int trailer_iterator_advance(struct trailer_iterator *iter) { - while (iter->cur < iter->info.trailer_nr) { - char *trailer = iter->info.trailers[iter->cur++]; + while (iter->internal.cur < iter->internal.info.trailer_nr) { + char *trailer = iter->internal.info.trailers[iter->internal.cur++]; int separator_pos = find_separator(trailer, separators); if (separator_pos < 1) @@ -1245,7 +1255,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter) void trailer_iterator_release(struct trailer_iterator *iter) { - trailer_info_release(&iter->info); + trailer_info_release(&iter->internal.info); strbuf_release(&iter->val); strbuf_release(&iter->key); } @@ -119,8 +119,10 @@ struct trailer_iterator { struct strbuf val; /* private */ - struct trailer_info info; - size_t cur; + struct { + struct trailer_info info; + size_t cur; + } internal; }; /* |
