From 02d900361c2b1e5d1fab006d2e71c8dc8bda8ca1 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 11 Nov 2024 02:01:34 -0500 Subject: [PATCH 01/14] test-lib: check malloc debug LD_PRELOAD before using This fixes test failures across the suite on glibc platforms that don't have libc_malloc_debug.so.0. We added support for glibc's malloc checking routines long ago in a731fa916e (Add MALLOC_CHECK_ and MALLOC_PERTURB_ libc env to the test suite for detecting heap corruption, 2012-09-14). Back then we didn't need to do any checks to see if the platform supported it. We were just setting some environment variables which would either enable it or not. That changed in 131b94a10a (test-lib.sh: Use GLIBC_TUNABLES instead of MALLOC_CHECK_ on glibc >= 2.34, 2022-03-04). Now that glibc split this out into libc_malloc_debug.so, we have to add it to LD_PRELOAD. We only do that when we detect glibc, but it's possible to have glibc but not the malloc debug library. In that case LD_PRELOAD will complain to stderr, and tests which check for an empty stderr will fail. You can work around this by setting TEST_NO_MALLOC_CHECK, which disables the feature entirely. But it's not obvious to know you need to do that. Instead, since this malloc checking is best-effort anyway, let's just automatically disable it when the LD_PRELOAD appears not to work. We can check it by running something simple that should work (and produce nothing on stderr) like "git version". Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/test-lib.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index b1a8ee5c00..e715d07f39 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -593,9 +593,12 @@ then } else _USE_GLIBC_TUNABLES= + _USE_GLIBC_PRELOAD=libc_malloc_debug.so.0 if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && - expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null + expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null && + stderr=$(LD_PRELOAD=$_USE_GLIBC_PRELOAD git version 2>&1 >/dev/null) && + test -z "$stderr" then _USE_GLIBC_TUNABLES=YesPlease fi @@ -607,7 +610,7 @@ else if test -n "$_USE_GLIBC_TUNABLES" then g= - LD_PRELOAD="libc_malloc_debug.so.0" + LD_PRELOAD=$_USE_GLIBC_PRELOAD for t in \ glibc.malloc.check=1 \ glibc.malloc.perturb=165 From b970509c59f3d5d684129dec9237ceb2e3084e1d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 12 Nov 2024 03:34:00 -0500 Subject: [PATCH 02/14] fetch: adjust refspec->raw_nr when filtering prefetch refspecs In filter_prefetch_refspecs(), we may remove one or more refspecs if they point into refs/tags/. When we do, we remove the item from the refspec->items array, shifting subsequent items down, and then decrement the refspec->nr count. We also remove the item from the refspec->raw array, but fail to decrement refspec->raw_nr. This leaves us with a count that is too high, and anybody looking at the "raw" array will erroneously see either: 1. The removed entry, if there were no subsequent items to shift down. 2. A duplicate of the final entry, as everything is shifted down but there was nothing to overwrite the final item. The obvious culprit to run into this is calling refspec_clear(), which will try to free the removed entry (case 1) or double-free the final entry (case 2). But even though the bug has existed since the function was added in 2e03115d0c (fetch: add --prefetch option, 2021-04-16), we did not trigger it in the test suite. The --prefetch option is normally only used with configured refspecs, and we never bother to call refspec_clear() on those (they are stored as part of a struct remote, which is held in a global variable). But you could trigger case 2 manually like: git fetch --prefetch . refs/tags/foo refs/tags/bar Ironically you couldn't trigger case 1, because the code accidentally leaked the string in the raw array, and the two bugs (the leak and the double-free) cancelled out. But when we fixed the leak in ea4780307c (fetch: free "raw" string when shrinking refspec, 2024-09-24), it became possible to trigger that, too, with a single item: git fetch --prefetch . refs/tags/foo We can fix both cases by just correctly decrementing "raw_nr" when we shrink the array. Even though we don't expect people to use --prefetch with command-line refspecs, we'll add a test to make sure it behaves well (like the test just before it, we're just confirming that the filtered prefetch succeeds at all). Reported-by: Eric Mills Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/fetch.c | 1 + t/t5582-fetch-negative-refspec.sh | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/builtin/fetch.c b/builtin/fetch.c index 80a64d0d26..1c59e1c256 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -463,6 +463,7 @@ static void filter_prefetch_refspec(struct refspec *rs) rs->raw[j - 1] = rs->raw[j]; } rs->nr--; + rs->raw_nr--; i--; continue; } diff --git a/t/t5582-fetch-negative-refspec.sh b/t/t5582-fetch-negative-refspec.sh index 7fa54a4029..b21bf2057d 100755 --- a/t/t5582-fetch-negative-refspec.sh +++ b/t/t5582-fetch-negative-refspec.sh @@ -283,4 +283,8 @@ test_expect_success '--prefetch succeeds when refspec becomes empty' ' git -C one fetch --prefetch ' +test_expect_success '--prefetch succeeds with empty command line refspec' ' + git -C one fetch --prefetch origin +refs/tags/extra +' + test_done From d36af33081cac89a840f4a4cbc4af1ba4aa40392 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 12 Nov 2024 03:36:10 -0500 Subject: [PATCH 03/14] refspec: drop separate raw_nr count A refspec struct contains zero or more refspec_item structs, along with matching "raw" strings. The items and raw strings are kept in separate arrays, but those arrays will always have the same length (because we write them only via refspec_append_nodup(), which grows both). This can lead to bugs when manipulating the array, since the arrays and lengths must be modified in lockstep. For example, the bug fixed in the previous commit, which forgot to decrement raw_nr. So let's get rid of "raw_nr" and have only "nr", making this kind of bug impossible (and also making it clear that the two are always matched, something that existing code already assumed but was not guaranteed by the interface). Even though we'd expect "alloc" and "raw_alloc" to likewise move in lockstep, we still need to keep separate counts there if we want to continue to use ALLOC_GROW() for both. Conceptually this would all be simpler if refspec_item just held onto its own raw string, and we had a single array. But there are callers which use refspec_item outside of "struct refspec" (and so don't hold on to a matching "raw" string at all), which we'd possibly need to adjust. So let's not worry about refactoring that for now, and just get rid of the redundant count variable. That is the first step on the road to combining them anyway. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/fetch.c | 1 - builtin/remote.c | 8 ++++---- refspec.c | 15 ++++++++------- refspec.h | 1 - submodule.c | 4 ++-- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 1c59e1c256..80a64d0d26 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -463,7 +463,6 @@ static void filter_prefetch_refspec(struct refspec *rs) rs->raw[j - 1] = rs->raw[j]; } rs->nr--; - rs->raw_nr--; i--; continue; } diff --git a/builtin/remote.c b/builtin/remote.c index 76670ddd8b..875d6c3bad 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -633,11 +633,11 @@ static int migrate_file(struct remote *remote) git_config_set_multivar(buf.buf, remote->url.v[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); - for (i = 0; i < remote->push.raw_nr; i++) + for (i = 0; i < remote->push.nr; i++) git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch.raw_nr; i++) + for (i = 0; i < remote->fetch.nr; i++) git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0); if (remote->origin == REMOTE_REMOTES) unlink_or_warn(git_path("remotes/%s", remote->name)); @@ -759,12 +759,12 @@ static int mv(int argc, const char **argv, const char *prefix) goto out; } - if (oldremote->fetch.raw_nr) { + if (oldremote->fetch.nr) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.raw_nr; i++) { + for (i = 0; i < oldremote->fetch.nr; i++) { char *ptr; strbuf_reset(&buf2); diff --git a/refspec.c b/refspec.c index c3cf003443..8e8ee8542d 100644 --- a/refspec.c +++ b/refspec.c @@ -186,10 +186,12 @@ static void refspec_append_nodup(struct refspec *rs, char *refspec) refspec_item_init_or_die(&item, refspec, rs->fetch); ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc); - rs->items[rs->nr++] = item; + rs->items[rs->nr] = item; - ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc); - rs->raw[rs->raw_nr++] = refspec; + ALLOC_GROW(rs->raw, rs->nr + 1, rs->raw_alloc); + rs->raw[rs->nr] = refspec; + + rs->nr++; } void refspec_append(struct refspec *rs, const char *refspec) @@ -217,18 +219,17 @@ void refspec_clear(struct refspec *rs) { int i; - for (i = 0; i < rs->nr; i++) + for (i = 0; i < rs->nr; i++) { refspec_item_clear(&rs->items[i]); + free(rs->raw[i]); + } FREE_AND_NULL(rs->items); rs->alloc = 0; rs->nr = 0; - for (i = 0; i < rs->raw_nr; i++) - free(rs->raw[i]); FREE_AND_NULL(rs->raw); rs->raw_alloc = 0; - rs->raw_nr = 0; rs->fetch = 0; } diff --git a/refspec.h b/refspec.h index 3760fdaf2b..0461c9def6 100644 --- a/refspec.h +++ b/refspec.h @@ -45,7 +45,6 @@ struct refspec { char **raw; int raw_alloc; - int raw_nr; int fetch; }; diff --git a/submodule.c b/submodule.c index 74d5766f07..307f73fb5b 100644 --- a/submodule.c +++ b/submodule.c @@ -1174,7 +1174,7 @@ static int push_submodule(const char *path, if (remote->origin != REMOTE_UNCONFIGURED) { int i; strvec_push(&cp.args, remote->name); - for (i = 0; i < rs->raw_nr; i++) + for (i = 0; i < rs->nr; i++) strvec_push(&cp.args, rs->raw[i]); } @@ -1209,7 +1209,7 @@ static void submodule_push_check(const char *path, const char *head, strvec_push(&cp.args, head); strvec_push(&cp.args, remote->name); - for (i = 0; i < rs->raw_nr; i++) + for (i = 0; i < rs->nr; i++) strvec_push(&cp.args, rs->raw[i]); prepare_submodule_repo_env(&cp.env); From fe17a25905f701ce91505851eb2bb213bb39edbe Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 12 Nov 2024 03:39:37 -0500 Subject: [PATCH 04/14] refspec: store raw refspecs inside refspec_item The refspec struct keeps two matched arrays: one for the refspec_item structs and one for the original raw refspec strings. The main reason for this is that there are other users of refspec_item that do not care about the raw strings. But it does make managing the refspec struct awkward, as we must keep the two arrays in sync. This has led to bugs in the past (both leaks and double-frees). Let's just store a copy of the raw refspec string directly in each refspec_item struct. This simplifies the handling at a small cost: 1. Direct callers of refspec_item_init() will now get an extra copy of the refspec string, even if they don't need it. This should be negligible, as the struct is already allocating two strings for the parsed src/dst values (and we tend to only do it sparingly anyway for things like the TAG_REFSPEC literal). 2. Users of refspec_appendf() will now generate a temporary string, copy it, and then free the result (versus handing off ownership of the temporary string). We could get around this by having a "nodup" variant of refspec_item_init(), but it doesn't seem worth the extra complexity for something that is not remotely a hot code path. Code which accesses refspec->raw now needs to look at refspec->item.raw. Other callers which just use refspec_item directly can remain the same. We'll free the allocated string in refspec_item_clear(), which they should be calling anyway to free src/dst. One subtle note: refspec_item_init() can return an error, in which case we'll still have set its "raw" field. But that is also true of the "src" and "dst" fields, so any caller which does not _clear() the failed item is already potentially leaking. In practice most code just calls die() on an error anyway, but you can see the exception in valid_fetch_refspec(), which does correctly call _clear() even on error. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/fetch.c | 8 ++------ builtin/remote.c | 8 ++++---- refspec.c | 25 +++++++++---------------- refspec.h | 5 ++--- submodule.c | 4 ++-- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/builtin/fetch.c b/builtin/fetch.c index 80a64d0d26..ed273be62c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -454,14 +454,10 @@ static void filter_prefetch_refspec(struct refspec *rs) ref_namespace[NAMESPACE_TAGS].ref))) { int j; - free(rs->items[i].src); - free(rs->items[i].dst); - free(rs->raw[i]); + refspec_item_clear(&rs->items[i]); - for (j = i + 1; j < rs->nr; j++) { + for (j = i + 1; j < rs->nr; j++) rs->items[j - 1] = rs->items[j]; - rs->raw[j - 1] = rs->raw[j]; - } rs->nr--; i--; continue; diff --git a/builtin/remote.c b/builtin/remote.c index 875d6c3bad..9093600965 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -377,7 +377,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat for (i = 0; i < states->remote->fetch.nr; i++) if (get_fetch_map(remote_refs, &states->remote->fetch.items[i], &tail, 1)) die(_("Could not get fetch map for refspec %s"), - states->remote->fetch.raw[i]); + states->remote->fetch.items[i].raw); for (ref = fetch_map; ref; ref = ref->next) { if (omit_name_by_refspec(ref->name, &states->remote->fetch)) @@ -634,11 +634,11 @@ static int migrate_file(struct remote *remote) strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); for (i = 0; i < remote->push.nr; i++) - git_config_set_multivar(buf.buf, remote->push.raw[i], "^$", 0); + git_config_set_multivar(buf.buf, remote->push.items[i].raw, "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); for (i = 0; i < remote->fetch.nr; i++) - git_config_set_multivar(buf.buf, remote->fetch.raw[i], "^$", 0); + git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0); if (remote->origin == REMOTE_REMOTES) unlink_or_warn(git_path("remotes/%s", remote->name)); else if (remote->origin == REMOTE_BRANCHES) @@ -768,7 +768,7 @@ static int mv(int argc, const char **argv, const char *prefix) char *ptr; strbuf_reset(&buf2); - strbuf_addstr(&buf2, oldremote->fetch.raw[i]); + strbuf_addstr(&buf2, oldremote->fetch.items[i].raw); ptr = strstr(buf2.buf, old_remote_context.buf); if (ptr) { refspec_updated = 1; diff --git a/refspec.c b/refspec.c index 8e8ee8542d..994901f55b 100644 --- a/refspec.c +++ b/refspec.c @@ -153,6 +153,7 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch) { memset(item, 0, sizeof(*item)); + item->raw = xstrdup(refspec); return parse_refspec(item, refspec, fetch); } @@ -167,6 +168,7 @@ void refspec_item_clear(struct refspec_item *item) { FREE_AND_NULL(item->src); FREE_AND_NULL(item->dst); + FREE_AND_NULL(item->raw); item->force = 0; item->pattern = 0; item->matching = 0; @@ -179,7 +181,7 @@ void refspec_init(struct refspec *rs, int fetch) rs->fetch = fetch; } -static void refspec_append_nodup(struct refspec *rs, char *refspec) +void refspec_append(struct refspec *rs, const char *refspec) { struct refspec_item item; @@ -188,24 +190,20 @@ static void refspec_append_nodup(struct refspec *rs, char *refspec) ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc); rs->items[rs->nr] = item; - ALLOC_GROW(rs->raw, rs->nr + 1, rs->raw_alloc); - rs->raw[rs->nr] = refspec; - rs->nr++; } -void refspec_append(struct refspec *rs, const char *refspec) -{ - refspec_append_nodup(rs, xstrdup(refspec)); -} - void refspec_appendf(struct refspec *rs, const char *fmt, ...) { va_list ap; + char *buf; va_start(ap, fmt); - refspec_append_nodup(rs, xstrvfmt(fmt, ap)); + buf = xstrvfmt(fmt, ap); va_end(ap); + + refspec_append(rs, buf); + free(buf); } void refspec_appendn(struct refspec *rs, const char **refspecs, int nr) @@ -219,18 +217,13 @@ void refspec_clear(struct refspec *rs) { int i; - for (i = 0; i < rs->nr; i++) { + for (i = 0; i < rs->nr; i++) refspec_item_clear(&rs->items[i]); - free(rs->raw[i]); - } FREE_AND_NULL(rs->items); rs->alloc = 0; rs->nr = 0; - FREE_AND_NULL(rs->raw); - rs->raw_alloc = 0; - rs->fetch = 0; } diff --git a/refspec.h b/refspec.h index 0461c9def6..69d693c87d 100644 --- a/refspec.h +++ b/refspec.h @@ -26,6 +26,8 @@ struct refspec_item { char *src; char *dst; + + char *raw; }; #define REFSPEC_FETCH 1 @@ -43,9 +45,6 @@ struct refspec { int alloc; int nr; - char **raw; - int raw_alloc; - int fetch; }; diff --git a/submodule.c b/submodule.c index 307f73fb5b..7ec564854d 100644 --- a/submodule.c +++ b/submodule.c @@ -1175,7 +1175,7 @@ static int push_submodule(const char *path, int i; strvec_push(&cp.args, remote->name); for (i = 0; i < rs->nr; i++) - strvec_push(&cp.args, rs->raw[i]); + strvec_push(&cp.args, rs->items[i].raw); } prepare_submodule_repo_env(&cp.env); @@ -1210,7 +1210,7 @@ static void submodule_push_check(const char *path, const char *head, strvec_push(&cp.args, remote->name); for (i = 0; i < rs->nr; i++) - strvec_push(&cp.args, rs->raw[i]); + strvec_push(&cp.args, rs->items[i].raw); prepare_submodule_repo_env(&cp.env); cp.git_cmd = 1; From 98e40155936e32a1d827f510f4d8c37f0eb3aef1 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Tue, 12 Nov 2024 11:22:57 -0500 Subject: [PATCH 05/14] builtin/difftool: intialize some hashmap variables When running a dir-diff command that produces no diff, variables `wt_modified` and `tmp_modified` are used while uninitialized, causing: $ /home/smarchi/src/git/git-difftool --dir-diff master free(): invalid pointer [1] 334004 IOT instruction (core dumped) /home/smarchi/src/git/git-difftool --dir-diff master $ valgrind --track-origins=yes /home/smarchi/src/git/git-difftool --dir-diff master ... Invalid free() / delete / delete[] / realloc() at 0x48478EF: free (vg_replace_malloc.c:989) by 0x422CAC: hashmap_clear_ (hashmap.c:208) by 0x283830: run_dir_diff (difftool.c:667) by 0x284103: cmd_difftool (difftool.c:801) by 0x238E0F: run_builtin (git.c:484) by 0x2392B9: handle_builtin (git.c:750) by 0x2399BC: cmd_main (git.c:921) by 0x356FEF: main (common-main.c:64) Address 0x1ffefff180 is on thread 1's stack in frame #2, created by run_dir_diff (difftool.c:358) ... If taking any `goto finish` path before these variables are initialized, `hashmap_clear_and_free()` operates on uninitialized data, sometimes causing a crash. This regression was introduced in 7f795a1715 (builtin/difftool: plug several trivial memory leaks, 2024-09-26). Fix it by initializing those variables with the `HASHMAP_INIT` macro. Add a test comparing the main branch to itself, resulting in no diff. Signed-off-by: Simon Marchi Signed-off-by: Junio C Hamano --- builtin/difftool.c | 6 ++---- t/t7800-difftool.sh | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/builtin/difftool.c b/builtin/difftool.c index ca1b089065..40e971e225 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -376,7 +376,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, struct checkout lstate, rstate; int err = 0; struct child_process cmd = CHILD_PROCESS_INIT; - struct hashmap wt_modified, tmp_modified; + struct hashmap wt_modified = HASHMAP_INIT(path_entry_cmp, NULL); + struct hashmap tmp_modified = HASHMAP_INIT(path_entry_cmp, NULL); int indices_loaded = 0; workdir = repo_get_work_tree(the_repository); @@ -601,9 +602,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, * in the common case of --symlinks and the difftool updating * files through the symlink. */ - hashmap_init(&wt_modified, path_entry_cmp, NULL, wtindex.cache_nr); - hashmap_init(&tmp_modified, path_entry_cmp, NULL, wtindex.cache_nr); - for (i = 0; i < wtindex.cache_nr; i++) { struct hashmap_entry dummy; const char *name = wtindex.cache[i]->name; diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index f67b9345b8..fcaa1748e4 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -666,6 +666,10 @@ run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' ' test_cmp expect file ' +run_dir_diff_test 'difftool --dir-diff with no diff' ' + git difftool -d main main +' + write_script modify-file <<\EOF echo "new content" >file EOF From 72ad6dc368892803c761fe3d82b98eb2555e4fbc Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 13 Nov 2024 20:39:12 -0500 Subject: [PATCH 06/14] test-lib: move malloc-debug setup after $PATH setup Originally, the conditional definition of the setup/teardown functions for malloc checking could be run at any time, because they depended only on command-line options and the system getconf function. But since 02d900361c (test-lib: check malloc debug LD_PRELOAD before using, 2024-11-11), we probe the system by running "git version". Since this code runs before we've set $PATH to point to the version of Git we intend to test, we actually run the system version of git. This mostly works, since what we really care about is whether the LD_PRELOAD works, and it should work the same with any program. But there are some corner cases: 1. You might not have a system git at all, in which case the preload will appear to fail, even though it could work with the actual built version of git. 2. Your system git could be linked in a different way. For example, if it was built statically, then it will ignore LD_PRELOAD entirely, and we might assume that the preload works, even though it might not when used with a dynamic build. We could give a more complete path to the version of Git we intend to test, but features like GIT_TEST_INSTALLED make that not entirely trivial. So instead, let's just bump the setup until after we've set up the $PATH. There's no need for us to do it early, as long as it is done before the first test runs. Reported-by: Toon Claes Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/test-lib.sh | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index e715d07f39..424f0b16af 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -577,56 +577,6 @@ case $GIT_TEST_FSYNC in ;; esac -# Add libc MALLOC and MALLOC_PERTURB test only if we are not executing -# the test with valgrind and have not compiled with conflict SANITIZE -# options. -if test -n "$valgrind" || - test -n "$SANITIZE_ADDRESS" || - test -n "$SANITIZE_LEAK" || - test -n "$TEST_NO_MALLOC_CHECK" -then - setup_malloc_check () { - : nothing - } - teardown_malloc_check () { - : nothing - } -else - _USE_GLIBC_TUNABLES= - _USE_GLIBC_PRELOAD=libc_malloc_debug.so.0 - if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && - _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && - expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null && - stderr=$(LD_PRELOAD=$_USE_GLIBC_PRELOAD git version 2>&1 >/dev/null) && - test -z "$stderr" - then - _USE_GLIBC_TUNABLES=YesPlease - fi - setup_malloc_check () { - local g - local t - MALLOC_CHECK_=3 MALLOC_PERTURB_=165 - export MALLOC_CHECK_ MALLOC_PERTURB_ - if test -n "$_USE_GLIBC_TUNABLES" - then - g= - LD_PRELOAD=$_USE_GLIBC_PRELOAD - for t in \ - glibc.malloc.check=1 \ - glibc.malloc.perturb=165 - do - g="${g#:}:$t" - done - GLIBC_TUNABLES=$g - export LD_PRELOAD GLIBC_TUNABLES - fi - } - teardown_malloc_check () { - unset MALLOC_CHECK_ MALLOC_PERTURB_ - unset LD_PRELOAD GLIBC_TUNABLES - } -fi - # Protect ourselves from common misconfiguration to export # CDPATH into the environment unset CDPATH @@ -1486,6 +1436,56 @@ GIT_ATTR_NOSYSTEM=1 GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/.." export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM GIT_CEILING_DIRECTORIES +# Add libc MALLOC and MALLOC_PERTURB test only if we are not executing +# the test with valgrind and have not compiled with conflict SANITIZE +# options. +if test -n "$valgrind" || + test -n "$SANITIZE_ADDRESS" || + test -n "$SANITIZE_LEAK" || + test -n "$TEST_NO_MALLOC_CHECK" +then + setup_malloc_check () { + : nothing + } + teardown_malloc_check () { + : nothing + } +else + _USE_GLIBC_TUNABLES= + _USE_GLIBC_PRELOAD=libc_malloc_debug.so.0 + if _GLIBC_VERSION=$(getconf GNU_LIBC_VERSION 2>/dev/null) && + _GLIBC_VERSION=${_GLIBC_VERSION#"glibc "} && + expr 2.34 \<= "$_GLIBC_VERSION" >/dev/null && + stderr=$(LD_PRELOAD=$_USE_GLIBC_PRELOAD git version 2>&1 >/dev/null) && + test -z "$stderr" + then + _USE_GLIBC_TUNABLES=YesPlease + fi + setup_malloc_check () { + local g + local t + MALLOC_CHECK_=3 MALLOC_PERTURB_=165 + export MALLOC_CHECK_ MALLOC_PERTURB_ + if test -n "$_USE_GLIBC_TUNABLES" + then + g= + LD_PRELOAD=$_USE_GLIBC_PRELOAD + for t in \ + glibc.malloc.check=1 \ + glibc.malloc.perturb=165 + do + g="${g#:}:$t" + done + GLIBC_TUNABLES=$g + export LD_PRELOAD GLIBC_TUNABLES + fi + } + teardown_malloc_check () { + unset MALLOC_CHECK_ MALLOC_PERTURB_ + unset LD_PRELOAD GLIBC_TUNABLES + } +fi + if test -z "$GIT_TEST_CMP" then if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT" From 57f35cfd7c6e9c7f7ab59e650280e09f85618e33 Mon Sep 17 00:00:00 2001 From: Taylor Blau Date: Thu, 14 Nov 2024 08:42:09 -0500 Subject: [PATCH 07/14] t5332-multi-pack-reuse.sh: demonstrate duplicate packing failure In the multi-pack reuse code, there are two paths for reusing the on-disk representation of an object, handled by: - builtin/pack-objects.c::write_reused_pack_one() - builtin/pack-objects.c::write_reused_pack_verbatim() The former is responsible for copying the bytes for a single object out of an existing source pack. The latter does the same but for a region of objects aligned at eword_t boundaries. Demonstrate a bug whereby write_reused_pack_verbatim() can be tricked into writing out objects from some source pack, even when those objects were selected from a different source pack in the MIDX bitmap. When the caller wants at least one of the objects in that region, pack-objects will write the same object twice as a result of this bug. In the other case where the caller doesn't want any of the objects in the region of interest, we will write out objects that weren't requested. Demonstrate this bug by creating two packs, where the preferred one of those packs contains a single object which also appears in the main (non-preferred) pack. A separate bug[^1] prevents us from triggering the main bug when the duplicated object is the last one in the main pack, but any earlier object will suffice. We could fix that separate bug, but the following commit will simplify write_reused_pack_verbatim() and only call it on the preferred pack, so doing so would have little point. [^1]: Because write_reused_pack_verbatim() only reuses bits in the range off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0); off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p, pos - reuse_packfile->bitmap_pos); written += pos - reuse_packfile->bitmap_pos; /* We're recording one chunk, not one object. */ record_reused_object(pack_start_off, pack_start_off - (hashfile_total(out) - pack_start)); , or in other words excluding the object beginning at position 'pos - reuse_packfile->bitmap_pos' in the source pack. But since reuse_packfile->bitmap_pos is '1' in the non-preferred pack (accounting for the single-object pack which is preferred), we don't actually copy the bytes from the last object. Helped-by: Jeff King Signed-off-by: Taylor Blau Signed-off-by: Junio C Hamano --- t/t5332-multi-pack-reuse.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh index 955ea42769..d87ea0ae19 100755 --- a/t/t5332-multi-pack-reuse.sh +++ b/t/t5332-multi-pack-reuse.sh @@ -259,4 +259,26 @@ test_expect_success 'duplicate objects' ' ) ' +test_expect_failure 'duplicate objects with verbatim reuse' ' + git init duplicate-objects-verbatim && + ( + cd duplicate-objects-verbatim && + + git config pack.allowPackReuse multi && + + test_commit_bulk 64 && + + # take the first object from the main pack... + git show-index <$(ls $packdir/pack-*.idx) >obj.raw && + sort -nk1 in && + + # ...and create a separate pack containing just that object + p="$(git pack-objects $packdir/pack Date: Thu, 14 Nov 2024 08:42:12 -0500 Subject: [PATCH 08/14] pack-objects: only perform verbatim reuse on the preferred pack When reusing objects from source pack(s), write_reused_pack_verbatim() is responsible for reusing objects whole eword_t's at a time. It works by taking the longest continuous run of objects from the beginning of each source pack that the caller wants, and reuses the entirety of that section from each pack. This is based on the assumption that we don't have any gaps within the region. This assumption relieves us from having to patch any OFS_DELTAs, since we know that there aren't any gaps between any delta and its base in that region. To illustrate why this assumption is necessary, suppose we have some pack P, which has objects X, Y, and Z. If the MIDX's copy of Y was selected from a pack other than P, then the bit corresponding to object Y will appear earlier in the bitmap than the bits corresponding to X and Z. If pack-objects already has or will use the copy of Y from the pack it was selected from in the MIDX, then it is an error to reuse all objects between X and Z in the source pack. Doing so will cause us to reuse Y from a different pack than the one which represents Y in the MIDX, causing us to either: - include the object twice, assuming that the caller wants Y in the pack, or - include the object once, resulting in us packing more objects than necessary. This regression comes from ca0fd69e37 (pack-objects: prepare `write_reused_pack_verbatim()` for multi-pack reuse, 2023-12-14), which incorrectly assumed that there would be no gaps in reusable regions of non-preferred packs. Instead, we can only safely perform the whole-word reuse optimization on the preferred pack, where we know with certainty that no gaps exist in that region of the bitmap. We can still reuse objects from non-preferred packs, but we have to inspect them individually in write_reused_pack() to ensure that any gaps that may exist are accounted for. This allows us to simplify the implementation of write_reused_pack_verbatim() back to almost its pre-multi-pack reuse form, since we can now assume that the beginning of the pack appears at the beginning of the bitmap, meaning that we don't have to account for any bits up to the first word boundary (like we had to special case in ca0fd69e37). The only significant changes from the pre-ca0fd69e37 implementation are: - that we can no longer inspect words up to the end of reuse_packfile_bitmap->word_alloc, since we only want to look at words whose bits all correspond to objects in the given packfile, and - that we return early when given a reuse_packfile which is not preferred, making the call a noop. In the future, it might be possible to restore this optimization if we could guarantee that some reuse packs don't contain any gaps by construction (similar to the "disjoint packs" idea in very early versions of multi-pack reuse). Helped-by: Jeff King Signed-off-by: Taylor Blau Signed-off-by: Junio C Hamano --- builtin/pack-objects.c | 95 ++++++++++++++++--------------------- t/t5332-multi-pack-reuse.sh | 2 +- 2 files changed, 41 insertions(+), 56 deletions(-) diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 0fc0680b40..7cbed8b256 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -1100,78 +1100,64 @@ static void write_reused_pack_one(struct packed_git *reuse_packfile, static size_t write_reused_pack_verbatim(struct bitmapped_pack *reuse_packfile, struct hashfile *out, - off_t pack_start, struct pack_window **w_curs) { - size_t pos = reuse_packfile->bitmap_pos; + size_t pos = 0; size_t end; - if (pos % BITS_IN_EWORD) { - size_t word_pos = (pos / BITS_IN_EWORD); - size_t offset = pos % BITS_IN_EWORD; - size_t last; - eword_t word = reuse_packfile_bitmap->words[word_pos]; - - if (offset + reuse_packfile->bitmap_nr < BITS_IN_EWORD) - last = offset + reuse_packfile->bitmap_nr; - else - last = BITS_IN_EWORD; - - for (; offset < last; offset++) { - if (word >> offset == 0) - return word_pos; - if (!bitmap_get(reuse_packfile_bitmap, - word_pos * BITS_IN_EWORD + offset)) - return word_pos; - } - - pos += BITS_IN_EWORD - (pos % BITS_IN_EWORD); + if (reuse_packfile->bitmap_pos) { + /* + * We can't reuse whole chunks verbatim out of + * non-preferred packs since we can't guarantee that + * all duplicate objects were resolved in favor of + * that pack. + * + * Even if we have a whole eword_t worth of bits that + * could be reused, there may be objects between the + * objects corresponding to the first and last bit of + * that word which were selected from a different + * pack, causing us to send duplicate or unwanted + * objects. + * + * Handle non-preferred packs from within + * write_reused_pack(), which inspects and reuses + * individual bits. + */ + return reuse_packfile->bitmap_pos / BITS_IN_EWORD; } /* - * Now we're going to copy as many whole eword_t's as possible. - * "end" is the index of the last whole eword_t we copy, but - * there may be additional bits to process. Those are handled - * individually by write_reused_pack(). + * Only read through the last word whose bits all correspond + * to objects in the given packfile, since we must stop at a + * word boundary. * - * Begin by advancing to the first word boundary in range of the - * bit positions occupied by objects in "reuse_packfile". Then - * pick the last word boundary in the same range. If we have at - * least one word's worth of bits to process, continue on. + * If there is no whole word to read (i.e. the packfile + * contains fewer than BITS_IN_EWORD objects), then we'll + * inspect bits one-by-one in write_reused_pack(). */ - end = reuse_packfile->bitmap_pos + reuse_packfile->bitmap_nr; - if (end % BITS_IN_EWORD) - end -= end % BITS_IN_EWORD; - if (pos >= end) - return reuse_packfile->bitmap_pos / BITS_IN_EWORD; + end = reuse_packfile->bitmap_nr / BITS_IN_EWORD; + if (reuse_packfile_bitmap->word_alloc < end) + BUG("fewer words than expected in reuse_packfile_bitmap"); - while (pos < end && - reuse_packfile_bitmap->words[pos / BITS_IN_EWORD] == (eword_t)~0) - pos += BITS_IN_EWORD; + while (pos < end && reuse_packfile_bitmap->words[pos] == (eword_t)~0) + pos++; - if (pos > end) - pos = end; + if (pos) { + off_t to_write; - if (reuse_packfile->bitmap_pos < pos) { - off_t pack_start_off = pack_pos_to_offset(reuse_packfile->p, 0); - off_t pack_end_off = pack_pos_to_offset(reuse_packfile->p, - pos - reuse_packfile->bitmap_pos); - - written += pos - reuse_packfile->bitmap_pos; + written = (pos * BITS_IN_EWORD); + to_write = pack_pos_to_offset(reuse_packfile->p, written) + - sizeof(struct pack_header); /* We're recording one chunk, not one object. */ - record_reused_object(pack_start_off, - pack_start_off - (hashfile_total(out) - pack_start)); + record_reused_object(sizeof(struct pack_header), 0); hashflush(out); copy_pack_data(out, reuse_packfile->p, w_curs, - pack_start_off, pack_end_off - pack_start_off); + sizeof(struct pack_header), to_write); display_progress(progress_state, written); } - if (pos % BITS_IN_EWORD) - BUG("attempted to jump past a word boundary to %"PRIuMAX, - (uintmax_t)pos); - return pos / BITS_IN_EWORD; + return pos; } static void write_reused_pack(struct bitmapped_pack *reuse_packfile, @@ -1183,8 +1169,7 @@ static void write_reused_pack(struct bitmapped_pack *reuse_packfile, struct pack_window *w_curs = NULL; if (allow_ofs_delta) - i = write_reused_pack_verbatim(reuse_packfile, f, pack_start, - &w_curs); + i = write_reused_pack_verbatim(reuse_packfile, f, &w_curs); for (; i < reuse_packfile_bitmap->word_alloc; ++i) { eword_t word = reuse_packfile_bitmap->words[i]; diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh index d87ea0ae19..e9d80186ec 100755 --- a/t/t5332-multi-pack-reuse.sh +++ b/t/t5332-multi-pack-reuse.sh @@ -259,7 +259,7 @@ test_expect_success 'duplicate objects' ' ) ' -test_expect_failure 'duplicate objects with verbatim reuse' ' +test_expect_success 'duplicate objects with verbatim reuse' ' git init duplicate-objects-verbatim && ( cd duplicate-objects-verbatim && From 04eaff62f286226f501dd21f069e0e257aee11a6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 22 Nov 2024 14:00:48 +0900 Subject: [PATCH 09/14] The eleventh batch Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.48.0.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/RelNotes/2.48.0.txt b/Documentation/RelNotes/2.48.0.txt index 33f297ccd2..b885050c34 100644 --- a/Documentation/RelNotes/2.48.0.txt +++ b/Documentation/RelNotes/2.48.0.txt @@ -145,6 +145,20 @@ Fixes since v2.47 repository. Work it around by including these objects in the referring promisor pack at the receiving end of the fetch. + * Avoid build/test breakage on a system without working malloc debug + support dynamic library. + (merge 72ad6dc368 jk/test-malloc-debug-check later to maint). + + * Double-free fix. + (merge fe17a25905 jk/fetch-prefetch-double-free-fix later to maint). + + * Use of some uninitialized variables in "git difftool" has been + corrected. + + * Object reuse code based on multi-pack-index sent an unwanted copy + of object. + (merge e199290592 tb/multi-pack-reuse-dupfix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 1164e270b5 jk/output-prefix-cleanup later to maint). (merge f36b8cbaef jh/config-unset-doc-fix later to maint). From 7539e569efb401a6301245bad48aedd47e8d405b Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Fri, 24 Nov 2023 03:35:12 +0000 Subject: [PATCH 10/14] doc: update links to current pages It's somewhat traditional to respect sites' self-identification. Signed-off-by: Josh Soref Signed-off-by: Junio C Hamano Cherry-picked-from: 65175d9ea26bebeb9d69977d0e75efc0e88dbced Signed-off-by: Johannes Sixt --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index df3ba2ea99..dd80ab8f3b 100755 --- a/gitk +++ b/gitk @@ -12472,7 +12472,7 @@ if {[tk windowingsystem] eq "aqua"} { catch { # follow the XDG base directory specification by default. See - # http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + # https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if {[info exists env(XDG_CONFIG_HOME)] && $env(XDG_CONFIG_HOME) ne ""} { # XDG_CONFIG_HOME environment variable is set set config_file [file join $env(XDG_CONFIG_HOME) git gitk] From ed87b13a504a323c19e9f823fa06ed191a7be62f Mon Sep 17 00:00:00 2001 From: Josh Soref Date: Fri, 24 Nov 2023 03:35:13 +0000 Subject: [PATCH 11/14] doc: switch links to https These sites offer https versions of their content. Using the https versions provides some protection for users. Signed-off-by: Josh Soref Signed-off-by: Junio C Hamano Cherry-picked-from: d05b08cd52cfda627f1d865bdfe6040a2c9521b5 Signed-off-by: Johannes Sixt --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index dd80ab8f3b..7a087f123d 100755 --- a/gitk +++ b/gitk @@ -11956,7 +11956,7 @@ proc formatdate {d} { } # This list of encoding names and aliases is distilled from -# http://www.iana.org/assignments/character-sets. +# https://www.iana.org/assignments/character-sets. # Not all of them are supported by Tcl. set encoding_aliases { { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII From c18400c6bb04f4e8875c1930db72ddd9b94649ca Mon Sep 17 00:00:00 2001 From: Taylor Blau Date: Mon, 8 Apr 2024 11:51:44 -0400 Subject: [PATCH 12/14] Makefile(s): avoid recipe prefix in conditional statements In GNU Make commit 07fcee35 ([SV 64815] Recipe lines cannot contain conditional statements, 2023-05-22) and following, conditional statements may no longer be preceded by a tab character (which Make refers to as the recipe prefix). There are a handful of spots in our various Makefile(s) which will break in a future release of Make containing 07fcee35. For instance, trying to compile the pre-image of this patch with the tip of make.git results in the following: $ make -v | head -1 && make GNU Make 4.4.90 config.mak.uname:842: *** missing 'endif'. Stop. The kernel addressed this issue in 82175d1f9430 (kbuild: Replace tabs with spaces when followed by conditionals, 2024-01-28). Address the issues in Git's tree by applying the same strategy. When a conditional word (ifeq, ifneq, ifdef, etc.) is preceded by one or more tab characters, replace each tab character with 8 space characters with the following: find . -type f -not -path './.git/*' -name Makefile -or -name '*.mak' | xargs perl -i -pe ' s/(\t+)(ifn?eq|ifn?def|else|endif)/" " x (length($1) * 8) . $2/ge unless /\\$/ ' The "unless /\\$/" removes any false-positives (like "\telse \" appearing within a shell script as part of a recipe). After doing so, Git compiles on newer versions of Make: $ make -v | head -1 && make GNU Make 4.4.90 GIT_VERSION = 2.44.0.414.gfac1dc44ca9 [...] $ echo $? 0 Reported-by: Dario Gjorgjevski Signed-off-by: Taylor Blau Signed-off-by: Junio C Hamano Cherry-picked-from: 728b9ac0c3b93aaa4ea80280c591deb198051785 Signed-off-by: Johannes Sixt --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5bdd52a6eb..e1f0aff4a1 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,9 @@ ifdef NO_MSGFMT MSGFMT ?= $(TCL_PATH) po/po2msg.sh else MSGFMT ?= msgfmt - ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0) + ifneq ($(shell $(MSGFMT) --tcl -l C -d . /dev/null 2>/dev/null; echo $$?),0) MSGFMT := $(TCL_PATH) po/po2msg.sh - endif + endif endif PO_TEMPLATE = po/gitk.pot From 92999a42db1c5f43f330e4f2bca4026b5b81576f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 Nov 2024 12:32:21 +0900 Subject: [PATCH 13/14] Git 2.47.1 Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.47.1.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/RelNotes/2.47.1.txt b/Documentation/RelNotes/2.47.1.txt index 6d7a4d98fb..39206c09fd 100644 --- a/Documentation/RelNotes/2.47.1.txt +++ b/Documentation/RelNotes/2.47.1.txt @@ -24,3 +24,8 @@ Fixes since Git 2.47 * A "git fetch" from the superproject going down to a submodule used a wrong remote when the default remote names are set differently between them. + + * The "gitk" project tree has been synchronized again with its new + maintainer, Johannes Sixt. + +Also contains minor documentation updates and code clean-ups. From 6f33d8e255cb2ff738cd28eab22751efb7c2d6ce Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Mon, 25 Nov 2024 15:55:30 +0100 Subject: [PATCH 14/14] builtin: pass repository to sub commands In 9b1cb5070f (builtin: add a repository parameter for builtin functions, 2024-09-13) the repository was passed down to all builtin commands. This allowed the repository to be passed down to lower layers without depending on the global `the_repository` variable. Continue this work by also passing down the repository parameter from the command to sub-commands. This will help pass down the repository to other subsystems and cleanup usage of global variables like 'the_repository' and 'the_hash_algo'. Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano --- builtin/bisect.c | 32 +++++++++++++++--------- builtin/bundle.c | 16 +++++++----- builtin/commit-graph.c | 10 +++++--- builtin/config.c | 25 ++++++++++++------- builtin/gc.c | 21 ++++++++++------ builtin/hook.c | 7 +++--- builtin/multi-pack-index.c | 16 +++++++----- builtin/notes.c | 36 +++++++++++++++++---------- builtin/reflog.c | 17 ++++++++----- builtin/refs.c | 10 +++++--- builtin/remote.c | 34 +++++++++++++++++--------- builtin/sparse-checkout.c | 25 ++++++++++++------- builtin/stash.c | 39 ++++++++++++++++++----------- builtin/submodule--helper.c | 46 +++++++++++++++++++++++------------ builtin/worktree.c | 28 +++++++++++++-------- parse-options.h | 4 ++- t/helper/test-parse-options.c | 8 +++--- 17 files changed, 239 insertions(+), 135 deletions(-) diff --git a/builtin/bisect.c b/builtin/bisect.c index 21d17a6c1a..8166d4abf5 100644 --- a/builtin/bisect.c +++ b/builtin/bisect.c @@ -1312,7 +1312,8 @@ static int bisect_run(struct bisect_terms *terms, int argc, const char **argv) return res; } -static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { if (argc > 1) return error(_("'%s' requires either no argument or a commit"), @@ -1320,7 +1321,8 @@ static int cmd_bisect__reset(int argc, const char **argv, const char *prefix UNU return bisect_reset(argc ? argv[0] : NULL); } -static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1333,7 +1335,8 @@ static int cmd_bisect__terms(int argc, const char **argv, const char *prefix UNU return res; } -static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1344,7 +1347,8 @@ static int cmd_bisect__start(int argc, const char **argv, const char *prefix UNU return res; } -static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *prefix) +static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *prefix, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1358,12 +1362,15 @@ static int cmd_bisect__next(int argc, const char **argv UNUSED, const char *pref return res; } -static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, const char *prefix UNUSED) +static int cmd_bisect__log(int argc UNUSED, const char **argv UNUSED, + const char *prefix UNUSED, + struct repository *repo UNUSED) { return bisect_log(); } -static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1376,7 +1383,8 @@ static int cmd_bisect__replay(int argc, const char **argv, const char *prefix UN return res; } -static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1388,7 +1396,8 @@ static int cmd_bisect__skip(int argc, const char **argv, const char *prefix UNUS return res; } -static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1399,7 +1408,8 @@ static int cmd_bisect__visualize(int argc, const char **argv, const char *prefix return res; } -static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSED) +static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int res; struct bisect_terms terms = { 0 }; @@ -1415,7 +1425,7 @@ static int cmd_bisect__run(int argc, const char **argv, const char *prefix UNUSE int cmd_bisect(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { int res = 0; parse_opt_subcommand_fn *fn = NULL; @@ -1451,7 +1461,7 @@ int cmd_bisect(int argc, } else { argc--; argv++; - res = fn(argc, argv, prefix); + res = fn(argc, argv, prefix, repo); } return is_bisect_success(res) ? 0 : -res; diff --git a/builtin/bundle.c b/builtin/bundle.c index 127518c2a8..3f14754197 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -67,7 +67,8 @@ static int parse_options_cmd_bundle(int argc, return argc; } -static int cmd_bundle_create(int argc, const char **argv, const char *prefix) { +static int cmd_bundle_create(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct strvec pack_opts = STRVEC_INIT; int version = -1; int ret; @@ -123,7 +124,8 @@ static int open_bundle(const char *path, struct bundle_header *header, return read_bundle_header(path, header); } -static int cmd_bundle_verify(int argc, const char **argv, const char *prefix) { +static int cmd_bundle_verify(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct bundle_header header = BUNDLE_HEADER_INIT; int bundle_fd = -1; int quiet = 0; @@ -164,7 +166,8 @@ cleanup: return ret; } -static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix) { +static int cmd_bundle_list_heads(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct bundle_header header = BUNDLE_HEADER_INIT; int bundle_fd = -1; int ret; @@ -189,7 +192,8 @@ cleanup: return ret; } -static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix) { +static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct bundle_header header = BUNDLE_HEADER_INIT; int bundle_fd = -1; int ret; @@ -231,7 +235,7 @@ cleanup: int cmd_bundle(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option options[] = { @@ -247,5 +251,5 @@ int cmd_bundle(int argc, packet_trace_identity("bundle"); - return !!fn(argc, argv, prefix); + return !!fn(argc, argv, prefix, repo); } diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 7c991db6eb..bd70d052e7 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -62,7 +62,8 @@ static struct option *add_common_options(struct option *to) return parse_options_concat(common_opts, to); } -static int graph_verify(int argc, const char **argv, const char *prefix) +static int graph_verify(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct commit_graph *graph = NULL; struct object_directory *odb = NULL; @@ -214,7 +215,8 @@ static int git_commit_graph_write_config(const char *var, const char *value, return 0; } -static int graph_write(int argc, const char **argv, const char *prefix) +static int graph_write(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct string_list pack_indexes = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; @@ -333,7 +335,7 @@ cleanup: int cmd_commit_graph(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option builtin_commit_graph_options[] = { @@ -352,5 +354,5 @@ int cmd_commit_graph(int argc, builtin_commit_graph_usage, 0); FREE_AND_NULL(options); - return fn(argc, argv, prefix); + return fn(argc, argv, prefix, repo); } diff --git a/builtin/config.c b/builtin/config.c index cba7022108..16e6e30555 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -826,7 +826,8 @@ static void display_options_init(struct config_display_options *opts) } } -static int cmd_config_list(int argc, const char **argv, const char *prefix) +static int cmd_config_list(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT; @@ -861,7 +862,8 @@ static int cmd_config_list(int argc, const char **argv, const char *prefix) return 0; } -static int cmd_config_get(int argc, const char **argv, const char *prefix) +static int cmd_config_get(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct config_display_options display_opts = CONFIG_DISPLAY_OPTIONS_INIT; @@ -915,7 +917,8 @@ static int cmd_config_get(int argc, const char **argv, const char *prefix) return ret; } -static int cmd_config_set(int argc, const char **argv, const char *prefix) +static int cmd_config_set(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; const char *value_pattern = NULL, *comment_arg = NULL; @@ -973,7 +976,8 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix) return ret; } -static int cmd_config_unset(int argc, const char **argv, const char *prefix) +static int cmd_config_unset(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; const char *value_pattern = NULL; @@ -1010,7 +1014,8 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix) return ret; } -static int cmd_config_rename_section(int argc, const char **argv, const char *prefix) +static int cmd_config_rename_section(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct option opts[] = { @@ -1039,7 +1044,8 @@ out: return ret; } -static int cmd_config_remove_section(int argc, const char **argv, const char *prefix) +static int cmd_config_remove_section(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct option opts[] = { @@ -1099,7 +1105,8 @@ static int show_editor(struct config_location_options *opts) return 0; } -static int cmd_config_edit(int argc, const char **argv, const char *prefix) +static int cmd_config_edit(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct config_location_options location_opts = CONFIG_LOCATION_OPTIONS_INIT; struct option opts[] = { @@ -1395,7 +1402,7 @@ out: int cmd_config(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *subcommand = NULL; struct option subcommand_opts[] = { @@ -1422,7 +1429,7 @@ int cmd_config(int argc, if (subcommand) { argc = parse_options(argc, argv, prefix, subcommand_opts, builtin_config_usage, PARSE_OPT_SUBCOMMAND_OPTIONAL|PARSE_OPT_KEEP_UNKNOWN_OPT); - return subcommand(argc, argv, prefix); + return subcommand(argc, argv, prefix, repo); } return cmd_config_actions(argc, argv, prefix); diff --git a/builtin/gc.c b/builtin/gc.c index d52735354c..a55f54c09f 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1561,7 +1561,8 @@ static int task_option_parse(const struct option *opt UNUSED, return 0; } -static int maintenance_run(int argc, const char **argv, const char *prefix) +static int maintenance_run(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i; struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT; @@ -1623,7 +1624,8 @@ static char const * const builtin_maintenance_register_usage[] = { NULL }; -static int maintenance_register(int argc, const char **argv, const char *prefix) +static int maintenance_register(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { char *config_file = NULL; struct option options[] = { @@ -1687,7 +1689,8 @@ static char const * const builtin_maintenance_unregister_usage[] = { NULL }; -static int maintenance_unregister(int argc, const char **argv, const char *prefix) +static int maintenance_unregister(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int force = 0; char *config_file = NULL; @@ -2917,7 +2920,8 @@ static const char *const builtin_maintenance_start_usage[] = { NULL }; -static int maintenance_start(int argc, const char **argv, const char *prefix) +static int maintenance_start(int argc, const char **argv, const char *prefix, + struct repository *repo) { struct maintenance_start_opts opts = { 0 }; struct option options[] = { @@ -2940,7 +2944,7 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) if (update_background_schedule(&opts, 1)) die(_("failed to set up maintenance schedule")); - if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL)) + if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL, repo)) warning(_("failed to add repo to global config")); return 0; } @@ -2950,7 +2954,8 @@ static const char *const builtin_maintenance_stop_usage[] = { NULL }; -static int maintenance_stop(int argc, const char **argv, const char *prefix) +static int maintenance_stop(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -2970,7 +2975,7 @@ static const char * const builtin_maintenance_usage[] = { int cmd_maintenance(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option builtin_maintenance_options[] = { @@ -2984,5 +2989,5 @@ int cmd_maintenance(int argc, argc = parse_options(argc, argv, prefix, builtin_maintenance_options, builtin_maintenance_usage, 0); - return fn(argc, argv, prefix); + return fn(argc, argv, prefix, repo); } diff --git a/builtin/hook.c b/builtin/hook.c index 367ef3e0b8..672d2e37e8 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -19,7 +19,8 @@ static const char * const builtin_hook_run_usage[] = { NULL }; -static int run(int argc, const char **argv, const char *prefix) +static int run(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i; struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; @@ -70,7 +71,7 @@ usage: int cmd_hook(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option builtin_hook_options[] = { @@ -81,5 +82,5 @@ int cmd_hook(int argc, argc = parse_options(argc, argv, NULL, builtin_hook_options, builtin_hook_usage, 0); - return fn(argc, argv, prefix); + return fn(argc, argv, prefix, repo); } diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index d159ed1314..85e40a4b6d 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -119,7 +119,8 @@ static void read_packs_from_stdin(struct string_list *to) } static int cmd_multi_pack_index_write(int argc, const char **argv, - const char *prefix) + const char *prefix, + struct repository *repo UNUSED) { struct option *options; static struct option builtin_multi_pack_index_write_options[] = { @@ -183,7 +184,8 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, } static int cmd_multi_pack_index_verify(int argc, const char **argv, - const char *prefix) + const char *prefix, + struct repository *repo UNUSED) { struct option *options; static struct option builtin_multi_pack_index_verify_options[] = { @@ -210,7 +212,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, } static int cmd_multi_pack_index_expire(int argc, const char **argv, - const char *prefix) + const char *prefix, + struct repository *repo UNUSED) { struct option *options; static struct option builtin_multi_pack_index_expire_options[] = { @@ -237,7 +240,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, } static int cmd_multi_pack_index_repack(int argc, const char **argv, - const char *prefix) + const char *prefix, + struct repository *repo UNUSED) { struct option *options; static struct option builtin_multi_pack_index_repack_options[] = { @@ -271,7 +275,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, int cmd_multi_pack_index(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { int res; parse_opt_subcommand_fn *fn = NULL; @@ -297,7 +301,7 @@ int cmd_multi_pack_index(int argc, builtin_multi_pack_index_usage, 0); FREE_AND_NULL(options); - res = fn(argc, argv, prefix); + res = fn(argc, argv, prefix, repo); free(opts.object_dir); return res; diff --git a/builtin/notes.c b/builtin/notes.c index 72c8a51cfa..d051abf6df 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -431,7 +431,8 @@ static struct notes_tree *init_notes_check(const char *subcommand, return t; } -static int list(int argc, const char **argv, const char *prefix) +static int list(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct notes_tree *t; struct object_id object; @@ -468,9 +469,11 @@ static int list(int argc, const char **argv, const char *prefix) return retval; } -static int append_edit(int argc, const char **argv, const char *prefix); +static int append_edit(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED); -static int add(int argc, const char **argv, const char *prefix) +static int add(int argc, const char **argv, const char *prefix, + struct repository *repo) { int force = 0, allow_empty = 0; const char *object_ref; @@ -543,7 +546,7 @@ static int add(int argc, const char **argv, const char *prefix) * argv[0-1]. */ argv[0] = "edit"; - return append_edit(argc, argv, prefix); + return append_edit(argc, argv, prefix, repo); } fprintf(stderr, _("Overwriting existing notes for object %s\n"), oid_to_hex(&object)); @@ -569,7 +572,8 @@ static int add(int argc, const char **argv, const char *prefix) return 0; } -static int copy(int argc, const char **argv, const char *prefix) +static int copy(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int retval = 0, force = 0, from_stdin = 0; const struct object_id *from_note, *note; @@ -646,7 +650,8 @@ out: return retval; } -static int append_edit(int argc, const char **argv, const char *prefix) +static int append_edit(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int allow_empty = 0; const char *object_ref; @@ -749,7 +754,8 @@ static int append_edit(int argc, const char **argv, const char *prefix) return 0; } -static int show(int argc, const char **argv, const char *prefix) +static int show(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { const char *object_ref; struct notes_tree *t; @@ -875,7 +881,8 @@ static int git_config_get_notes_strategy(const char *key, return 0; } -static int merge(int argc, const char **argv, const char *prefix) +static int merge(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT; struct object_id result_oid; @@ -1016,7 +1023,8 @@ static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag return (flag & IGNORE_MISSING) ? 0 : status; } -static int remove_cmd(int argc, const char **argv, const char *prefix) +static int remove_cmd(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { unsigned flag = 0; int from_stdin = 0; @@ -1059,7 +1067,8 @@ static int remove_cmd(int argc, const char **argv, const char *prefix) return retval; } -static int prune(int argc, const char **argv, const char *prefix) +static int prune(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct notes_tree *t; int show_only = 0, verbose = 0; @@ -1088,7 +1097,8 @@ static int prune(int argc, const char **argv, const char *prefix) return 0; } -static int get_ref(int argc, const char **argv, const char *prefix) +static int get_ref(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() }; char *notes_ref; @@ -1109,7 +1119,7 @@ static int get_ref(int argc, const char **argv, const char *prefix) int cmd_notes(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { const char *override_notes_ref = NULL; parse_opt_subcommand_fn *fn = NULL; @@ -1148,5 +1158,5 @@ int cmd_notes(int argc, strbuf_release(&sb); } - return !!fn(argc, argv, prefix); + return !!fn(argc, argv, prefix, repo); } diff --git a/builtin/reflog.c b/builtin/reflog.c index 22df6834f7..5a0c22f2f7 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -234,7 +234,8 @@ static int expire_total_callback(const struct option *opt, return 0; } -static int cmd_reflog_show(int argc, const char **argv, const char *prefix) +static int cmd_reflog_show(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -253,7 +254,8 @@ static int show_reflog(const char *refname, void *cb_data UNUSED) return 0; } -static int cmd_reflog_list(int argc, const char **argv, const char *prefix) +static int cmd_reflog_list(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -270,7 +272,8 @@ static int cmd_reflog_list(int argc, const char **argv, const char *prefix) return refs_for_each_reflog(ref_store, show_reflog, NULL); } -static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) +static int cmd_reflog_expire(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct cmd_reflog_expire_cb cmd = { 0 }; timestamp_t now = time(NULL); @@ -394,7 +397,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) return status; } -static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) +static int cmd_reflog_delete(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i, status = 0; unsigned int flags = 0; @@ -424,7 +428,8 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) return status; } -static int cmd_reflog_exists(int argc, const char **argv, const char *prefix) +static int cmd_reflog_exists(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -467,7 +472,7 @@ int cmd_reflog(int argc, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); if (fn) - return fn(argc - 1, argv + 1, prefix); + return fn(argc - 1, argv + 1, prefix, repository); else return cmd_log_reflog(argc, argv, prefix, repository); } diff --git a/builtin/refs.c b/builtin/refs.c index 24978a7b7b..3502980d21 100644 --- a/builtin/refs.c +++ b/builtin/refs.c @@ -12,7 +12,8 @@ #define REFS_VERIFY_USAGE \ N_("git refs verify [--strict] [--verbose]") -static int cmd_refs_migrate(int argc, const char **argv, const char *prefix) +static int cmd_refs_migrate(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { const char * const migrate_usage[] = { REFS_MIGRATE_USAGE, @@ -63,7 +64,8 @@ out: return err; } -static int cmd_refs_verify(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 fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT; const char * const verify_usage[] = { @@ -93,7 +95,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix) int cmd_refs(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { const char * const refs_usage[] = { REFS_MIGRATE_USAGE, @@ -108,5 +110,5 @@ int cmd_refs(int argc, }; argc = parse_options(argc, argv, prefix, opts, refs_usage, 0); - return fn(argc, argv, prefix); + return fn(argc, argv, prefix, repo); } diff --git a/builtin/remote.c b/builtin/remote.c index 9093600965..1ad3e70a6b 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -155,7 +155,8 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) return 0; } -static int add(int argc, const char **argv, const char *prefix) +static int add(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int fetch = 0, fetch_tags = TAGS_DEFAULT; unsigned mirror = MIRROR_NONE; @@ -706,7 +707,8 @@ static void handle_push_default(const char* old_name, const char* new_name) } -static int mv(int argc, const char **argv, const char *prefix) +static int mv(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int show_progress = isatty(2); struct option options[] = { @@ -881,7 +883,8 @@ out: return result; } -static int rm(int argc, const char **argv, const char *prefix) +static int rm(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -1303,7 +1306,8 @@ static int show_all(void) return result; } -static int show(int argc, const char **argv, const char *prefix) +static int show(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int no_query = 0, result = 0, query_flag = 0; struct option options[] = { @@ -1399,7 +1403,8 @@ static int show(int argc, const char **argv, const char *prefix) return result; } -static int set_head(int argc, const char **argv, const char *prefix) +static int set_head(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i, opt_a = 0, opt_d = 0, result = 0; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; @@ -1503,7 +1508,8 @@ static int prune_remote(const char *remote, int dry_run) return result; } -static int prune(int argc, const char **argv, const char *prefix) +static int prune(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int dry_run = 0, result = 0; struct option options[] = { @@ -1534,7 +1540,8 @@ static int get_remote_default(const char *key, const char *value UNUSED, return 0; } -static int update(int argc, const char **argv, const char *prefix) +static int update(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i, prune = -1; struct option options[] = { @@ -1616,7 +1623,8 @@ static int set_remote_branches(const char *remotename, const char **branches, return 0; } -static int set_branches(int argc, const char **argv, const char *prefix) +static int set_branches(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int add_mode = 0; struct option options[] = { @@ -1635,7 +1643,8 @@ static int set_branches(int argc, const char **argv, const char *prefix) return set_remote_branches(argv[0], argv + 1, add_mode); } -static int get_url(int argc, const char **argv, const char *prefix) +static int get_url(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i, push_mode = 0, all_mode = 0; const char *remotename = NULL; @@ -1674,7 +1683,8 @@ static int get_url(int argc, const char **argv, const char *prefix) return 0; } -static int set_url(int argc, const char **argv, const char *prefix) +static int set_url(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i, push_mode = 0, add_mode = 0, delete_mode = 0; int matches = 0, negative_matches = 0; @@ -1765,7 +1775,7 @@ out: int cmd_remote(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option options[] = { @@ -1788,7 +1798,7 @@ int cmd_remote(int argc, PARSE_OPT_SUBCOMMAND_OPTIONAL); if (fn) { - return !!fn(argc, argv, prefix); + return !!fn(argc, argv, prefix, repo); } else { if (argc) { error(_("unknown subcommand: `%s'"), argv[0]); diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 49aedc1de8..6f5fa5893b 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -48,7 +48,8 @@ static char const * const builtin_sparse_checkout_list_usage[] = { NULL }; -static int sparse_checkout_list(int argc, const char **argv, const char *prefix) +static int sparse_checkout_list(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { static struct option builtin_sparse_checkout_list_options[] = { OPT_END(), @@ -443,7 +444,8 @@ static struct sparse_checkout_init_opts { int sparse_index; } init_opts; -static int sparse_checkout_init(int argc, const char **argv, const char *prefix) +static int sparse_checkout_init(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct pattern_list pl; char *sparse_filename; @@ -770,7 +772,8 @@ static struct sparse_checkout_add_opts { int use_stdin; } add_opts; -static int sparse_checkout_add(int argc, const char **argv, const char *prefix) +static int sparse_checkout_add(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { static struct option builtin_sparse_checkout_add_options[] = { OPT_BOOL_F(0, "skip-checks", &add_opts.skip_checks, @@ -808,7 +811,8 @@ static struct sparse_checkout_set_opts { int use_stdin; } set_opts; -static int sparse_checkout_set(int argc, const char **argv, const char *prefix) +static int sparse_checkout_set(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int default_patterns_nr = 2; const char *default_patterns[] = {"/*", "!/*/", NULL}; @@ -866,7 +870,8 @@ static struct sparse_checkout_reapply_opts { } reapply_opts; static int sparse_checkout_reapply(int argc, const char **argv, - const char *prefix) + const char *prefix, + struct repository *repo UNUSED) { static struct option builtin_sparse_checkout_reapply_options[] = { OPT_BOOL(0, "cone", &reapply_opts.cone_mode, @@ -901,7 +906,8 @@ static char const * const builtin_sparse_checkout_disable_usage[] = { }; static int sparse_checkout_disable(int argc, const char **argv, - const char *prefix) + const char *prefix, + struct repository *repo UNUSED) { static struct option builtin_sparse_checkout_disable_options[] = { OPT_END(), @@ -989,7 +995,8 @@ static int check_rules(struct pattern_list *pl, int null_terminated) { return 0; } -static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix) +static int sparse_checkout_check_rules(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { static struct option builtin_sparse_checkout_check_rules_options[] = { OPT_BOOL('z', NULL, &check_rules_opts.null_termination, @@ -1037,7 +1044,7 @@ static int sparse_checkout_check_rules(int argc, const char **argv, const char * int cmd_sparse_checkout(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option builtin_sparse_checkout_options[] = { @@ -1060,5 +1067,5 @@ int cmd_sparse_checkout(int argc, prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - return fn(argc, argv, prefix); + return fn(argc, argv, prefix, repo); } diff --git a/builtin/stash.c b/builtin/stash.c index 1399a1bbe2..c212b1c0b2 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -249,7 +249,8 @@ static int do_clear_stash(void) ref_stash, &obj, 0); } -static int clear_stash(int argc, const char **argv, const char *prefix) +static int clear_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -652,7 +653,8 @@ restore_untracked: return ret; } -static int apply_stash(int argc, const char **argv, const char *prefix) +static int apply_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int ret = -1; int quiet = 0; @@ -726,7 +728,8 @@ static int get_stash_info_assert(struct stash_info *info, int argc, return 0; } -static int drop_stash(int argc, const char **argv, const char *prefix) +static int drop_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int ret = -1; int quiet = 0; @@ -748,7 +751,8 @@ cleanup: return ret; } -static int pop_stash(int argc, const char **argv, const char *prefix) +static int pop_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int ret = -1; int index = 0; @@ -778,7 +782,8 @@ cleanup: return ret; } -static int branch_stash(int argc, const char **argv, const char *prefix) +static int branch_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int ret = -1; const char *branch = NULL; @@ -816,7 +821,8 @@ cleanup: return ret; } -static int list_stash(int argc, const char **argv, const char *prefix) +static int list_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct child_process cp = CHILD_PROCESS_INIT; struct option options[] = { @@ -889,7 +895,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op do_diff_cache(&info->b_commit, diff_opt); } -static int show_stash(int argc, const char **argv, const char *prefix) +static int show_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i; int ret = -1; @@ -1017,7 +1024,8 @@ static int do_store_stash(const struct object_id *w_commit, const char *stash_ms return 0; } -static int store_stash(int argc, const char **argv, const char *prefix) +static int store_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int quiet = 0; const char *stash_msg = NULL; @@ -1491,7 +1499,8 @@ done: return ret; } -static int create_stash(int argc, const char **argv, const char *prefix UNUSED) +static int create_stash(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { int ret; struct strbuf stash_msg_buf = STRBUF_INIT; @@ -1827,12 +1836,14 @@ static int push_stash(int argc, const char **argv, const char *prefix, return ret; } -static int push_stash_unassumed(int argc, const char **argv, const char *prefix) +static int push_stash_unassumed(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { return push_stash(argc, argv, prefix, 0); } -static int save_stash(int argc, const char **argv, const char *prefix) +static int save_stash(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int keep_index = -1; int only_staged = 0; @@ -1878,7 +1889,7 @@ static int save_stash(int argc, const char **argv, const char *prefix) int cmd_stash(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { pid_t pid = getpid(); const char *index_file; @@ -1916,9 +1927,9 @@ int cmd_stash(int argc, (uintmax_t)pid); if (fn) - return !!fn(argc, argv, prefix); + return !!fn(argc, argv, prefix, repo); else if (!argc) - return !!push_stash_unassumed(0, NULL, prefix); + return !!push_stash_unassumed(0, NULL, prefix, repo); /* Assume 'stash push' */ strvec_push(&args, "push"); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index b6b5f1ebde..19e5878381 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -399,7 +399,8 @@ cleanup: free(displaypath); } -static int module_foreach(int argc, const char **argv, const char *prefix) +static int module_foreach(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct foreach_cb info = FOREACH_CB_INIT; struct pathspec pathspec = { 0 }; @@ -544,7 +545,8 @@ static void init_submodule_cb(const struct cache_entry *list_item, void *cb_data info->flags); } -static int module_init(int argc, const char **argv, const char *prefix) +static int module_init(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct init_cb info = INIT_CB_INIT; struct pathspec pathspec = { 0 }; @@ -738,7 +740,8 @@ static void status_submodule_cb(const struct cache_entry *list_item, info->prefix, info->super_prefix, info->flags); } -static int module_status(int argc, const char **argv, const char *prefix) +static int module_status(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct status_cb info = STATUS_CB_INIT; struct pathspec pathspec = { 0 }; @@ -1163,7 +1166,8 @@ cleanup: return ret; } -static int module_summary(int argc, const char **argv, const char *prefix) +static int module_summary(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct summary_cb info = SUMMARY_CB_INIT; int cached = 0; @@ -1339,7 +1343,8 @@ static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data info->flags); } -static int module_sync(int argc, const char **argv, const char *prefix) +static int module_sync(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct sync_cb info = SYNC_CB_INIT; struct pathspec pathspec = { 0 }; @@ -1485,7 +1490,8 @@ static void deinit_submodule_cb(const struct cache_entry *list_item, deinit_submodule(list_item->name, info->prefix, info->flags); } -static int module_deinit(int argc, const char **argv, const char *prefix) +static int module_deinit(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct deinit_cb info = DEINIT_CB_INIT; struct pathspec pathspec = { 0 }; @@ -1842,7 +1848,8 @@ static int clone_submodule(const struct module_clone_data *clone_data, return 0; } -static int module_clone(int argc, const char **argv, const char *prefix) +static int module_clone(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int dissociate = 0, quiet = 0, progress = 0, require_init = 0; struct module_clone_data clone_data = MODULE_CLONE_DATA_INIT; @@ -2779,7 +2786,8 @@ cleanup: return ret; } -static int module_update(int argc, const char **argv, const char *prefix) +static int module_update(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { struct pathspec pathspec = { 0 }; struct pathspec pathspec2 = { 0 }; @@ -2911,7 +2919,8 @@ cleanup: return ret; } -static int push_check(int argc, const char **argv, const char *prefix UNUSED) +static int push_check(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { struct remote *remote; const char *superproject_head; @@ -2991,7 +3000,8 @@ static int push_check(int argc, const char **argv, const char *prefix UNUSED) return 0; } -static int absorb_git_dirs(int argc, const char **argv, const char *prefix) +static int absorb_git_dirs(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int i; struct pathspec pathspec = { 0 }; @@ -3024,7 +3034,8 @@ cleanup: return ret; } -static int module_set_url(int argc, const char **argv, const char *prefix) +static int module_set_url(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int quiet = 0, ret; const char *newurl; @@ -3063,7 +3074,8 @@ static int module_set_url(int argc, const char **argv, const char *prefix) return !!ret; } -static int module_set_branch(int argc, const char **argv, const char *prefix) +static int module_set_branch(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int opt_default = 0, ret; const char *opt_branch = NULL; @@ -3113,7 +3125,8 @@ static int module_set_branch(int argc, const char **argv, const char *prefix) return !!ret; } -static int module_create_branch(int argc, const char **argv, const char *prefix) +static int module_create_branch(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { enum branch_track track; int quiet = 0, force = 0, reflog = 0, dry_run = 0; @@ -3424,7 +3437,8 @@ static void die_on_repo_without_commits(const char *path) strbuf_release(&sb); } -static int module_add(int argc, const char **argv, const char *prefix) +static int module_add(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) { int force = 0, quiet = 0, progress = 0, dissociate = 0; struct add_data add_data = ADD_DATA_INIT; @@ -3557,7 +3571,7 @@ cleanup: int cmd_submodule__helper(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; const char *const usage[] = { @@ -3583,5 +3597,5 @@ int cmd_submodule__helper(int argc, }; argc = parse_options(argc, argv, prefix, options, usage, 0); - return fn(argc, argv, prefix); + return fn(argc, argv, prefix, repo); } diff --git a/builtin/worktree.c b/builtin/worktree.c index dae63dedf4..824dd71d64 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -231,7 +231,8 @@ static void prune_worktrees(void) strbuf_release(&reason); } -static int prune(int ac, const char **av, const char *prefix) +static int prune(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), @@ -763,7 +764,8 @@ static char *dwim_branch(const char *path, char **new_branch) return NULL; } -static int add(int ac, const char **av, const char *prefix) +static int add(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { struct add_opts opts; const char *new_branch_force = NULL; @@ -1039,7 +1041,8 @@ static void pathsort(struct worktree **wt) QSORT(wt, n, pathcmp); } -static int list(int ac, const char **av, const char *prefix) +static int list(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { int porcelain = 0; int line_terminator = '\n'; @@ -1084,7 +1087,8 @@ static int list(int ac, const char **av, const char *prefix) return 0; } -static int lock_worktree(int ac, const char **av, const char *prefix) +static int lock_worktree(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { const char *reason = "", *old_reason; struct option options[] = { @@ -1119,7 +1123,8 @@ static int lock_worktree(int ac, const char **av, const char *prefix) return 0; } -static int unlock_worktree(int ac, const char **av, const char *prefix) +static int unlock_worktree(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { struct option options[] = { OPT_END() @@ -1182,7 +1187,8 @@ static void validate_no_submodules(const struct worktree *wt) die(_("working trees containing submodules cannot be moved or removed")); } -static int move_worktree(int ac, const char **av, const char *prefix) +static int move_worktree(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { int force = 0; struct option options[] = { @@ -1312,7 +1318,8 @@ static int delete_git_work_tree(struct worktree *wt) return ret; } -static int remove_worktree(int ac, const char **av, const char *prefix) +static int remove_worktree(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { int force = 0; struct option options[] = { @@ -1377,7 +1384,8 @@ static void report_repair(int iserr, const char *path, const char *msg, void *cb } } -static int repair(int ac, const char **av, const char *prefix) +static int repair(int ac, const char **av, const char *prefix, + struct repository *repo UNUSED) { const char **p; const char *self[] = { ".", NULL }; @@ -1397,7 +1405,7 @@ static int repair(int ac, const char **av, const char *prefix) int cmd_worktree(int ac, const char **av, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { parse_opt_subcommand_fn *fn = NULL; struct option options[] = { @@ -1422,5 +1430,5 @@ int cmd_worktree(int ac, prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - return fn(ac, av, prefix); + return fn(ac, av, prefix, repo); } diff --git a/parse-options.h b/parse-options.h index ae15342390..f0801d4532 100644 --- a/parse-options.h +++ b/parse-options.h @@ -3,6 +3,8 @@ #include "gettext.h" +struct repository; + /** * Refer to Documentation/technical/api-parse-options.txt for the API doc. */ @@ -73,7 +75,7 @@ typedef enum parse_opt_result parse_opt_ll_cb(struct parse_opt_ctx_t *ctx, const char *arg, int unset); typedef int parse_opt_subcommand_fn(int argc, const char **argv, - const char *prefix); + const char *prefix, struct repository *repo); /* * `type`:: diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 5250913d99..5da359486c 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -282,14 +282,16 @@ int cmd__parse_options_flags(int argc, const char **argv) return parse_options_flags__cmd(argc, argv, test_flags); } -static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED) +static int subcmd_one(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { printf("fn: subcmd_one\n"); print_args(argc, argv); return 0; } -static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED) +static int subcmd_two(int argc, const char **argv, const char *prefix UNUSED, + struct repository *repo UNUSED) { printf("fn: subcmd_two\n"); print_args(argc, argv); @@ -319,7 +321,7 @@ static int parse_subcommand__cmd(int argc, const char **argv, printf("opt: %d\n", opt); - return fn(argc, argv, NULL); + return fn(argc, argv, NULL, NULL); } int cmd__parse_subcommand(int argc, const char **argv)