From efa3d64ce86a600563c8bf909c46dd0985ee6c11 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 3 Sep 2021 11:06:37 +0200 Subject: [PATCH 01/71] update-ref: fix streaming of status updates When executing git-update-ref(1) with the `--stdin` flag, then the user can queue updates and, since e48cf33b61 (update-ref: implement interactive transaction handling, 2020-04-02), interactively drive the transaction's state via a set of transactional verbs. This interactivity is somewhat broken though: while the caller can use these verbs to drive the transaction's state, the status messages which confirm that a verb has been processed is not flushed. The caller may thus be left hanging waiting for the acknowledgement. Fix the bug by flushing stdout after writing the status update. Add a test which catches this bug. Signed-off-by: Patrick Steinhardt Signed-off-by: Junio C Hamano --- builtin/update-ref.c | 14 ++++++++++---- t/t1400-update-ref.sh | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 6029a80544..a84e7b47a2 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -302,6 +302,12 @@ static void parse_cmd_verify(struct ref_transaction *transaction, strbuf_release(&err); } +static void report_ok(const char *command) +{ + fprintf(stdout, "%s: ok\n", command); + fflush(stdout); +} + static void parse_cmd_option(struct ref_transaction *transaction, const char *next, const char *end) { @@ -317,7 +323,7 @@ static void parse_cmd_start(struct ref_transaction *transaction, { if (*next != line_termination) die("start: extra input: %s", next); - puts("start: ok"); + report_ok("start"); } static void parse_cmd_prepare(struct ref_transaction *transaction, @@ -328,7 +334,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction, die("prepare: extra input: %s", next); if (ref_transaction_prepare(transaction, &error)) die("prepare: %s", error.buf); - puts("prepare: ok"); + report_ok("prepare"); } static void parse_cmd_abort(struct ref_transaction *transaction, @@ -339,7 +345,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction, die("abort: extra input: %s", next); if (ref_transaction_abort(transaction, &error)) die("abort: %s", error.buf); - puts("abort: ok"); + report_ok("abort"); } static void parse_cmd_commit(struct ref_transaction *transaction, @@ -350,7 +356,7 @@ static void parse_cmd_commit(struct ref_transaction *transaction, die("commit: extra input: %s", next); if (ref_transaction_commit(transaction, &error)) die("commit: %s", error.buf); - puts("commit: ok"); + report_ok("commit"); ref_transaction_free(transaction); } diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 4506cd435b..1e754e258f 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1598,6 +1598,38 @@ test_expect_success 'transaction cannot restart ongoing transaction' ' test_must_fail git show-ref --verify refs/heads/restart ' +test_expect_success PIPE 'transaction flushes status updates' ' + mkfifo in out && + (git update-ref --stdin out &) && + + exec 9>in && + test_when_finished "exec 9>&-" && + + echo "start" >&9 && + echo "start: ok" >expected && + read line actual && + test_cmp expected actual && + + echo "create refs/heads/flush $A" >&9 && + + echo prepare >&9 && + echo "prepare: ok" >expected && + read line actual && + test_cmp expected actual && + + # This must now fail given that we have locked the ref. + test_must_fail git update-ref refs/heads/flush $B 2>stderr && + grep "fatal: update_ref failed for ref ${SQ}refs/heads/flush${SQ}: cannot lock ref" stderr && + + echo commit >&9 && + echo "commit: ok" >expected && + read line actual && + test_cmp expected actual +' + test_expect_success 'directory not created deleting packed ref' ' git branch d1/d2/r1 HEAD && git pack-refs --all && From 6346f704a00a2fc94cc2ca26dbe872b446500bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 10 Sep 2021 22:25:50 +0200 Subject: [PATCH 02/71] index-pack: use xopen in init_thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support an arbitrary file descriptor expression in the semantic patch for replacing open+die_errno with xopen, not just an identifier, and apply it. This makes the error message at the single affected place more consistent and reduces code duplication. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- builtin/index-pack.c | 4 +--- contrib/coccinelle/xopen.cocci | 17 ++++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 6cc4890217..738a35c53c 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -187,9 +187,7 @@ static void init_thread(void) pthread_key_create(&key, NULL); CALLOC_ARRAY(thread_data, nr_threads); for (i = 0; i < nr_threads; i++) { - thread_data[i].pack_fd = open(curr_pack, O_RDONLY); - if (thread_data[i].pack_fd == -1) - die_errno(_("unable to open %s"), curr_pack); + thread_data[i].pack_fd = xopen(curr_pack, O_RDONLY); } threads_active = 1; diff --git a/contrib/coccinelle/xopen.cocci b/contrib/coccinelle/xopen.cocci index 814d7b8a1a..b71db67019 100644 --- a/contrib/coccinelle/xopen.cocci +++ b/contrib/coccinelle/xopen.cocci @@ -2,15 +2,18 @@ identifier fd; identifier die_fn =~ "^(die|die_errno)$"; @@ -( - fd = -- open -+ xopen - (...); -| int fd = - open + xopen (...); -) +- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); } + +@@ +expression fd; +identifier die_fn =~ "^(die|die_errno)$"; +@@ + fd = +- open ++ xopen + (...); - if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); } From 26146980f1298e468a49af4aa5b2f1cc5dc8857b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 10 Sep 2021 10:04:42 -0400 Subject: [PATCH 03/71] t5551: test v2-to-v0 http protocol fallback Since we use the v2 protocol by default, the connection of a v2 client to a v2 server is well covered by the test suite. And with the GIT_TEST_PROTOCOL_VERSION knob, we can easily test a v0 client connecting to a v2-aware server (which will then just speak v0). But we have no regular tests that a v2 client, when encountering a non-v2-aware server, will correctly fall back to using v0. In theory this is a job for the cross-version tests in t/interop, but: - they cover only git:// and file:// clones - they are not part of the usual test suite, so nobody ever runs them anyway Since using v2 over http requires configuring the web server to pass along the Git-Protocol header, we can easily create a situation where the server does not respect the v2 probe, and the conversation falls back to v0. This works just fine. This new test is not about fixing any particular bug, but just making sure that the system works (and continues to work) as expected. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/lib-httpd/apache.conf | 5 +++++ t/t5551-http-fetch-smart.sh | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index afa91e38b0..1321357d8b 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -117,6 +117,11 @@ Alias /auth/dumb/ www/auth/dumb/ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL + + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL + SetEnv GIT_PROTOCOL + ScriptAlias /smart/incomplete_length/git-upload-pack incomplete-length-upload-pack-v2-http.sh/ ScriptAlias /smart/incomplete_body/git-upload-pack incomplete-body-upload-pack-v2-http.sh/ ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/ diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 4f87d90c5b..cffc47a8e3 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -558,4 +558,13 @@ test_expect_success 'http auth forgets bogus credentials' ' expect_askpass both user@host ' +test_expect_success 'client falls back from v2 to v0 to match server' ' + GIT_TRACE_PACKET=$PWD/trace \ + GIT_TEST_PROTOCOL_VERSION=2 \ + git clone $HTTPD_URL/smart_v0/repo.git repo-v0 && + # check for v0; there the HEAD symref is communicated in the capability + # line; v2 uses a different syntax on each ref advertisement line + grep symref=HEAD:refs/heads/ trace +' + test_done From ff6a37c99e3343633c53f56789afcc8f8165d276 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 10 Sep 2021 10:05:45 -0400 Subject: [PATCH 04/71] http-backend: handle HTTP_GIT_PROTOCOL CGI variable When a client requests the v2 protocol over HTTP, they set the Git-Protocol header. Webservers will generally make that available to our CGI as HTTP_GIT_PROTOCOL in the environment. However, that's not sufficient for upload-pack, etc, to respect it; they look in GIT_PROTOCOL (without the HTTP_ prefix). Either the webserver or the CGI is responsible for relaying that HTTP header into the GIT_PROTOCOL variable. Traditionally, our tests have configured the webserver to do so, but that's a burden on the server admin. We can make this work out of the box by having the http-backend CGI copy the contents of HTTP_GIT_PROTOCOL to GIT_PROTOCOL. There are no new tests here. By removing the SetEnvIf line from our test Apache config, we're now relying on this behavior of http-backend to trigger the v2 protocol there (and there are numerous tests that fail if this doesn't work). There is one subtlety here: we copy HTTP_GIT_PROTOCOL only if there is no existing GIT_PROTOCOL variable. That leaves the webserver admin free to override the client's decision if they choose. This is unlikely to be useful in practice, but is more flexible. And indeed, it allows the v2-to-v0 fallback test added in the previous commit to continue working. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- http-backend.c | 4 ++++ t/lib-httpd/apache.conf | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/http-backend.c b/http-backend.c index b329bf63f0..92ceb31f9a 100644 --- a/http-backend.c +++ b/http-backend.c @@ -739,6 +739,7 @@ static int bad_request(struct strbuf *hdr, const struct service_cmd *c) int cmd_main(int argc, const char **argv) { char *method = getenv("REQUEST_METHOD"); + const char *proto_header; char *dir; struct service_cmd *cmd = NULL; char *cmd_arg = NULL; @@ -789,6 +790,9 @@ int cmd_main(int argc, const char **argv) http_config(); max_request_buffer = git_env_ulong("GIT_HTTP_MAX_REQUEST_BUFFER", max_request_buffer); + proto_header = getenv("HTTP_GIT_PROTOCOL"); + if (proto_header) + setenv(GIT_PROTOCOL_ENVIRONMENT, proto_header, 0); cmd->imp(&hdr, cmd_arg); return 0; diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 1321357d8b..180a41fe96 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -81,8 +81,6 @@ PassEnv GIT_TRACE PassEnv GIT_CONFIG_NOSYSTEM PassEnv GIT_TEST_SIDEBAND_ALL -SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0 - Alias /dumb/ www/ Alias /auth/dumb/ www/auth/dumb/ From 295d81b9e4c91517f4f436a16889027cb86cba57 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 10 Sep 2021 10:09:13 -0400 Subject: [PATCH 05/71] docs/http-backend: mention v2 protocol Historically there was a little bit of configuration needed at the webserver level in order to get the client's v2 protocol probes to Git. But when we introduced the v2 protocol, we never documented these. As of the previous commit, this should mostly work out of the box without any explicit configuration. But it's worth documenting this to make it clear how we expect it to work, especially in the face of webservers which don't provide all headers over the CGI interface. Or anybody who runs across this documentation but has an older version of Git (or _used_ to have an older version, and wonders why they still have a SetEnvIf line in their Apache config and whether it's still necessary). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-http-backend.txt | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt index 558966aa83..0c5c0dde19 100644 --- a/Documentation/git-http-backend.txt +++ b/Documentation/git-http-backend.txt @@ -16,7 +16,9 @@ A simple CGI program to serve the contents of a Git repository to Git clients accessing the repository over http:// and https:// protocols. The program supports clients fetching using both the smart HTTP protocol and the backwards-compatible dumb HTTP protocol, as well as clients -pushing using the smart HTTP protocol. +pushing using the smart HTTP protocol. It also supports Git's +more-efficient "v2" protocol if properly configured; see the +discussion of `GIT_PROTOCOL` in the ENVIRONMENT section below. It verifies that the directory has the magic file "git-daemon-export-ok", and it will refuse to export any Git directory @@ -77,6 +79,18 @@ Apache 2.x:: SetEnv GIT_PROJECT_ROOT /var/www/git SetEnv GIT_HTTP_EXPORT_ALL ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/ + +# This is not strictly necessary using Apache and a modern version of +# git-http-backend, as the webserver will pass along the header in the +# environment as HTTP_GIT_PROTOCOL, and http-backend will copy that into +# GIT_PROTOCOL. But you may need this line (or something similar if you +# are using a different webserver), or if you want to support older Git +# versions that did not do that copying. +# +# Having the webserver set up GIT_PROTOCOL is perfectly fine even with +# modern versions (and will take precedence over HTTP_GIT_PROTOCOL, +# which means it can be used to override the client's request). +SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0 ---------------------------------------------------------------- + To enable anonymous read access but authenticated write access, @@ -264,6 +278,16 @@ a repository with an extremely large number of refs. The value can be specified with a unit (e.g., `100M` for 100 megabytes). The default is 10 megabytes. +Clients may probe for optional protocol capabilities (like the v2 +protocol) using the `Git-Protocol` HTTP header. In order to support +these, the contents of that header must appear in the `GIT_PROTOCOL` +environment variable. Most webservers will pass this header to the CGI +via the `HTTP_GIT_PROTOCOL` variable, and `git-http-backend` will +automatically copy that to `GIT_PROTOCOL`. However, some webservers may +be more selective about which headers they'll pass, in which case they +need to be configured explicitly (see the mention of `Git-Protocol` in +the Apache config from the earlier EXAMPLES section). + The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}', ensuring that any reflogs created by 'git-receive-pack' contain some From 2834a72d5e22fa1138efdeb1f515e894d743cfb4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 10 Sep 2021 10:09:56 -0400 Subject: [PATCH 06/71] docs/git: discuss server-side config for GIT_PROTOCOL The v2 protocol requires that the GIT_PROTOCOL environment variable gets passed around, but we don't have any documentation describing how this is supposed to work. In particular, we need to note what server admins might need to configure to make things work. The definition of the GIT_PROTOCOL variable is probably the best place for this, since: - we deal with multiple transports (ssh, http, etc). Transport-specific documentation (like the git-http-backend bits added in the previous commit) are helpful for those transports, but this gives a broader overview. Plus we do not have a specific transport endpoint program for ssh, so this is a reasonable place to mention it. - the server side of the protocol involves multiple programs. For now, upload-pack is the only endpoint which uses GIT_PROTOCOL, but that will likely expand in the future. We're better off with a central discussion of what the server admin might need to do. However, for discoverability, this patch adds a pointer from upload-pack's documentation. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/git-upload-pack.txt | 8 ++++++++ Documentation/git.txt | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt index 9822c1eb1a..070fc78008 100644 --- a/Documentation/git-upload-pack.txt +++ b/Documentation/git-upload-pack.txt @@ -44,6 +44,14 @@ OPTIONS :: The repository to sync from. +ENVIRONMENT +----------- + +`GIT_PROTOCOL`:: + Internal variable used for handshaking the wire protocol. Server + admins may need to configure some transports to allow this + variable to be passed. See the discussion in linkgit:git[1]. + SEE ALSO -------- linkgit:gitnamespaces[7] diff --git a/Documentation/git.txt b/Documentation/git.txt index 6dd241ef83..e4b82599fc 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -894,6 +894,21 @@ for full details. Contains a colon ':' separated list of keys with optional values 'key[=value]'. Presence of unknown keys and values must be ignored. ++ +Note that servers may need to be configured to allow this variable to +pass over some transports. It will be propagated automatically when +accessing local repositories (i.e., `file://` or a filesystem path), as +well as over the `git://` protocol. For git-over-http, it should work +automatically in most configurations, but see the discussion in +linkgit:git-http-backend[1]. For git-over-ssh, the ssh server may need +to be configured to allow clients to pass this variable (e.g., by using +`AcceptEnv GIT_PROTOCOL` with OpenSSH). ++ +This configuration is optional. If the variable is not propagated, then +clients will fall back to the original "v0" protocol (but may miss out +on some performance improvements or features). This variable currently +only affects clones and fetches; it is not yet used for pushes (but may +be in the future). `GIT_OPTIONAL_LOCKS`:: If set to `0`, Git will complete any requested operation without From 1b421e7a5aa6984777029ab799f9a0221875e1dd Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 10 Sep 2021 10:10:19 -0400 Subject: [PATCH 07/71] docs/protocol-v2: point readers transport config discussion We recently added tips for server admins to configure various transports to support v2's GIT_PROTOCOL variable. While the protocol-v2 document is pretty technical and not of interest to most admins, it may be a starting point for them to figure out how to turn on v2. Let's put some pointers from there to the other documentation. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- Documentation/technical/protocol-v2.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt index 1040d85319..a703d37e08 100644 --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@ -42,7 +42,8 @@ Initial Client Request In general a client can request to speak protocol v2 by sending `version=2` through the respective side-channel for the transport being used which inevitably sets `GIT_PROTOCOL`. More information can be -found in `pack-protocol.txt` and `http-protocol.txt`. In all cases the +found in `pack-protocol.txt` and `http-protocol.txt`, as well as the +`GIT_PROTOCOL` definition in `git.txt`. In all cases the response from the server is the capability advertisement. Git Transport @@ -58,6 +59,8 @@ SSH and File Transport When using either the ssh:// or file:// transport, the GIT_PROTOCOL environment variable must be set explicitly to include "version=2". +The server may need to be configured to allow this environment variable +to pass. HTTP Transport ~~~~~~~~~~~~~~ @@ -81,6 +84,9 @@ A v2 server would reply: Subsequent requests are then made directly to the service `$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack). +The server may need to be configured to pass this header's contents via +the `GIT_PROTOCOL` variable. See the discussion in `git-http-backend.txt`. + Capability Advertisement ------------------------ From ea7dc012d20feb2b19964c207cd579437d2da26c Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Fri, 10 Sep 2021 10:31:14 +0000 Subject: [PATCH 08/71] git-am.txt: clarify --abort behavior Both Johannes and I assumed (perhaps due to familiarity with rebase) that am --abort would return the user to a clean state. However, since am, unlike rebase, is intended to be used within a dirty working tree, --abort will only clean the files involved in the am operation. Signed-off-by: Elijah Newren Signed-off-by: Junio C Hamano --- Documentation/git-am.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 8714dfcb76..0a4a984dfd 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -178,6 +178,8 @@ default. You can use `--no-utf8` to override this. --abort:: Restore the original branch and abort the patching operation. + Revert contents of files involved in the am operation to their + pre-am state. --quit:: Abort the patching operation but keep HEAD and the index From 42b5e09d1e555689168941eb639be644d004cb9d Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Fri, 10 Sep 2021 10:31:15 +0000 Subject: [PATCH 09/71] t4151: add a few am --abort tests Signed-off-by: Elijah Newren Signed-off-by: Junio C Hamano --- t/t4151-am-abort.sh | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh index 9d8d3c72e7..15f2f92cd7 100755 --- a/t/t4151-am-abort.sh +++ b/t/t4151-am-abort.sh @@ -23,7 +23,13 @@ test_expect_success setup ' test_tick && git commit -a -m $i || return 1 done && + git branch changes && git format-patch --no-numbered initial && + git checkout -b conflicting initial && + echo different >>file-1 && + echo whatever >new-file && + git add file-1 new-file && + git commit -m different && git checkout -b side initial && echo local change >file-2-expect ' @@ -191,4 +197,37 @@ test_expect_success 'am --abort leaves index stat info alone' ' git diff-files --exit-code --quiet ' +test_expect_failure 'git am --abort return failed exit status when it fails' ' + test_when_finished "rm -rf file-2/ && git reset --hard && git am --abort" && + git checkout changes && + git format-patch -1 --stdout conflicting >changes.mbox && + test_must_fail git am --3way changes.mbox && + + git rm file-2 && + mkdir file-2 && + echo precious >file-2/somefile && + test_must_fail git am --abort && + test_path_is_dir file-2/ +' + +test_expect_success 'git am --abort cleans relevant files' ' + git checkout changes && + git format-patch -1 --stdout conflicting >changes.mbox && + test_must_fail git am --3way changes.mbox && + + test_path_is_file new-file && + echo further changes >>file-1 && + echo change other file >>file-2 && + + # Abort, and expect the files touched by am to be reverted + git am --abort && + + test_path_is_missing new-file && + + # Files not involved in am operation are left modified + git diff --name-only changes >actual && + test_write_lines file-2 >expect && + test_cmp expect actual +' + test_done From c5ead19ea282a288e01d86536349a4ae4a093e4b Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Fri, 10 Sep 2021 10:31:16 +0000 Subject: [PATCH 10/71] am: fix incorrect exit status on am fail to abort Signed-off-by: Elijah Newren Signed-off-by: Junio C Hamano --- builtin/am.c | 3 ++- t/t4151-am-abort.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index 0c2ad96b70..c79e0167e9 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2106,7 +2106,8 @@ static void am_abort(struct am_state *state) if (!has_orig_head) oidcpy(&orig_head, the_hash_algo->empty_tree); - clean_index(&curr_head, &orig_head); + if (clean_index(&curr_head, &orig_head)) + die(_("failed to clean index")); if (has_orig_head) update_ref("am --abort", "HEAD", &orig_head, diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh index 15f2f92cd7..2374151662 100755 --- a/t/t4151-am-abort.sh +++ b/t/t4151-am-abort.sh @@ -197,7 +197,7 @@ test_expect_success 'am --abort leaves index stat info alone' ' git diff-files --exit-code --quiet ' -test_expect_failure 'git am --abort return failed exit status when it fails' ' +test_expect_success 'git am --abort return failed exit status when it fails' ' test_when_finished "rm -rf file-2/ && git reset --hard && git am --abort" && git checkout changes && git format-patch -1 --stdout conflicting >changes.mbox && From 5b952447ccfc3719d59eb9d28af153e34e6d69b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 11:34:15 +0200 Subject: [PATCH 11/71] INSTALL: don't mention the "curl" executable at all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 1d53f90ed97 (The "curl" executable is no longer required, 2008-06-15) the wording for requiring curl(1) was changed to the current "you might also want...". Mentioning the "curl" executable at all is just confusing, someone building git might want to use it to debug things, but they might also just use wget(1) or some other http client. The "curl" executable has the advantage that you might be able to e.g. reproduce a bug in git's usage of libcurl with it, but anyone going to those extents is unlikely to be aided by this note in INSTALL. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- INSTALL | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/INSTALL b/INSTALL index 66389ce059..5b8bd5ccce 100644 --- a/INSTALL +++ b/INSTALL @@ -139,8 +139,7 @@ Issues of note: (PPC_SHA1). - "libcurl" library is used by git-http-fetch, git-fetch, and, if - the curl version >= 7.34.0, for git-imap-send. You might also - want the "curl" executable for debugging purposes. If you do not + the curl version >= 7.34.0, for git-imap-send. If you do not use http:// or https:// repositories, and do not want to put patches into an IMAP mailbox, you do not have to have them (use NO_CURL). From e54e50201cc2df009a60b8754570cf6ce9dcff43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 11:34:16 +0200 Subject: [PATCH 12/71] INSTALL: reword and copy-edit the "libcurl" section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the "libcurl" section shorter and more to the point, this is mostly based on suggestions from [1]. 1. https://lore.kernel.org/git/YTtxcBdF2VQdWp5C@coredump.intra.peff.net/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- INSTALL | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/INSTALL b/INSTALL index 5b8bd5ccce..d593f62874 100644 --- a/INSTALL +++ b/INSTALL @@ -138,11 +138,11 @@ Issues of note: BLK_SHA1. Also included is a version optimized for PowerPC (PPC_SHA1). - - "libcurl" library is used by git-http-fetch, git-fetch, and, if - the curl version >= 7.34.0, for git-imap-send. If you do not - use http:// or https:// repositories, and do not want to put - patches into an IMAP mailbox, you do not have to have them - (use NO_CURL). + - "libcurl" library is used for fetching and pushing + repositories over http:// or https://, as well as by + git-imap-send if the curl version is >= 7.34.0. If you do + not need that functionality, use NO_CURL to build without + it. - "expat" library; git-http-push uses it for remote lock management over DAV. Similar to "curl" above, this is optional From 325006f2dbeb482f422b2c0e62b485a41cb1c64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 11 Sep 2021 22:36:40 +0200 Subject: [PATCH 13/71] oidset: make oidset_size() an inline function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit oidset_size() just reads a single word from memory and returns it. Avoid the function call overhead for this trivial operation by turning it into an inline function. While we're at it, declare its parameter const to allow it to be used on read-only oidsets. Suggested-by: Jeff King Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- oidset.c | 5 ----- oidset.h | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/oidset.c b/oidset.c index 5aac633c1f..b36a2bae86 100644 --- a/oidset.c +++ b/oidset.c @@ -36,11 +36,6 @@ void oidset_clear(struct oidset *set) oidset_init(set, 0); } -int oidset_size(struct oidset *set) -{ - return kh_size(&set->set); -} - void oidset_parse_file(struct oidset *set, const char *path) { oidset_parse_file_carefully(set, path, NULL, NULL); diff --git a/oidset.h b/oidset.h index 01f6560283..ba4a5a2cd3 100644 --- a/oidset.h +++ b/oidset.h @@ -57,7 +57,10 @@ int oidset_remove(struct oidset *set, const struct object_id *oid); /** * Returns the number of oids in the set. */ -int oidset_size(struct oidset *set); +static inline int oidset_size(const struct oidset *set) +{ + return kh_size(&set->set); +} /** * Remove all entries from the oidset, freeing any resources associated with From 893b5635059a7e47b042073bc69ca0c5d9d15dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 11 Sep 2021 22:39:31 +0200 Subject: [PATCH 14/71] midx: inline nth_midxed_pack_entry() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fill_midx_entry() finds the position of an object ID and passes it to nth_midxed_pack_entry(), which uses the position to look up the object ID for its own purposes. Inline the latter into the former to avoid that lookup. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- midx.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/midx.c b/midx.c index 321c6fdd2f..8cb063023c 100644 --- a/midx.c +++ b/midx.c @@ -276,14 +276,18 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos) (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH); } -static int nth_midxed_pack_entry(struct repository *r, - struct multi_pack_index *m, - struct pack_entry *e, - uint32_t pos) +int fill_midx_entry(struct repository * r, + const struct object_id *oid, + struct pack_entry *e, + struct multi_pack_index *m) { + uint32_t pos; uint32_t pack_int_id; struct packed_git *p; + if (!bsearch_midx(oid, m, &pos)) + return 0; + if (pos >= m->num_objects) return 0; @@ -305,10 +309,8 @@ static int nth_midxed_pack_entry(struct repository *r, if (p->num_bad_objects) { uint32_t i; - struct object_id oid; - nth_midxed_object_oid(&oid, m, pos); for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(oid.hash, + if (hasheq(oid->hash, p->bad_object_sha1 + the_hash_algo->rawsz * i)) return 0; } @@ -319,19 +321,6 @@ static int nth_midxed_pack_entry(struct repository *r, return 1; } -int fill_midx_entry(struct repository * r, - const struct object_id *oid, - struct pack_entry *e, - struct multi_pack_index *m) -{ - uint32_t pos; - - if (!bsearch_midx(oid, m, &pos)) - return 0; - - return nth_midxed_pack_entry(r, m, e, pos); -} - /* Match "foo.idx" against either "foo.pack" _or_ "foo.idx". */ static int cmp_idx_or_pack_name(const char *idx_or_pack_name, const char *idx_name) From 751530de5db77196a08897e3c47526c0f0147ef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 11 Sep 2021 22:40:33 +0200 Subject: [PATCH 15/71] packfile: convert mark_bad_packed_object() to object_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers have full object IDs, so pass them on instead of just their hash member. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- object-file.c | 2 +- packfile.c | 12 ++++++------ packfile.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/object-file.c b/object-file.c index a8be899481..fb5a385a06 100644 --- a/object-file.c +++ b/object-file.c @@ -1616,7 +1616,7 @@ static int do_oid_object_info_extended(struct repository *r, return 0; rtype = packed_object_info(r, e.p, e.offset, oi); if (rtype < 0) { - mark_bad_packed_object(e.p, real->hash); + mark_bad_packed_object(e.p, real); return do_oid_object_info_extended(r, real, oi, 0); } else if (oi->whence == OI_PACKED) { oi->u.packed.offset = e.offset; diff --git a/packfile.c b/packfile.c index 4d0d625238..fb15fc5b49 100644 --- a/packfile.c +++ b/packfile.c @@ -1161,17 +1161,17 @@ int unpack_object_header(struct packed_git *p, return type; } -void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1) +void mark_bad_packed_object(struct packed_git *p, const struct object_id *oid) { unsigned i; const unsigned hashsz = the_hash_algo->rawsz; for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(sha1, p->bad_object_sha1 + hashsz * i)) + if (hasheq(oid->hash, p->bad_object_sha1 + hashsz * i)) return; p->bad_object_sha1 = xrealloc(p->bad_object_sha1, st_mult(GIT_MAX_RAWSZ, st_add(p->num_bad_objects, 1))); - hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, sha1); + hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, oid->hash); p->num_bad_objects++; } @@ -1272,7 +1272,7 @@ static int retry_bad_packed_offset(struct repository *r, if (offset_to_pack_pos(p, obj_offset, &pos) < 0) return OBJ_BAD; nth_packed_object_id(&oid, p, pack_pos_to_index(p, pos)); - mark_bad_packed_object(p, oid.hash); + mark_bad_packed_object(p, &oid); type = oid_object_info(r, &oid, NULL); if (type <= OBJ_NONE) return OBJ_BAD; @@ -1722,7 +1722,7 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset, nth_packed_object_id(&oid, p, index_pos); error("bad packed object CRC for %s", oid_to_hex(&oid)); - mark_bad_packed_object(p, oid.hash); + mark_bad_packed_object(p, &oid); data = NULL; goto out; } @@ -1811,7 +1811,7 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset, " at offset %"PRIuMAX" from %s", oid_to_hex(&base_oid), (uintmax_t)obj_offset, p->pack_name); - mark_bad_packed_object(p, base_oid.hash); + mark_bad_packed_object(p, &base_oid); base = read_object(r, &base_oid, &type, &base_size); external_base = base; } diff --git a/packfile.h b/packfile.h index 3ae117a8ae..a982ed9994 100644 --- a/packfile.h +++ b/packfile.h @@ -159,7 +159,7 @@ int packed_object_info(struct repository *r, struct packed_git *pack, off_t offset, struct object_info *); -void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1); +void mark_bad_packed_object(struct packed_git *, const struct object_id *); const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1); #define ON_DISK_KEEP_PACKS 1 From 7407d733a4a99cf946cab0c82e03c41dbd305b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 11 Sep 2021 22:42:20 +0200 Subject: [PATCH 16/71] packfile: convert has_packed_and_bad() to object_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single caller has a full object ID, so pass it on instead of just its hash member. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- object-file.c | 2 +- packfile.c | 4 ++-- packfile.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/object-file.c b/object-file.c index fb5a385a06..01e7058b4e 100644 --- a/object-file.c +++ b/object-file.c @@ -1725,7 +1725,7 @@ void *read_object_file_extended(struct repository *r, die(_("loose object %s (stored in %s) is corrupt"), oid_to_hex(repl), path); - if ((p = has_packed_and_bad(r, repl->hash)) != NULL) + if ((p = has_packed_and_bad(r, repl)) != NULL) die(_("packed object %s (stored in %s) is corrupt"), oid_to_hex(repl), p->pack_name); obj_read_unlock(); diff --git a/packfile.c b/packfile.c index fb15fc5b49..04080a558b 100644 --- a/packfile.c +++ b/packfile.c @@ -1176,14 +1176,14 @@ void mark_bad_packed_object(struct packed_git *p, const struct object_id *oid) } const struct packed_git *has_packed_and_bad(struct repository *r, - const unsigned char *sha1) + const struct object_id *oid) { struct packed_git *p; unsigned i; for (p = r->objects->packed_git; p; p = p->next) for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(sha1, + if (hasheq(oid->hash, p->bad_object_sha1 + the_hash_algo->rawsz * i)) return p; return NULL; diff --git a/packfile.h b/packfile.h index a982ed9994..186146779d 100644 --- a/packfile.h +++ b/packfile.h @@ -160,7 +160,7 @@ int packed_object_info(struct repository *r, off_t offset, struct object_info *); void mark_bad_packed_object(struct packed_git *, const struct object_id *); -const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1); +const struct packed_git *has_packed_and_bad(struct repository *, const struct object_id *); #define ON_DISK_KEEP_PACKS 1 #define IN_CORE_KEEP_PACKS 2 From 09ef66179b943d03cbe0bea0603e5f40574695a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 11 Sep 2021 22:43:26 +0200 Subject: [PATCH 17/71] packfile: use oidset for bad objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Store the object ID of broken pack entries in an oidset instead of keeping only their hashes in an unsorted array. The resulting code is shorter and easier to read. It also handles the (hopefully) very rare case of having a high number of bad objects better. Helped-by: Jeff King Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- midx.c | 10 +++------- object-store.h | 4 ++-- packfile.c | 28 ++++++---------------------- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/midx.c b/midx.c index 8cb063023c..76322b713c 100644 --- a/midx.c +++ b/midx.c @@ -307,13 +307,9 @@ int fill_midx_entry(struct repository * r, if (!is_pack_valid(p)) return 0; - if (p->num_bad_objects) { - uint32_t i; - for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(oid->hash, - p->bad_object_sha1 + the_hash_algo->rawsz * i)) - return 0; - } + if (oidset_size(&p->bad_objects) && + oidset_contains(&p->bad_objects, oid)) + return 0; e->offset = nth_midxed_offset(m, pos); e->p = p; diff --git a/object-store.h b/object-store.h index b4dc6668aa..c7bead66f6 100644 --- a/object-store.h +++ b/object-store.h @@ -10,6 +10,7 @@ #include "khash.h" #include "dir.h" #include "oidtree.h" +#include "oidset.h" struct object_directory { struct object_directory *next; @@ -75,9 +76,8 @@ struct packed_git { const void *index_data; size_t index_size; uint32_t num_objects; - uint32_t num_bad_objects; uint32_t crc_offset; - unsigned char *bad_object_sha1; + struct oidset bad_objects; int index_version; time_t mtime; int pack_fd; diff --git a/packfile.c b/packfile.c index 04080a558b..caba29c624 100644 --- a/packfile.c +++ b/packfile.c @@ -1163,29 +1163,17 @@ int unpack_object_header(struct packed_git *p, void mark_bad_packed_object(struct packed_git *p, const struct object_id *oid) { - unsigned i; - const unsigned hashsz = the_hash_algo->rawsz; - for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(oid->hash, p->bad_object_sha1 + hashsz * i)) - return; - p->bad_object_sha1 = xrealloc(p->bad_object_sha1, - st_mult(GIT_MAX_RAWSZ, - st_add(p->num_bad_objects, 1))); - hashcpy(p->bad_object_sha1 + hashsz * p->num_bad_objects, oid->hash); - p->num_bad_objects++; + oidset_insert(&p->bad_objects, oid); } const struct packed_git *has_packed_and_bad(struct repository *r, const struct object_id *oid) { struct packed_git *p; - unsigned i; for (p = r->objects->packed_git; p; p = p->next) - for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(oid->hash, - p->bad_object_sha1 + the_hash_algo->rawsz * i)) - return p; + if (oidset_contains(&p->bad_objects, oid)) + return p; return NULL; } @@ -2016,13 +2004,9 @@ static int fill_pack_entry(const struct object_id *oid, { off_t offset; - if (p->num_bad_objects) { - unsigned i; - for (i = 0; i < p->num_bad_objects; i++) - if (hasheq(oid->hash, - p->bad_object_sha1 + the_hash_algo->rawsz * i)) - return 0; - } + if (oidset_size(&p->bad_objects) && + oidset_contains(&p->bad_objects, oid)) + return 0; offset = find_pack_entry_one(oid->hash, p); if (!offset) From 162410f8a020dc039ead88c0ec5337ed808b7019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 13:17:48 +0200 Subject: [PATCH 18/71] git-submodule: remove unused is_zero_oid() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The is_zero_oid() function in git-submodule.sh has not been used since e83e3333b57 (submodule: port submodule subcommand 'summary' from shell to C, 2020-08-13), so we can remove it. This was the last user of the sane_egrep() function in git-sh-setup.sh. I'm not removing it in case some out-of-tree user relied on it. Per the discussion that can be found upthread of [1]. 1. https://lore.kernel.org/git/87tuiwjfvi.fsf@evledraar.gmail.com/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-submodule.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index dbd2ec2050..aeb96c5824 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -63,11 +63,6 @@ isnumber() n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1" } -# Given a full hex object ID, is this the zero OID? -is_zero_oid () { - echo "$1" | sane_egrep '^0+$' >/dev/null 2>&1 -} - # Sanitize the local git environment for use within a submodule. We # can't simply use clear_local_git_env since we want to preserve some # of the settings from GIT_CONFIG_PARAMETERS. From be8d370e3c0791e8b181ddc73cce37ff8356257c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 13:17:49 +0200 Subject: [PATCH 19/71] git-sh-setup: remove unused "pull with rebase" message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the "pull with rebase" message previously used by the git-pull.sh script, which was removed in 49eb8d39c78 (Remove contrib/examples/*, 2018-03-25). Even if some out-of-tree user copy/pasted the old git-pull.sh code, and relied on passing it a "pull with rebase" argument, we'll fall back on the "*" case here, they just won't get the "pull with rebase" part of their message translated. I don't think it's likely that anyone out-of-tree relied on that, but I'm being conservative here per the discussion that can be found upthread of [1]. 1. https://lore.kernel.org/git/87tuiwjfvi.fsf@evledraar.gmail.com/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-sh-setup.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 10d9764185..cee053cdc3 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -223,9 +223,6 @@ require_clean_work_tree () { "rewrite branches") gettextln "Cannot rewrite branches: You have unstaged changes." >&2 ;; - "pull with rebase") - gettextln "Cannot pull with rebase: You have unstaged changes." >&2 - ;; *) eval_gettextln "Cannot \$action: You have unstaged changes." >&2 ;; @@ -242,9 +239,6 @@ require_clean_work_tree () { rebase) gettextln "Cannot rebase: Your index contains uncommitted changes." >&2 ;; - "pull with rebase") - gettextln "Cannot pull with rebase: Your index contains uncommitted changes." >&2 - ;; *) eval_gettextln "Cannot \$action: Your index contains uncommitted changes." >&2 ;; From 296339549a52ee17c8e61429c081ac17fe81ac26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 13:17:50 +0200 Subject: [PATCH 20/71] git-bisect: remove unused SHA-1 $x40 shell variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This variable was last used in code removed in 06f5608c14 (bisect--helper: `bisect_start` shell function partially in C, 2019-01-02). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-bisect.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 6a7afaea8d..b59f3aaad4 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -34,8 +34,6 @@ Please use "git help bisect" to get the full man page.' OPTIONS_SPEC= . git-sh-setup -_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' -_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" TERM_BAD=bad TERM_GOOD=good From 705079112669c05c7ce5dee38ee8d7f075b8b3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 13:17:51 +0200 Subject: [PATCH 21/71] test-lib: remove unused $_x40 and $_z40 variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two have fallen out of use with the SHA-256 migration. The last use of $_x40 was removed in fc7e73d7ef (t4013: improve diff-post-processor logic, 2020-08-21) and The last use of $_z40 was removed in 7a868c51c2 (t5562: use $ZERO_OID, 2019-12-21), but it was then needlessly refactored to be hash-agnostic in 192b517589 (t: use hash-specific lookup tables to define test constants, 2020-02-22). We can just remove it. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/test-lib.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index fc1e521519..a0b944e8fe 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -534,7 +534,7 @@ SQ=\' # when case-folding filenames u200c=$(printf '\342\200\214') -export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX +export _x05 _x35 LF u200c EMPTY_TREE EMPTY_BLOB ZERO_OID OID_REGEX # Each test should start with something like this, after copyright notices: # @@ -1423,10 +1423,9 @@ then fi # Convenience -# A regexp to match 5, 35 and 40 hexdigits +# A regexp to match 5 and 35 hexdigits _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05" -_x40="$_x35$_x05" test_oid_init @@ -1435,7 +1434,6 @@ OID_REGEX=$(echo $ZERO_OID | sed -e 's/0/[0-9a-f]/g') OIDPATH_REGEX=$(test_oid_to_path $ZERO_OID | sed -e 's/0/[0-9a-f]/g') EMPTY_TREE=$(test_oid empty_tree) EMPTY_BLOB=$(test_oid empty_blob) -_z40=$ZERO_OID # Provide an implementation of the 'yes' utility; the upper bound # limit is there to help Windows that cannot stop this loop from From 8f0f11015683c0db1a06148300624fdb767b1d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sat, 11 Sep 2021 13:45:43 +0200 Subject: [PATCH 22/71] compression: drop write-only core_compression_* variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 8de7eeb54b (compression: unify pack.compression configuration parsing, 2016-11-15) the variables core_compression_level and core_compression_seen are only set, but never read. Remove them. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- cache.h | 1 - config.c | 3 --- environment.c | 1 - 3 files changed, 5 deletions(-) diff --git a/cache.h b/cache.h index bd4869beee..0a8d44ce71 100644 --- a/cache.h +++ b/cache.h @@ -958,7 +958,6 @@ extern char *apply_default_ignorewhitespace; extern const char *git_attributes_file; extern const char *git_hooks_path; extern int zlib_compression_level; -extern int core_compression_level; extern int pack_compression_level; extern size_t packed_git_window_size; extern size_t packed_git_limit; diff --git a/config.c b/config.c index f33abeab85..7a6ff18ffa 100644 --- a/config.c +++ b/config.c @@ -76,7 +76,6 @@ static struct key_value_info *current_config_kvi; */ static enum config_scope current_parsing_scope; -static int core_compression_seen; static int pack_compression_seen; static int zlib_compression_seen; @@ -1400,8 +1399,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) die(_("bad zlib compression level %d"), level); - core_compression_level = level; - core_compression_seen = 1; if (!zlib_compression_seen) zlib_compression_level = level; if (!pack_compression_seen) diff --git a/environment.c b/environment.c index d6b22ede7e..b4ba4fa22d 100644 --- a/environment.c +++ b/environment.c @@ -41,7 +41,6 @@ char *apply_default_ignorewhitespace; const char *git_attributes_file; const char *git_hooks_path; int zlib_compression_level = Z_BEST_SPEED; -int core_compression_level; int pack_compression_level = Z_DEFAULT_COMPRESSION; int fsync_object_files; size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; From 8d133a4653abed4b06d3deb8bd71cf55cd87c990 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 11 Sep 2021 11:01:16 -0400 Subject: [PATCH 23/71] strvec: use size_t to store nr and alloc We converted argv_array (which later became strvec) to use size_t in 819f0e76b1 (argv-array: use size_t for count and alloc, 2020-07-28) in order to avoid the possibility of integer overflow. But later, commit d70a9eb611 (strvec: rename struct fields, 2020-07-28) accidentally converted these back to ints! Those two commits were part of the same patch series. I'm pretty sure what happened is that they were originally written in the opposite order and then cleaned up and re-ordered during an interactive rebase. And when resolving the inevitable conflict, I mistakenly took the "rename" patch completely, accidentally dropping the type change. We can correct it now; better late than never. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- strvec.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strvec.h b/strvec.h index fdcad75b45..6b3cbd6758 100644 --- a/strvec.h +++ b/strvec.h @@ -29,8 +29,8 @@ extern const char *empty_strvec[]; */ struct strvec { const char **v; - int nr; - int alloc; + size_t nr; + size_t alloc; }; #define STRVEC_INIT { empty_strvec, 0, 0 } From f222bd34ffde9e2fb06794bb657fd9dc371261df Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Sat, 11 Sep 2021 17:08:42 +0000 Subject: [PATCH 24/71] tests: remove leftover untracked files Remove untracked files that are unwanted after they are done being used. While the set of cases in this patch is certainly far from comprehensive, it was motivated by some work to see what the fallout would be if we were to make the removal of untracked files as a side effect of other commands into an error. Some cases were a bit more involved than the testcase changes included in this patch, but the ones included here represent the simple cases. While this patch is not that important since we are not changing the behavior of those other commands into an error in the near term, I thought these changes were useful anyway as an explicit documentation of the intent that these untracked files are no longer useful. Acked-by: Johannes Schindelin Acked-by: Derrick Stolee Signed-off-by: Elijah Newren Signed-off-by: Junio C Hamano --- t/t0090-cache-tree.sh | 1 + t/t2021-checkout-overwrite.sh | 1 + t/t3404-rebase-interactive.sh | 1 + t/t3435-rebase-gpg-sign.sh | 1 + t/t3510-cherry-pick-sequence.sh | 1 + t/t5304-prune.sh | 1 + t/t6415-merge-dir-to-symlink.sh | 6 ++++-- t/t6424-merge-unrelated-index-changes.sh | 1 + t/t6430-merge-recursive.sh | 4 +++- t/t6436-merge-overwrite.sh | 3 ++- t/t7201-co.sh | 1 + t/t7600-merge.sh | 1 + 12 files changed, 18 insertions(+), 4 deletions(-) diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh index 9bf66c9e68..9067572648 100755 --- a/t/t0090-cache-tree.sh +++ b/t/t0090-cache-tree.sh @@ -195,6 +195,7 @@ test_expect_success 'reset --hard gives cache-tree' ' test_expect_success 'reset --hard without index gives cache-tree' ' rm -f .git/index && + git clean -fd && git reset --hard && test_cache_tree ' diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh index 70d69263e6..660132ff8d 100755 --- a/t/t2021-checkout-overwrite.sh +++ b/t/t2021-checkout-overwrite.sh @@ -48,6 +48,7 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' ' test_expect_success SYMLINKS 'the symlink remained' ' + test_when_finished "rm a/b" && test -h a/b ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index d877872e8f..972ce026bb 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -297,6 +297,7 @@ test_expect_success 'abort with error when new base cannot be checked out' ' output && test_i18ngrep "file1" output && test_path_is_missing .git/rebase-merge && + rm file1 && git reset --hard HEAD^ ' diff --git a/t/t3435-rebase-gpg-sign.sh b/t/t3435-rebase-gpg-sign.sh index ec10766858..5f8ba2c739 100755 --- a/t/t3435-rebase-gpg-sign.sh +++ b/t/t3435-rebase-gpg-sign.sh @@ -65,6 +65,7 @@ test_rebase_gpg_sign ! true -i --gpg-sign --no-gpg-sign test_rebase_gpg_sign false -i --no-gpg-sign --gpg-sign test_expect_failure 'rebase -p --no-gpg-sign override commit.gpgsign' ' + test_when_finished "git clean -f" && git reset --hard merged && git config commit.gpgsign true && git rebase -p --no-gpg-sign --onto=one fork-point main && diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 49010aa946..3b0fa66c33 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -238,6 +238,7 @@ test_expect_success 'allow skipping commit but not abort for a new history' ' ' test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' ' + test_when_finished "rm unrelated" && pristine_detach initial && git rm --cached unrelated && git commit -m "untrack unrelated" && diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index 7cabb85ca6..8ae314af58 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -291,6 +291,7 @@ test_expect_success 'prune: handle HEAD reflog in multiple worktrees' ' cat ../expected >blob && git add blob && git commit -m "second commit in third" && + git clean -f && # Remove untracked left behind by deleting index git reset --hard HEAD^ ) && git prune --expire=now && diff --git a/t/t6415-merge-dir-to-symlink.sh b/t/t6415-merge-dir-to-symlink.sh index 2ce104aca7..2655e295f5 100755 --- a/t/t6415-merge-dir-to-symlink.sh +++ b/t/t6415-merge-dir-to-symlink.sh @@ -25,7 +25,8 @@ test_expect_success 'checkout does not clobber untracked symlink' ' git reset --hard main && git rm --cached a/b && git commit -m "untracked symlink remains" && - test_must_fail git checkout start^0 + test_must_fail git checkout start^0 && + git clean -fd # Do not leave the untracked symlink in the way ' test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' ' @@ -34,7 +35,8 @@ test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' ' git rm --cached a/b && git commit -m "untracked symlink remains" && git checkout -f start^0 && - test_path_is_file a/b-2/c/d + test_path_is_file a/b-2/c/d && + git clean -fd # Do not leave the untracked symlink in the way ' test_expect_success 'checkout should not have deleted a/b-2/c/d' ' diff --git a/t/t6424-merge-unrelated-index-changes.sh b/t/t6424-merge-unrelated-index-changes.sh index 5e3779ebc9..89dd544f38 100755 --- a/t/t6424-merge-unrelated-index-changes.sh +++ b/t/t6424-merge-unrelated-index-changes.sh @@ -132,6 +132,7 @@ test_expect_success 'merge-recursive, when index==head but head!=HEAD' ' # Make index match B git diff C B -- | git apply --cached && + test_when_finished "git clean -fd" && # Do not leave untracked around # Merge B & F, with B as "head" git merge-recursive A -- B F > out && test_i18ngrep "Already up to date" out diff --git a/t/t6430-merge-recursive.sh b/t/t6430-merge-recursive.sh index ffcc01fe65..a0efe7cb6d 100755 --- a/t/t6430-merge-recursive.sh +++ b/t/t6430-merge-recursive.sh @@ -718,7 +718,9 @@ test_expect_success 'merge-recursive remembers the names of all base trees' ' # merge-recursive prints in reverse order, but we do not care sort expect && sed -n "s/^virtual //p" out | sort >actual && - test_cmp expect actual + test_cmp expect actual && + + git clean -fd ' test_expect_success 'merge-recursive internal merge resolves to the sameness' ' diff --git a/t/t6436-merge-overwrite.sh b/t/t6436-merge-overwrite.sh index 84b4aacf49..c0b7bd7c3f 100755 --- a/t/t6436-merge-overwrite.sh +++ b/t/t6436-merge-overwrite.sh @@ -68,7 +68,8 @@ test_expect_success 'will not overwrite removed file' ' git commit -m "rm c1.c" && cp important c1.c && test_must_fail git merge c1a && - test_cmp important c1.c + test_cmp important c1.c && + rm c1.c # Do not leave untracked file in way of future tests ' test_expect_success 'will not overwrite re-added file' ' diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 7f6e23a4bb..b7ba1c3268 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -585,6 +585,7 @@ test_expect_success 'checkout --conflict=diff3' ' ' test_expect_success 'failing checkout -b should not break working tree' ' + git clean -fd && # Remove untracked files in the way git reset --hard main && git symbolic-ref HEAD refs/heads/main && test_must_fail git checkout -b renamer side^ && diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 2ef39d3088..c773e30b3f 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -717,6 +717,7 @@ test_expect_success 'failed fast-forward merge with --autostash' ' git reset --hard c0 && git merge-file file file.orig file.5 && cp file.5 other && + test_when_finished "rm other" && test_must_fail git merge --autostash c1 2>err && test_i18ngrep "Applied autostash." err && test_cmp file.5 file From c90be786da950a2705152dc49b9fb771f1d0a86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sat, 11 Sep 2021 20:26:41 +0200 Subject: [PATCH 25/71] test-tool run-command: fix flip-flop init pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In be5d88e1128 (test-tool run-command: learn to run (parts of) the testsuite, 2019-10-04) an init pattern was added that would use TESTSUITE_INIT, but then promptly memset() everything back to 0. We'd then set the "dup" on the two string lists. Our setting of "next" to "-1" thus did nothing, we'd reset it to "0" before using it. Let's set it to "0" instead, and trust the "STRING_LIST_INIT_DUP" to set "strdup_strings" appropriately for us. Note that while we compile this code, there's no in-tree user for the "testsuite" target being modified here anymore, see the discussion at and around [1]. 1. https://lore.kernel.org/git/nycvar.QRO.7.76.6.2109091323150.59@tvgsbejvaqbjf.bet/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- t/helper/test-run-command.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/t/helper/test-run-command.c b/t/helper/test-run-command.c index 7ae03dc712..14c57365e7 100644 --- a/t/helper/test-run-command.c +++ b/t/helper/test-run-command.c @@ -61,7 +61,7 @@ struct testsuite { int quiet, immediate, verbose, verbose_log, trace, write_junit_xml; }; #define TESTSUITE_INIT \ - { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, -1, 0, 0, 0, 0, 0, 0 } + { STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, 0, 0, 0, 0, 0, 0, 0 } static int next_test(struct child_process *cp, struct strbuf *err, void *cb, void **task_cb) @@ -142,9 +142,6 @@ static int testsuite(int argc, const char **argv) OPT_END() }; - memset(&suite, 0, sizeof(suite)); - suite.tests.strdup_strings = suite.failed.strdup_strings = 1; - argc = parse_options(argc, argv, NULL, options, testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION); From 3218cb753f5a2a49808bf07f039051e7b79b8f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Sun, 12 Sep 2021 02:24:40 +0200 Subject: [PATCH 26/71] gc: remove unused launchctl_get_uid() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the launchctl_boot_plist() function was added in a16eb6b1ff3 (maintenance: skip bootout/bootstrap when plist is registered, 2021-08-24), an unused call to launchctl_get_uid() was added along with it. That call appears to have been copy/pasted from launchctl_boot_plist(). Since we can remove that, we can also get rid of the "result" variable, whose only purpose was allow for the free() between its assignment and the return. That pattern also appears to have been copy/pasted from launchctl_boot_plist(). As the patch shows the returned value from launchctl_get_uid() wasn't used at all in this function. The launchctl_get_uid() function itself just calls xstrfmt() and getuid(), neither of which have any subtle global side-effects, so this removal is safe. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/gc.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/builtin/gc.c b/builtin/gc.c index 43c36024cb..db76af4f31 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1602,9 +1602,7 @@ static int launchctl_remove_plists(const char *cmd) static int launchctl_list_contains_plist(const char *name, const char *cmd) { - int result; struct child_process child = CHILD_PROCESS_INIT; - char *uid = launchctl_get_uid(); strvec_split(&child.args, cmd); strvec_pushl(&child.args, "list", name, NULL); @@ -1615,12 +1613,8 @@ static int launchctl_list_contains_plist(const char *name, const char *cmd) if (start_command(&child)) die(_("failed to start launchctl")); - result = finish_command(&child); - - free(uid); - /* Returns failure if 'name' doesn't exist. */ - return !result; + return !finish_command(&child); } static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd) From ec3cc27ab06c0b7ebaf9aee3eaa6e35719eef8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 05:35:37 +0200 Subject: [PATCH 27/71] difftool: prepare "struct child_process" in cmd_difftool() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the preparation of the "struct child_process" from run_dir_diff() to its only caller, cmd_difftool(). This is in preparation for migrating run_file_diff() to using the run_command() API directly, and to move more of the shared setup of the two to cmd_difftool(). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/difftool.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/builtin/difftool.c b/builtin/difftool.c index 6a9242a803..9f08a8f3fd 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -331,7 +331,8 @@ static int checkout_path(unsigned mode, struct object_id *oid, } static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, - int argc, const char **argv) + int argc, const char **argv, + struct child_process *child) { char tmpdir[PATH_MAX]; struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT; @@ -352,7 +353,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, struct index_state wtindex; struct checkout lstate, rstate; int rc, flags = RUN_GIT_CMD, err = 0; - struct child_process child = CHILD_PROCESS_INIT; const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL }; struct hashmap wt_modified, tmp_modified; int indices_loaded = 0; @@ -387,19 +387,19 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, rdir_len = rdir.len; wtdir_len = wtdir.len; - child.no_stdin = 1; - child.git_cmd = 1; - child.use_shell = 0; - child.clean_on_exit = 1; - child.dir = prefix; - child.out = -1; - strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z", + child->no_stdin = 1; + child->git_cmd = 1; + child->use_shell = 0; + child->clean_on_exit = 1; + child->dir = prefix; + child->out = -1; + strvec_pushl(&child->args, "diff", "--raw", "--no-abbrev", "-z", NULL); for (i = 0; i < argc; i++) - strvec_push(&child.args, argv[i]); - if (start_command(&child)) + strvec_push(&child->args, argv[i]); + if (start_command(child)) die("could not obtain raw diff"); - fp = xfdopen(child.out, "r"); + fp = xfdopen(child->out, "r"); /* Build index info for left and right sides of the diff */ i = 0; @@ -525,7 +525,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, fclose(fp); fp = NULL; - if (finish_command(&child)) { + if (finish_command(child)) { ret = error("error occurred running diff --raw"); goto finish; } @@ -719,6 +719,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")), OPT_END() }; + struct child_process child = CHILD_PROCESS_INIT; git_config(difftool_config, NULL); symlinks = has_symlinks; @@ -769,6 +770,6 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) * each file that changed. */ if (dir_diff) - return run_dir_diff(extcmd, symlinks, prefix, argc, argv); + return run_dir_diff(extcmd, symlinks, prefix, argc, argv, &child); return run_file_diff(prompt, prefix, argc, argv); } From b4c7aab7b9fa7f97c6328751b2cc429189b08514 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 13 Sep 2021 05:35:38 +0200 Subject: [PATCH 28/71] difftool: prepare "diff" cmdline in cmd_difftool() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We call into either run_dir_diff() or run_file_diff(), each of which sets up a child argv starting with "diff" and some hard-coded options (depending on which mode we're using). Let's extract that logic into the caller, which will make it easier to modify the options for cases which affect both functions. Signed-off-by: Jeff King Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/difftool.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/builtin/difftool.c b/builtin/difftool.c index 9f08a8f3fd..f8fcc67640 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -331,7 +331,6 @@ static int checkout_path(unsigned mode, struct object_id *oid, } static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, - int argc, const char **argv, struct child_process *child) { char tmpdir[PATH_MAX]; @@ -393,10 +392,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, child->clean_on_exit = 1; child->dir = prefix; child->out = -1; - strvec_pushl(&child->args, "diff", "--raw", "--no-abbrev", "-z", - NULL); - for (i = 0; i < argc; i++) - strvec_push(&child->args, argv[i]); if (start_command(child)) die("could not obtain raw diff"); fp = xfdopen(child->out, "r"); @@ -683,7 +678,6 @@ static int run_file_diff(int prompt, const char *prefix, env[2] = "GIT_DIFFTOOL_NO_PROMPT=true"; - strvec_push(&args, "diff"); for (i = 0; i < argc; i++) strvec_push(&args, argv[i]); return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env); @@ -769,7 +763,12 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) * will invoke a separate instance of 'git-difftool--helper' for * each file that changed. */ + strvec_push(&child.args, "diff"); if (dir_diff) - return run_dir_diff(extcmd, symlinks, prefix, argc, argv, &child); - return run_file_diff(prompt, prefix, argc, argv); + strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL); + strvec_pushv(&child.args, argv); + + if (dir_diff) + return run_dir_diff(extcmd, symlinks, prefix, &child); + return run_file_diff(prompt, prefix, child.args.nr, child.args.v); } From cc5b594788c3fc89ab5e84de2d657ddf36409821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 05:35:39 +0200 Subject: [PATCH 29/71] difftool: use run_command() API in run_file_diff() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the run_file_diff() function to use the run_command() API directly, instead of invoking the run_command_v_opt_cd_env() wrapper. This allows it, like run_dir_diff(), to use the "args" from "struct strvec", instead of the "const char **argv" passed into cmd_difftool(). This will be used in the subsequent commit to get rid of OPT_ARGUMENT() from cmd_difftool(). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- builtin/difftool.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/builtin/difftool.c b/builtin/difftool.c index f8fcc67640..de2e5545c8 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -663,24 +663,23 @@ finish: } static int run_file_diff(int prompt, const char *prefix, - int argc, const char **argv) + struct child_process *child) { - struct strvec args = STRVEC_INIT; const char *env[] = { "GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL, NULL }; - int i; if (prompt > 0) env[2] = "GIT_DIFFTOOL_PROMPT=true"; else if (!prompt) env[2] = "GIT_DIFFTOOL_NO_PROMPT=true"; + child->git_cmd = 1; + child->dir = prefix; + strvec_pushv(&child->env_array, env); - for (i = 0; i < argc; i++) - strvec_push(&args, argv[i]); - return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env); + return run_command(child); } int cmd_difftool(int argc, const char **argv, const char *prefix) @@ -770,5 +769,5 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) if (dir_diff) return run_dir_diff(extcmd, symlinks, prefix, &child); - return run_file_diff(prompt, prefix, child.args.nr, child.args.v); + return run_file_diff(prompt, prefix, &child); } From 4c25356e0edaa92e21aadb67579a0263019fbdc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 05:35:40 +0200 Subject: [PATCH 30/71] parse-options API: remove OPTION_ARGUMENT feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As was noted in 1a85b49b87a (parse-options: make OPT_ARGUMENT() more useful, 2019-03-14) there's only ever been one user of the OPT_ARGUMENT(), that user was added in 20de316e334 (difftool: allow running outside Git worktrees with --no-index, 2019-03-14). The OPT_ARGUMENT() feature itself was added way back in 580d5bffdea (parse-options: new option type to treat an option-like parameter as an argument., 2008-03-02), but as discussed in 1a85b49b87a wasn't used until 20de316e334 in 2019. Now that the preceding commit has migrated this code over to using "struct strvec" to manage the "args" member of a "struct child_process", we can just use that directly instead of relying on OPT_ARGUMENT. This has a minor change in behavior in that if we'll pass --no-index we'll now always pass it as the first argument, before we'd pass it in whatever position the caller did. Preserving this was the real value of OPT_ARGUMENT(), but as it turns out we didn't need that either. We can always inject it as the first argument, the other end will parse it just the same. Note that we cannot remove the "out" and "cpidx" members of "struct parse_opt_ctx_t" added in 580d5bffdea, while they were introduced with OPT_ARGUMENT() we since used them for other things. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Documentation/technical/api-parse-options.txt | 5 ----- builtin/difftool.c | 4 +++- parse-options.c | 13 ------------- parse-options.h | 3 --- t/helper/test-parse-options.c | 1 - t/t0040-parse-options.sh | 5 ----- 6 files changed, 3 insertions(+), 28 deletions(-) diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt index 5a60bbfa7f..acfd5dc1d8 100644 --- a/Documentation/technical/api-parse-options.txt +++ b/Documentation/technical/api-parse-options.txt @@ -198,11 +198,6 @@ There are some macros to easily define options: The filename will be prefixed by passing the filename along with the prefix argument of `parse_options()` to `prefix_filename()`. -`OPT_ARGUMENT(long, &int_var, description)`:: - Introduce a long-option argument that will be kept in `argv[]`. - If this option was seen, `int_var` will be set to one (except - if a `NULL` pointer was passed). - `OPT_NUMBER_CALLBACK(&var, description, func_ptr)`:: Recognize numerical options like -123 and feed the integer as if it was an argument to the function given by `func_ptr`. diff --git a/builtin/difftool.c b/builtin/difftool.c index de2e5545c8..bb9fe7245a 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -709,7 +709,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) "tool returns a non - zero exit code")), OPT_STRING('x', "extcmd", &extcmd, N_("command"), N_("specify a custom command for viewing diffs")), - OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")), + OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")), OPT_END() }; struct child_process child = CHILD_PROCESS_INIT; @@ -763,6 +763,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) * each file that changed. */ strvec_push(&child.args, "diff"); + if (no_index) + strvec_push(&child.args, "--no-index"); if (dir_diff) strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL); strvec_pushv(&child.args, argv); diff --git a/parse-options.c b/parse-options.c index 2abff136a1..55c5821b08 100644 --- a/parse-options.c +++ b/parse-options.c @@ -310,19 +310,6 @@ static enum parse_opt_result parse_long_opt( again: if (!skip_prefix(arg, long_name, &rest)) rest = NULL; - if (options->type == OPTION_ARGUMENT) { - if (!rest) - continue; - if (*rest == '=') - return error(_("%s takes no value"), - optname(options, flags)); - if (*rest) - continue; - if (options->value) - *(int *)options->value = options->defval; - p->out[p->cpidx++] = arg - 2; - return PARSE_OPT_DONE; - } if (!rest) { /* abbreviated? */ if (!(p->flags & PARSE_OPT_KEEP_UNKNOWN) && diff --git a/parse-options.h b/parse-options.h index a845a9d952..39d9088254 100644 --- a/parse-options.h +++ b/parse-options.h @@ -8,7 +8,6 @@ enum parse_opt_type { /* special types */ OPTION_END, - OPTION_ARGUMENT, OPTION_GROUP, OPTION_NUMBER, OPTION_ALIAS, @@ -155,8 +154,6 @@ struct option { #define OPT_INTEGER_F(s, l, v, h, f) { OPTION_INTEGER, (s), (l), (v), N_("n"), (h), (f) } #define OPT_END() { OPTION_END } -#define OPT_ARGUMENT(l, v, h) { OPTION_ARGUMENT, 0, (l), (v), NULL, \ - (h), PARSE_OPT_NOARG, NULL, 1 } #define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } #define OPT_BIT(s, l, v, h, b) OPT_BIT_F(s, l, v, h, b, 0) #define OPT_BITOP(s, l, v, h, set, clear) { OPTION_BITOP, (s), (l), (v), NULL, (h), \ diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 2051ce57db..a282b6ff13 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -134,7 +134,6 @@ int cmd__parse_options(int argc, const char **argv) OPT_NOOP_NOARG(0, "obsolete"), OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), OPT_GROUP("Magic arguments"), - OPT_ARGUMENT("quux", NULL, "means --quux"), OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", number_callback), { OPTION_COUNTUP, '+', NULL, &boolean, NULL, "same as -b", diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index ad4746d899..da310ed29b 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -37,7 +37,6 @@ String options --list add str to list Magic arguments - --quux means --quux -NUM set integer to NUM + same as -b --ambiguous positive ambiguity @@ -263,10 +262,6 @@ test_expect_success 'detect possible typos' ' test_cmp typo.err output.err ' -test_expect_success 'keep some options as arguments' ' - test-tool parse-options --expect="arg 00: --quux" --quux -' - cat >expect <<\EOF Callback: "four", 0 boolean: 5 From 59a399ed36247b26a1092a130ef660647a169981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:23 +0200 Subject: [PATCH 31/71] INSTALL: mention that we need libcurl 7.19.4 or newer to build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without NO_CURL=Y we require at least version "7.19.4" of libcurl, see 644de29e220 (http: drop support for curl < 7.19.4, 2021-07-30). Let's document this in the "INSTALL" document. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- INSTALL | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTALL b/INSTALL index d593f62874..4140a3f5c8 100644 --- a/INSTALL +++ b/INSTALL @@ -144,6 +144,10 @@ Issues of note: not need that functionality, use NO_CURL to build without it. + Git requires version "7.19.4" or later of "libcurl" to build + without NO_CURL. This version requirement may be bumped in + the future. + - "expat" library; git-http-push uses it for remote lock management over DAV. Similar to "curl" above, this is optional (with NO_EXPAT). From 2d4032c2fb8e38960103649f0a70b48e4feeda36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:24 +0200 Subject: [PATCH 32/71] Makefile: drop support for curl < 7.9.8 (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 1119a15b5c8 (http: drop support for curl < 7.11.1, 2021-07-30) support for curl versions older than 7.11.1 was removed, and we currently require at least version 7.19.4, see 644de29e220 (http: drop support for curl < 7.19.4, 2021-07-30). In those changes this Makefile-specific check added in 0890098780f (Decide whether to build http-push in the Makefile, 2005-11-18) was missed, now that we're never going to use such an ancient curl version we don't need to check that we have at least 7.9.8 here. I have no idea what in http-push.c broke on versions older than that. This does not impact "NO_CURL" setups, as this is in the "else" branch after that check. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- Makefile | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 429c276058..378f58b950 100644 --- a/Makefile +++ b/Makefile @@ -1436,15 +1436,8 @@ else REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES) PROGRAM_OBJS += http-fetch.o PROGRAMS += $(REMOTE_CURL_NAMES) - curl_check := $(shell (echo 070908; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p) - ifeq "$(curl_check)" "070908" - ifndef NO_EXPAT - PROGRAM_OBJS += http-push.o - else - EXCLUDED_PROGRAMS += git-http-push - endif - else - EXCLUDED_PROGRAMS += git-http-push + ifndef NO_EXPAT + PROGRAM_OBJS += http-push.o endif curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p) ifeq "$(curl_check)" "072200" From 7ce3dcd5335981d6d339bd22592ef9fa72808692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:25 +0200 Subject: [PATCH 33/71] http: drop support for curl < 7.18.0 (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 644de29e220 (http: drop support for curl < 7.19.4, 2021-07-30) we dropped support for curl < 7.19.4, so we can drop support for this non-obvious dependency on curl < 7.18.0. It's non-obvious because in curl's hex version notation 0x071800 is version 7.24.0, *not* 7.18.0, so at a glance this patch looks incorrect. But it's correct, because the existing version check being removed here is wrong. The check guards use of the following curl defines: CURLPROXY_SOCKS4 7.10 CURLPROXY_SOCKS4A 7.18.0 CURLPROXY_SOCKS5 7.10 CURLPROXY_SOCKS5_HOSTNAME 7.18.0 I.e. the oldest version that has these is in fact 7.18.0, not 7.24.0. That we were checking 7.24.0 is just an mistake in 6d7afe07f29 (remote-http(s): support SOCKS proxies, 2015-10-26), i.e. its author confusing base 10 and base 16. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- http.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/http.c b/http.c index a0f169d2fe..56856178bf 100644 --- a/http.c +++ b/http.c @@ -927,7 +927,6 @@ static CURL *get_curl_handle(void) */ curl_easy_setopt(result, CURLOPT_PROXY, ""); } else if (curl_http_proxy) { -#if LIBCURL_VERSION_NUM >= 0x071800 if (starts_with(curl_http_proxy, "socks5h")) curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME); @@ -940,7 +939,6 @@ static CURL *get_curl_handle(void) else if (starts_with(curl_http_proxy, "socks")) curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); -#endif #if LIBCURL_VERSION_NUM >= 0x073400 else if (starts_with(curl_http_proxy, "https")) { curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS); From 2a7f64616a38ee13e1986672c1e26de58cd98896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:26 +0200 Subject: [PATCH 34/71] http: correct version check for CURL_HTTP_VERSION_2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In d73019feb44 (http: add support selecting http version, 2018-11-08) a dependency was added on CURL_HTTP_VERSION_2, but this feature was introduced in curl version 7.43.0, not 7.47.0, as the incorrect version check led us to believe. As looking through the history of that commit on the mailing list will reveal[1], the reason for this is that an earlier version of it depended on CURL_HTTP_VERSION_2TLS, which was introduced in libcurl 7.47.0. But the version that made it in in d73019feb44 had dropped the dependency on CURL_HTTP_VERSION_2TLS, but the corresponding version check was not corrected. The newest symbol we depend on is CURL_HTTP_VERSION_2. It was added in 7.33.0, but the CURL_HTTP_VERSION_2 alias we used was added in 7.47.0. So we could support an even older version here, but let's just correct the checked version. 1. https://lore.kernel.org/git/pull.69.git.gitgitgadget@gmail.com/ Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- http.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/http.c b/http.c index 56856178bf..b82b5b7a53 100644 --- a/http.c +++ b/http.c @@ -732,7 +732,7 @@ static long get_curl_allowed_protocols(int from_user) return allowed_protocols; } -#if LIBCURL_VERSION_NUM >=0x072f00 +#if LIBCURL_VERSION_NUM >=0x072b00 static int get_curl_http_version_opt(const char *version_string, long *opt) { int i; @@ -774,7 +774,7 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2); } -#if LIBCURL_VERSION_NUM >= 0x072f00 // 7.47.0 +#if LIBCURL_VERSION_NUM >= 0x072b00 if (curl_http_version) { long opt; if (!get_curl_http_version_opt(curl_http_version, &opt)) { From 905a02880473c54f6c817e4ec8262d195d149940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:27 +0200 Subject: [PATCH 35/71] http: correct curl version check for CURLOPT_PINNEDPUBLICKEY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In aeff8a61216 (http: implement public key pinning, 2016-02-15) a dependency and warning() was added if curl older than 7.44.0 was used, but the relevant code depended on CURLOPT_PINNEDPUBLICKEY, introduced in 7.39.0. Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- http.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/http.c b/http.c index b82b5b7a53..543faad987 100644 --- a/http.c +++ b/http.c @@ -59,7 +59,7 @@ static struct { static const char *ssl_key; static const char *ssl_capath; static const char *curl_no_proxy; -#if LIBCURL_VERSION_NUM >= 0x072c00 +#if LIBCURL_VERSION_NUM >= 0x072700 static const char *ssl_pinnedkey; #endif static const char *ssl_cainfo; @@ -373,10 +373,10 @@ static int http_options(const char *var, const char *value, void *cb) } if (!strcmp("http.pinnedpubkey", var)) { -#if LIBCURL_VERSION_NUM >= 0x072c00 +#if LIBCURL_VERSION_NUM >= 0x072700 return git_config_pathname(&ssl_pinnedkey, var, value); #else - warning(_("Public key pinning not supported with cURL < 7.44.0")); + warning(_("Public key pinning not supported with cURL < 7.39.0")); return 0; #endif } @@ -845,7 +845,7 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); if (ssl_capath != NULL) curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); -#if LIBCURL_VERSION_NUM >= 0x072c00 +#if LIBCURL_VERSION_NUM >= 0x072700 if (ssl_pinnedkey != NULL) curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey); #endif From e4ff3b67c2ad854113331029dea9843928a9c5ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:28 +0200 Subject: [PATCH 36/71] http: centralize the accounting of libcurl dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As discussed in 644de29e220 (http: drop support for curl < 7.19.4, 2021-07-30) checking against LIBCURL_VERSION_NUM isn't as reliable as checking specific symbols present in curl, as some distros have been known to backport features. However, while some of the curl_easy_setopt() arguments we rely on are macros, others are enum, and we can't assume that those that are macros won't change into enums in the future. So we're still going to have to check LIBCURL_VERSION_NUM, but by doing that in one central place and using a macro definition of our own, anyone who's backporting features can define it themselves, and thus have access to more modern curl features that they backported, even if they didn't bump the LIBCURL_VERSION_NUM. More importantly, as shown in a preceding commit doing these version checks makes for hard to read and possibly buggy code, as shown by the bug fixed there where we were conflating base 10 for base 16 when comparing the version. By doing them all in one place we'll hopefully reduce the chances of such future mistakes, furthermore it now becomes easier to see at a glance what the oldest supported version is, which makes it easier to reason about any future deprecation similar to the recent e48a623dea0 (Merge branch 'ab/http-drop-old-curl', 2021-08-24). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-curl-compat.h | 117 ++++++++++++++++++++++++++++++++++++++++++++++ http.c | 29 ++++++------ imap-send.c | 2 +- 3 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 git-curl-compat.h diff --git a/git-curl-compat.h b/git-curl-compat.h new file mode 100644 index 0000000000..7ad87e89ed --- /dev/null +++ b/git-curl-compat.h @@ -0,0 +1,117 @@ +#ifndef GIT_CURL_COMPAT_H +#define GIT_CURL_COMPAT_H +#include + +/** + * This header centralizes the declaration of our libcurl dependencies + * to make it easy to discover the oldest versions we support, and to + * inform decisions about removing support for older libcurl in the + * future. + * + * The oldest supported version of curl is documented in the "INSTALL" + * document. + * + * The source of truth for what versions have which symbols is + * https://github.com/curl/curl/blob/master/docs/libcurl/symbols-in-versions; + * the release dates are taken from curl.git (at + * https://github.com/curl/curl/). + * + * For each X symbol we need from curl we define our own + * GIT_CURL_HAVE_X. If multiple similar symbols with the same prefix + * were defined in the same version we pick one and check for that name. + * + * Keep any symbols in date order of when their support was + * introduced, oldest first, in the official version of cURL library. + */ + +/** + * CURLOPT_TCP_KEEPALIVE was added in 7.25.0, released in March 2012. + */ +#if LIBCURL_VERSION_NUM >= 0x071900 +#define GITCURL_HAVE_CURLOPT_TCP_KEEPALIVE 1 +#endif + + +/** + * CURLOPT_LOGIN_OPTIONS was added in 7.34.0, released in December + * 2013. + * + * If we start requiring 7.34.0 we might also be able to remove the + * code conditional on USE_CURL_FOR_IMAP_SEND in imap-send.c, see + * 1e16b255b95 (git-imap-send: use libcurl for implementation, + * 2014-11-09) and the check it added for "072200" in the Makefile. + + */ +#if LIBCURL_VERSION_NUM >= 0x072200 +#define GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS 1 +#endif + +/** + * CURL_SSLVERSION_TLSv1_[012] was added in 7.34.0, released in + * December 2013. + */ +#if LIBCURL_VERSION_NUM >= 0x072200 +#define GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_0 +#endif + +/** + * CURLOPT_PINNEDPUBLICKEY was added in 7.39.0, released in November + * 2014. + */ +#if LIBCURL_VERSION_NUM >= 0x072c00 +#define GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY 1 +#endif + +/** + * CURL_HTTP_VERSION_2 was added in 7.43.0, released in June 2015. + * + * The CURL_HTTP_VERSION_2 alias (but not CURL_HTTP_VERSION_2_0) has + * always been a macro, not an enum field (checked on curl version + * 7.78.0) + */ +#if LIBCURL_VERSION_NUM >= 0x072b00 +#define GIT_CURL_HAVE_CURL_HTTP_VERSION_2 1 +#endif + +/** + * CURLSSLOPT_NO_REVOKE was added in 7.44.0, released in August 2015. + * + * The CURLSSLOPT_NO_REVOKE is, has always been a macro, not an enum + * field (checked on curl version 7.78.0) + */ +#if LIBCURL_VERSION_NUM >= 0x072c00 +#define GIT_CURL_HAVE_CURLSSLOPT_NO_REVOKE 1 +#endif + +/** + * CURLOPT_PROXY_CAINFO was added in 7.52.0, released in August 2017. + */ +#if LIBCURL_VERSION_NUM >= 0x073400 +#define GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO 1 +#endif + +/** + * CURLOPT_PROXY_{KEYPASSWD,SSLCERT,SSLKEY} was added in 7.52.0, + * released in August 2017. + */ +#if LIBCURL_VERSION_NUM >= 0x073400 +#define GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD 1 +#endif + +/** + * CURL_SSLVERSION_TLSv1_3 was added in 7.53.0, released in February + * 2017. + */ +#if LIBCURL_VERSION_NUM >= 0x073400 +#define GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_3 1 +#endif + +/** + * CURLSSLSET_{NO_BACKENDS,OK,TOO_LATE,UNKNOWN_BACKEND} were added in + * 7.56.0, released in September 2017. + */ +#if LIBCURL_VERSION_NUM >= 0x073800 +#define GIT_CURL_HAVE_CURLSSLSET_NO_BACKENDS +#endif + +#endif diff --git a/http.c b/http.c index 543faad987..94eefe9708 100644 --- a/http.c +++ b/http.c @@ -1,4 +1,5 @@ #include "git-compat-util.h" +#include "git-curl-compat.h" #include "http.h" #include "config.h" #include "pack.h" @@ -47,19 +48,19 @@ static struct { { "sslv2", CURL_SSLVERSION_SSLv2 }, { "sslv3", CURL_SSLVERSION_SSLv3 }, { "tlsv1", CURL_SSLVERSION_TLSv1 }, -#if LIBCURL_VERSION_NUM >= 0x072200 +#ifdef GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_0 { "tlsv1.0", CURL_SSLVERSION_TLSv1_0 }, { "tlsv1.1", CURL_SSLVERSION_TLSv1_1 }, { "tlsv1.2", CURL_SSLVERSION_TLSv1_2 }, #endif -#if LIBCURL_VERSION_NUM >= 0x073400 +#ifdef GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_3 { "tlsv1.3", CURL_SSLVERSION_TLSv1_3 }, #endif }; static const char *ssl_key; static const char *ssl_capath; static const char *curl_no_proxy; -#if LIBCURL_VERSION_NUM >= 0x072700 +#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY static const char *ssl_pinnedkey; #endif static const char *ssl_cainfo; @@ -373,7 +374,7 @@ static int http_options(const char *var, const char *value, void *cb) } if (!strcmp("http.pinnedpubkey", var)) { -#if LIBCURL_VERSION_NUM >= 0x072700 +#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY return git_config_pathname(&ssl_pinnedkey, var, value); #else warning(_("Public key pinning not supported with cURL < 7.39.0")); @@ -500,7 +501,7 @@ static int has_cert_password(void) return 1; } -#if LIBCURL_VERSION_NUM >= 0x073400 +#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD static int has_proxy_cert_password(void) { if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1) @@ -516,7 +517,7 @@ static int has_proxy_cert_password(void) } #endif -#if LIBCURL_VERSION_NUM >= 0x071900 +#ifdef GITCURL_HAVE_CURLOPT_TCP_KEEPALIVE static void set_curl_keepalive(CURL *c) { curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1); @@ -732,7 +733,7 @@ static long get_curl_allowed_protocols(int from_user) return allowed_protocols; } -#if LIBCURL_VERSION_NUM >=0x072b00 +#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_2 static int get_curl_http_version_opt(const char *version_string, long *opt) { int i; @@ -774,7 +775,7 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2); } -#if LIBCURL_VERSION_NUM >= 0x072b00 +#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_2 if (curl_http_version) { long opt; if (!get_curl_http_version_opt(curl_http_version, &opt)) { @@ -805,7 +806,7 @@ static CURL *get_curl_handle(void) if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) && !http_schannel_check_revoke) { -#if LIBCURL_VERSION_NUM >= 0x072c00 +#ifdef GIT_CURL_HAVE_CURLSSLOPT_NO_REVOKE curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); #else warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0")); @@ -845,20 +846,20 @@ static CURL *get_curl_handle(void) curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); if (ssl_capath != NULL) curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); -#if LIBCURL_VERSION_NUM >= 0x072700 +#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY if (ssl_pinnedkey != NULL) curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey); #endif if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) && !http_schannel_use_ssl_cainfo) { curl_easy_setopt(result, CURLOPT_CAINFO, NULL); -#if LIBCURL_VERSION_NUM >= 0x073400 +#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL); #endif } else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) { if (ssl_cainfo != NULL) curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); -#if LIBCURL_VERSION_NUM >= 0x073400 +#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO if (http_proxy_ssl_ca_info != NULL) curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info); #endif @@ -939,7 +940,7 @@ static CURL *get_curl_handle(void) else if (starts_with(curl_http_proxy, "socks")) curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); -#if LIBCURL_VERSION_NUM >= 0x073400 +#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD else if (starts_with(curl_http_proxy, "https")) { curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS); @@ -1004,7 +1005,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) free(normalized_url); string_list_clear(&config.vars, 1); -#if LIBCURL_VERSION_NUM >= 0x073800 +#ifdef GIT_CURL_HAVE_CURLSSLSET_NO_BACKENDS if (http_ssl_backend) { const curl_ssl_backend **backends; struct strbuf buf = STRBUF_INIT; diff --git a/imap-send.c b/imap-send.c index 49a5f8aa59..e6090a0346 100644 --- a/imap-send.c +++ b/imap-send.c @@ -1441,7 +1441,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred) curl_easy_setopt(curl, CURLOPT_PORT, server.port); if (server.auth_method) { -#if LIBCURL_VERSION_NUM < 0x072200 +#ifndef GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS warning("No LOGIN_OPTIONS support in this cURL version"); #else struct strbuf auth = STRBUF_INIT; From 32da6e6dafb1db563b6fa1ec80a21d58268e4ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86var=20Arnfj=C3=B6r=C3=B0=20Bjarmason?= Date: Mon, 13 Sep 2021 16:51:29 +0200 Subject: [PATCH 37/71] http: don't hardcode the value of CURL_SOCKOPT_OK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new git-curl-compat.h header to define CURL_SOCKOPT_OK to its known value if we're on an older curl version that doesn't have it. It was hardcoded in http.c in a15d069a198 (http: enable keepalive on TCP sockets, 2013-10-12). Signed-off-by: Ævar Arnfjörð Bjarmason Signed-off-by: Junio C Hamano --- git-curl-compat.h | 11 +++++++++++ http.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/git-curl-compat.h b/git-curl-compat.h index 7ad87e89ed..a308bdb3b9 100644 --- a/git-curl-compat.h +++ b/git-curl-compat.h @@ -20,10 +20,21 @@ * GIT_CURL_HAVE_X. If multiple similar symbols with the same prefix * were defined in the same version we pick one and check for that name. * + * We may also define a missing CURL_* symbol to its known value, if + * doing so is sufficient to add support for it to older versions that + * don't have it. + * * Keep any symbols in date order of when their support was * introduced, oldest first, in the official version of cURL library. */ +/** + * CURL_SOCKOPT_OK was added in 7.21.5, released in April 2011. + */ +#if LIBCURL_VERSION_NUM < 0x071505 +#define CURL_SOCKOPT_OK 0 +#endif + /** * CURLOPT_TCP_KEEPALIVE was added in 7.25.0, released in March 2012. */ diff --git a/http.c b/http.c index 94eefe9708..d7c20493d7 100644 --- a/http.c +++ b/http.c @@ -537,7 +537,7 @@ static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type) if (rc < 0) warning_errno("unable to set SO_KEEPALIVE on socket"); - return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */ + return CURL_SOCKOPT_OK; } static void set_curl_keepalive(CURL *c) From 282073cce2e7f0fba1f2862a324118fdfa10719d Mon Sep 17 00:00:00 2001 From: Miriam Rubio Date: Mon, 13 Sep 2021 19:38:59 +0200 Subject: [PATCH 38/71] t6030-bisect-porcelain: add tests to control bisect run exit cases There is a gap on bisect run test coverage related with error exits. Add two tests to control these error cases. Signed-off-by: Miriam Rubio Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index a1baf4e451..5986fbecd1 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -962,4 +962,15 @@ test_expect_success 'bisect handles annotated tags' ' grep "$bad is the first bad commit" output ' +test_expect_success 'bisect run fails with exit code equals or greater than 128' ' + write_script test_script.sh <<-\EOF && + exit 128 + EOF + test_must_fail git bisect run ./test_script.sh && + write_script test_script.sh <<-\EOF && + exit 255 + EOF + test_must_fail git bisect run ./test_script.sh +' + test_done From 5fe973b9122903ed540c468b8fb347ab0b6ca6ff Mon Sep 17 00:00:00 2001 From: Miriam Rubio Date: Mon, 13 Sep 2021 19:39:00 +0200 Subject: [PATCH 39/71] t6030-bisect-porcelain: add test for bisect visualize Add a test to control breakages in bisect visualize command. Signed-off-by: Miriam Rubio Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 5986fbecd1..1be85d064e 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -973,4 +973,11 @@ test_expect_success 'bisect run fails with exit code equals or greater than 128' test_must_fail git bisect run ./test_script.sh ' +test_expect_success 'bisect visualize with a filename with dash and space' ' + echo "My test line" >>"./-hello 2" && + git add -- "./-hello 2" && + git commit --quiet -m "Add test line" -- "./-hello 2" && + git bisect visualize -p -- "-hello 2" +' + test_done From 3f36e6f30c5ee8d337dffa65c487080b87b29030 Mon Sep 17 00:00:00 2001 From: Pranit Bauva Date: Mon, 13 Sep 2021 19:39:01 +0200 Subject: [PATCH 40/71] run-command: make `exists_in_PATH()` non-static Remove the `static` keyword from `exists_in_PATH()` function and declare the function in `run-command.h` file. The function will be used in bisect_visualize() in a later commit. Mentored by: Christian Couder Mentored by: Johannes Schindelin Signed-off-by: Tanushree Tumane Signed-off-by: Miriam Rubio Signed-off-by: Junio C Hamano --- run-command.c | 4 ++-- run-command.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/run-command.c b/run-command.c index f72e72cce7..da02553f44 100644 --- a/run-command.c +++ b/run-command.c @@ -210,9 +210,9 @@ static char *locate_in_PATH(const char *file) return NULL; } -static int exists_in_PATH(const char *file) +int exists_in_PATH(const char *command) { - char *r = locate_in_PATH(file); + char *r = locate_in_PATH(command); int found = r != NULL; free(r); return found; diff --git a/run-command.h b/run-command.h index af1296769f..aad027984d 100644 --- a/run-command.h +++ b/run-command.h @@ -182,6 +182,18 @@ void child_process_clear(struct child_process *); int is_executable(const char *name); +/** + * Check if the command exists on $PATH. This emulates the path search that + * execvp would perform, without actually executing the command so it + * can be used before fork() to prepare to run a command using + * execve() or after execvp() to diagnose why it failed. + * + * The caller should ensure that command contains no directory separators. + * + * Returns 1 if it is found in $PATH or 0 if the command could not be found. + */ +int exists_in_PATH(const char *command); + /** * Start a sub-process. Takes a pointer to a `struct child_process` * that specifies the details and returns pipe FDs (if requested). From 5e1f28d20600ea7d482db875cbd759b410c095b7 Mon Sep 17 00:00:00 2001 From: Pranit Bauva Date: Mon, 13 Sep 2021 19:39:02 +0200 Subject: [PATCH 41/71] bisect--helper: reimplement `bisect_visualize()` shell function in C Reimplement the `bisect_visualize()` shell function in C and also add `--bisect-visualize` subcommand to `git bisect--helper` to call it from git-bisect.sh. Mentored-by: Christian Couder Mentored-by: Johannes Schindelin Signed-off-by: Tanushree Tumane Signed-off-by: Miriam Rubio Signed-off-by: Junio C Hamano --- builtin/bisect--helper.c | 48 +++++++++++++++++++++++++++++++++++++++- git-bisect.sh | 25 +-------------------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index f184eaeac6..1465310734 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -30,6 +30,7 @@ static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --bisect-state (good|old) [...]"), N_("git bisect--helper --bisect-replay "), N_("git bisect--helper --bisect-skip [(|)...]"), + N_("git bisect--helper --bisect-visualize"), NULL }; @@ -1036,6 +1037,44 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar return res; } +static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc) +{ + struct strvec args = STRVEC_INIT; + int flags = RUN_COMMAND_NO_STDIN, res = 0; + struct strbuf sb = STRBUF_INIT; + + if (bisect_next_check(terms, NULL) != 0) + return BISECT_FAILED; + + if (!argc) { + if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") || + getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) { + strvec_push(&args, "gitk"); + } else { + strvec_push(&args, "log"); + flags |= RUN_GIT_CMD; + } + } else { + if (argv[0][0] == '-') { + strvec_push(&args, "log"); + flags |= RUN_GIT_CMD; + } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git")) + flags |= RUN_GIT_CMD; + + strvec_pushv(&args, argv); + } + + strvec_pushl(&args, "--bisect", "--", NULL); + + strbuf_read_file(&sb, git_path_bisect_names(), 0); + sq_dequote_to_strvec(sb.buf, &args); + strbuf_release(&sb); + + res = run_command_v_opt(args.v, flags); + strvec_clear(&args); + return res; +} + int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { enum { @@ -1048,7 +1087,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) BISECT_STATE, BISECT_LOG, BISECT_REPLAY, - BISECT_SKIP + BISECT_SKIP, + BISECT_VISUALIZE, } cmdmode = 0; int res = 0, nolog = 0; struct option options[] = { @@ -1070,6 +1110,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) N_("replay the bisection process from the given file"), BISECT_REPLAY), OPT_CMDMODE(0, "bisect-skip", &cmdmode, N_("skip some commits for checkout"), BISECT_SKIP), + OPT_CMDMODE(0, "bisect-visualize", &cmdmode, + N_("visualize the bisection"), BISECT_VISUALIZE), OPT_BOOL(0, "no-log", &nolog, N_("no log for BISECT_WRITE")), OPT_END() @@ -1131,6 +1173,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) get_terms(&terms); res = bisect_skip(&terms, argv, argc); break; + case BISECT_VISUALIZE: + get_terms(&terms); + res = bisect_visualize(&terms, argv, argc); + break; default: BUG("unknown subcommand %d", cmdmode); } diff --git a/git-bisect.sh b/git-bisect.sh index 6a7afaea8d..95f7f3fb8c 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -39,29 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" TERM_BAD=bad TERM_GOOD=good -bisect_visualize() { - git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit - - if test $# = 0 - then - if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" && - type gitk >/dev/null 2>&1 - then - set gitk - else - set git log - fi - else - case "$1" in - git*|tig) ;; - -*) set git log "$@" ;; - *) set git "$@" ;; - esac - fi - - eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES") -} - bisect_run () { git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit @@ -152,7 +129,7 @@ case "$#" in # Not sure we want "next" at the UI level anymore. git bisect--helper --bisect-next "$@" || exit ;; visualize|view) - bisect_visualize "$@" ;; + git bisect--helper --bisect-visualize "$@" || exit;; reset) git bisect--helper --bisect-reset "$@" ;; replay) From d1bbbe45df8f1019364d3bdf458b49de1a693721 Mon Sep 17 00:00:00 2001 From: Tanushree Tumane Date: Mon, 13 Sep 2021 19:39:03 +0200 Subject: [PATCH 42/71] bisect--helper: reimplement `bisect_run` shell function in C Reimplement the `bisect_run()` shell function in C and also add `--bisect-run` subcommand to `git bisect--helper` to call it from git-bisect.sh. Mentored-by: Christian Couder Signed-off-by: Tanushree Tumane Signed-off-by: Miriam Rubio Signed-off-by: Junio C Hamano --- builtin/bisect--helper.c | 105 +++++++++++++++++++++++++++++++++++++++ git-bisect.sh | 62 +---------------------- 2 files changed, 106 insertions(+), 61 deletions(-) diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 1465310734..ea966268df 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -18,6 +18,7 @@ static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG") static GIT_PATH_FUNC(git_path_head_name, "head-name") static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES") static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT") +static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --bisect-reset []"), @@ -31,6 +32,7 @@ static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --bisect-replay "), N_("git bisect--helper --bisect-skip [(|)...]"), N_("git bisect--helper --bisect-visualize"), + N_("git bisect--helper --bisect-run ..."), NULL }; @@ -144,6 +146,19 @@ static int append_to_file(const char *path, const char *format, ...) return res; } +static int print_file_to_stdout(const char *path) +{ + int fd = open(path, O_RDONLY); + int ret = 0; + + if (fd < 0) + return error_errno(_("cannot open file '%s' for reading"), path); + if (copy_fd(fd, 1) < 0) + ret = error_errno(_("failed to read '%s'"), path); + close(fd); + return ret; +} + static int check_term_format(const char *term, const char *orig_term) { int res; @@ -1075,6 +1090,87 @@ static int bisect_visualize(struct bisect_terms *terms, const char **argv, int a return res; } +static int bisect_run(struct bisect_terms *terms, const char **argv, int argc) +{ + int res = BISECT_OK; + struct strbuf command = STRBUF_INIT; + struct strvec args = STRVEC_INIT; + struct strvec run_args = STRVEC_INIT; + const char *new_state; + int temporary_stdout_fd, saved_stdout; + + if (bisect_next_check(terms, NULL)) + return BISECT_FAILED; + + if (argc) + sq_quote_argv(&command, argv); + else { + error(_("bisect run failed: no command provided.")); + return BISECT_FAILED; + } + + strvec_push(&run_args, command.buf); + + while (1) { + strvec_clear(&args); + + printf(_("running %s\n"), command.buf); + res = run_command_v_opt(run_args.v, RUN_USING_SHELL); + + if (res < 0 || 128 <= res) { + error(_("bisect run failed: exit code %d from" + " '%s' is < 0 or >= 128"), res, command.buf); + strbuf_release(&command); + return res; + } + + if (res == 125) + new_state = "skip"; + else if (!res) + new_state = terms->term_good; + else + new_state = terms->term_bad; + + temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666); + + if (temporary_stdout_fd < 0) + return error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run()); + + fflush(stdout); + saved_stdout = dup(1); + dup2(temporary_stdout_fd, 1); + + res = bisect_state(terms, &new_state, 1); + + fflush(stdout); + dup2(saved_stdout, 1); + close(saved_stdout); + close(temporary_stdout_fd); + + print_file_to_stdout(git_path_bisect_run()); + + if (res == BISECT_ONLY_SKIPPED_LEFT) + error(_("bisect run cannot continue any more")); + else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) { + printf(_("bisect run success")); + res = BISECT_OK; + } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) { + printf(_("bisect found first bad commit")); + res = BISECT_OK; + } else if (res) { + error(_("bisect run failed:'git bisect--helper --bisect-state" + " %s' exited with error code %d"), args.v[0], res); + } else { + continue; + } + + strbuf_release(&command); + strvec_clear(&args); + strvec_clear(&run_args); + return res; + } +} + int cmd_bisect__helper(int argc, const char **argv, const char *prefix) { enum { @@ -1089,6 +1185,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) BISECT_REPLAY, BISECT_SKIP, BISECT_VISUALIZE, + BISECT_RUN, } cmdmode = 0; int res = 0, nolog = 0; struct option options[] = { @@ -1112,6 +1209,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) N_("skip some commits for checkout"), BISECT_SKIP), OPT_CMDMODE(0, "bisect-visualize", &cmdmode, N_("visualize the bisection"), BISECT_VISUALIZE), + OPT_CMDMODE(0, "bisect-run", &cmdmode, + N_("use ... to automatically bisect."), BISECT_RUN), OPT_BOOL(0, "no-log", &nolog, N_("no log for BISECT_WRITE")), OPT_END() @@ -1177,6 +1276,12 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) get_terms(&terms); res = bisect_visualize(&terms, argv, argc); break; + case BISECT_RUN: + if (!argc) + return error(_("bisect run failed: no command provided.")); + get_terms(&terms); + res = bisect_run(&terms, argv, argc); + break; default: BUG("unknown subcommand %d", cmdmode); } diff --git a/git-bisect.sh b/git-bisect.sh index 95f7f3fb8c..e83d011e17 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -39,66 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" TERM_BAD=bad TERM_GOOD=good -bisect_run () { - git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit - - test -n "$*" || die "$(gettext "bisect run failed: no command provided.")" - - while true - do - command="$@" - eval_gettextln "running \$command" - "$@" - res=$? - - # Check for really bad run error. - if [ $res -lt 0 -o $res -ge 128 ] - then - eval_gettextln "bisect run failed: -exit code \$res from '\$command' is < 0 or >= 128" >&2 - exit $res - fi - - # Find current state depending on run success or failure. - # A special exit code of 125 means cannot test. - if [ $res -eq 125 ] - then - state='skip' - elif [ $res -gt 0 ] - then - state="$TERM_BAD" - else - state="$TERM_GOOD" - fi - - git bisect--helper --bisect-state $state >"$GIT_DIR/BISECT_RUN" - res=$? - - cat "$GIT_DIR/BISECT_RUN" - - if sane_grep "first $TERM_BAD commit could be any of" "$GIT_DIR/BISECT_RUN" \ - >/dev/null - then - gettextln "bisect run cannot continue any more" >&2 - exit $res - fi - - if [ $res -ne 0 ] - then - eval_gettextln "bisect run failed: -'bisect-state \$state' exited with error code \$res" >&2 - exit $res - fi - - if sane_grep "is the first $TERM_BAD commit" "$GIT_DIR/BISECT_RUN" >/dev/null - then - gettextln "bisect run success" - exit 0; - fi - - done -} - get_terms () { if test -s "$GIT_DIR/BISECT_TERMS" then @@ -137,7 +77,7 @@ case "$#" in log) git bisect--helper --bisect-log || exit ;; run) - bisect_run "$@" ;; + git bisect--helper --bisect-run "$@" || exit;; terms) git bisect--helper --bisect-terms "$@" || exit;; *) From 911aba1420522991f9d79cec42d0cb957d2ba00a Mon Sep 17 00:00:00 2001 From: Miriam Rubio Date: Mon, 13 Sep 2021 19:39:04 +0200 Subject: [PATCH 43/71] bisect--helper: retire `--bisect-next-check` subcommand After reimplementation of `git bisect run` in C, `--bisect-next-check` subcommand is not needed anymore. Let's remove it from options list and code. Mentored by: Christian Couder Signed-off-by: Miriam Rubio Signed-off-by: Junio C Hamano --- builtin/bisect--helper.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index ea966268df..bc210b23c8 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -22,7 +22,6 @@ static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN") static const char * const git_bisect_helper_usage[] = { N_("git bisect--helper --bisect-reset []"), - N_("git bisect--helper --bisect-next-check []"), N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"), N_("git bisect--helper --bisect-start [--term-{new,bad}= --term-{old,good}=]" " [--no-checkout] [--first-parent] [ [...]] [--] [...]"), @@ -1230,12 +1229,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix) return error(_("--bisect-reset requires either no argument or a commit")); res = bisect_reset(argc ? argv[0] : NULL); break; - case BISECT_NEXT_CHECK: - if (argc != 2 && argc != 3) - return error(_("--bisect-next-check requires 2 or 3 arguments")); - set_terms(&terms, argv[1], argv[0]); - res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL); - break; case BISECT_TERMS: if (argc > 1) return error(_("--bisect-terms requires 0 or 1 argument")); From ae578de926ef4323c0b6f0748616ee2a569667ac Mon Sep 17 00:00:00 2001 From: Philip Oakley Date: Mon, 13 Sep 2021 22:23:05 +0100 Subject: [PATCH 44/71] doc: config, tell readers of `git help --config` The `git help` command gained the ability to list config variables in 3ac68a93fd (help: add --config to list all available config, 2018-05-26) but failed to tell readers of the config documenation itself. Provide that cross reference. Signed-off-by: Philip Oakley Signed-off-by: Junio C Hamano --- Documentation/git-config.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 2dc4bae6da..992225f612 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -71,6 +71,9 @@ codes are: On success, the command returns the exit code 0. +A list of all available configuration variables can be obtained using the +`git help --config` command. + [[OPTIONS]] OPTIONS ------- From 0fdcfa2f9f59a8bf4202ac3933991f42a4ac1393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= Date: Tue, 14 Sep 2021 00:25:58 -0700 Subject: [PATCH 45/71] t0301: fixes for windows compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation for a future patch that will allow building with Unix Sockets in Windows, workaround a couple of issues from the Mingw-W64 compatibility layer. test -S is not able to detect that a file is a socket, so use test -e instead (through a library function). `mkdir -m` can't represent a valid ACL directly and fails with permission problems, so instead call mkdir followed by chmod, which has been enhanced to do so. The last invocation of mkdir would likely need the same treatment but SYMLINK is unlikely to be enabled on Windows so it has been punted for now. Signed-off-by: Carlo Marcelo Arenas Belón Signed-off-by: Junio C Hamano --- t/t0301-credential-cache.sh | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index ebd5fa5249..698b7159f0 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -9,6 +9,21 @@ test -z "$NO_UNIX_SOCKETS" || { test_done } +uname_s=$(uname -s) +case $uname_s in +*MINGW*) + test_path_is_socket () { + # `test -S` cannot detect Win10's Unix sockets + test_path_exists "$1" + } + ;; +*) + test_path_is_socket () { + test -S "$1" + } + ;; +esac + # don't leave a stale daemon running test_atexit 'git credential-cache exit' @@ -21,7 +36,7 @@ test_expect_success 'socket defaults to ~/.cache/git/credential/socket' ' rmdir -p .cache/git/credential/ " && test_path_is_missing "$HOME/.git-credential-cache" && - test -S "$HOME/.cache/git/credential/socket" + test_path_is_socket "$HOME/.cache/git/credential/socket" ' XDG_CACHE_HOME="$HOME/xdg" @@ -31,7 +46,7 @@ helper_test cache test_expect_success "use custom XDG_CACHE_HOME if set and default sockets are not created" ' test_when_finished "git credential-cache exit" && - test -S "$XDG_CACHE_HOME/git/credential/socket" && + test_path_is_socket "$XDG_CACHE_HOME/git/credential/socket" && test_path_is_missing "$HOME/.git-credential-cache/socket" && test_path_is_missing "$HOME/.cache/git/credential/socket" ' @@ -48,7 +63,7 @@ test_expect_success 'credential-cache --socket option overrides default location username=store-user password=store-pass EOF - test -S "$HOME/dir/socket" + test_path_is_socket "$HOME/dir/socket" ' test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" ' @@ -62,7 +77,7 @@ test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" ' username=store-user password=store-pass EOF - test -S "$HOME/.cache/git/credential/socket" && + test_path_is_socket "$HOME/.cache/git/credential/socket" && XDG_CACHE_HOME="$HOME/xdg" && export XDG_CACHE_HOME && check approve cache <<-\EOF && @@ -71,7 +86,7 @@ test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" ' username=store-user password=store-pass EOF - test -S "$XDG_CACHE_HOME/git/credential/socket" + test_path_is_socket "$XDG_CACHE_HOME/git/credential/socket" ' test_expect_success 'use user socket if user directory exists' ' @@ -79,14 +94,15 @@ test_expect_success 'use user socket if user directory exists' ' git credential-cache exit && rmdir \"\$HOME/.git-credential-cache/\" " && - mkdir -p -m 700 "$HOME/.git-credential-cache/" && + mkdir -p "$HOME/.git-credential-cache/" && + chmod 700 "$HOME/.git-credential-cache/" && check approve cache <<-\EOF && protocol=https host=example.com username=store-user password=store-pass EOF - test -S "$HOME/.git-credential-cache/socket" + test_path_is_socket "$HOME/.git-credential-cache/socket" ' test_expect_success SYMLINKS 'use user socket if user directory is a symlink to a directory' ' @@ -103,7 +119,7 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to username=store-user password=store-pass EOF - test -S "$HOME/.git-credential-cache/socket" + test_path_is_socket "$HOME/.git-credential-cache/socket" ' helper_test_timeout cache --timeout=1 From 245670cd46c4713818b5fbca2c084ce2e19f4ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= Date: Tue, 14 Sep 2021 00:25:59 -0700 Subject: [PATCH 46/71] credential-cache: check for windows specific errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Connect and reset errors aren't what will be expected by POSIX but are instead compatible with the ones used by WinSock. To avoid any possibility of confusion with other systems, checks for disconnection and availability had been abstracted into helper functions that are platform specific. Signed-off-by: Carlo Marcelo Arenas Belón Signed-off-by: Junio C Hamano --- builtin/credential-cache.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c index e8a7415747..78c02ad531 100644 --- a/builtin/credential-cache.c +++ b/builtin/credential-cache.c @@ -11,6 +11,32 @@ #define FLAG_SPAWN 0x1 #define FLAG_RELAY 0x2 +#ifdef GIT_WINDOWS_NATIVE + +static int connection_closed(int error) +{ + return (error == EINVAL); +} + +static int connection_fatally_broken(int error) +{ + return (error != ENOENT) && (error != ENETDOWN); +} + +#else + +static int connection_closed(int error) +{ + return (error == ECONNRESET); +} + +static int connection_fatally_broken(int error) +{ + return (error != ENOENT) && (error != ECONNREFUSED); +} + +#endif + static int send_request(const char *socket, const struct strbuf *out) { int got_data = 0; @@ -28,7 +54,7 @@ static int send_request(const char *socket, const struct strbuf *out) int r; r = read_in_full(fd, in, sizeof(in)); - if (r == 0 || (r < 0 && errno == ECONNRESET)) + if (r == 0 || (r < 0 && connection_closed(errno))) break; if (r < 0) die_errno("read error from cache daemon"); @@ -75,7 +101,7 @@ static void do_cache(const char *socket, const char *action, int timeout, } if (send_request(socket, &buf) < 0) { - if (errno != ENOENT && errno != ECONNREFUSED) + if (connection_fatally_broken(errno)) die_errno("unable to connect to cache daemon"); if (flags & FLAG_SPAWN) { spawn_daemon(socket); From bb390b1f49406d967e8bf3401337cf1d9535f820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= Date: Tue, 14 Sep 2021 00:26:00 -0700 Subject: [PATCH 47/71] git-compat-util: include declaration for unix sockets in windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Available since Windows 10 release 1803 and Windows Server 2019. NO_UNIX_SOCKETS is still the default for Windows builds, as they need to keep backward compatibility with releases up to Windows 7, but allow including the header otherwise. Signed-off-by: Carlo Marcelo Arenas Belón Signed-off-by: Junio C Hamano --- git-compat-util.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-compat-util.h b/git-compat-util.h index b46605300a..6a420d104c 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -160,6 +160,9 @@ # endif #define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */ #include +#ifndef NO_UNIX_SOCKETS +#include +#endif #include #define GIT_WINDOWS_NATIVE #endif From a3952f8e7c0e74d9266bc9cf4ac50dd179129f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20A=C3=9Fhauer?= Date: Tue, 14 Sep 2021 13:27:17 +0000 Subject: [PATCH 48/71] help: make sure local html page exists before calling external processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We check that git.html exists, regardless of the page the user wants to open. Checking whether the requested page exists instead gives us a smoother user experience in two use cases: 1) The requested page doesn't exist When calling a git command and there is an error, most users reasonably expect git to produce an error message on the standard error stream, but in this case we pass the filepath to git web--browse which passes it on to a browser (or a helper program like xdg-open or start that should in turn open a browser) without any error and many GUI based browsers or helpers won't output such a message onto the standard error stream. Especially the helper programs tend to show the corresponding error message in a message box and wait for user input before exiting. This leaves users in interactive console sessions without an error message in their console, without a console prompt and without the help page they expected. 2) git.html is missing for some reason, but the user asked for some other page We currently refuse to show any local html help page when we can't find git.html. Even if the requested help page exists. If we check for the requested page instead, we can show the user all available pages and only error out on those that don't exist. Signed-off-by: Matthias Aßhauer Signed-off-by: Junio C Hamano --- builtin/help.c | 9 ++++++--- t/t0012-help.sh | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/builtin/help.c b/builtin/help.c index b7eec06c3d..7731659765 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -467,11 +467,14 @@ static void get_html_page_path(struct strbuf *page_path, const char *page) if (!html_path) html_path = to_free = system_path(GIT_HTML_PATH); - /* Check that we have a git documentation directory. */ + /* + * Check that the page we're looking for exists. + */ if (!strstr(html_path, "://")) { - if (stat(mkpath("%s/git.html", html_path), &st) + if (stat(mkpath("%s/%s.html", html_path, page), &st) || !S_ISREG(st.st_mode)) - die("'%s': not a documentation directory.", html_path); + die("'%s/%s.html': documentation file not found.", + html_path, page); } strbuf_init(page_path, 0); diff --git a/t/t0012-help.sh b/t/t0012-help.sh index 5679e29c62..913f34c8e9 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -73,6 +73,22 @@ test_expect_success 'git help -g' ' test_i18ngrep "^ tutorial " help.output ' +test_expect_success 'git help fails for non-existing html pages' ' + configure_help && + mkdir html-empty && + test_must_fail git -c help.htmlpath=html-empty help status && + test_must_be_empty test-browser.log +' + +test_expect_success 'git help succeeds without git.html' ' + configure_help && + mkdir html-with-docs && + touch html-with-docs/git-status.html && + git -c help.htmlpath=html-with-docs help status && + echo "html-with-docs/git-status.html" >expect && + test_cmp expect test-browser.log +' + test_expect_success 'generate builtin list' ' git --list-cmds=builtins >builtins ' From b6d8887d3d607e0da971218f20428c0635c6362c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20A=C3=9Fhauer?= Date: Tue, 14 Sep 2021 13:27:18 +0000 Subject: [PATCH 49/71] documentation: add documentation for 'git version' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While 'git version' is probably the least complex git command, it is a non-experimental user-facing builtin command. As such it should have a help page. Both `git help` and `git version` can be called as options (`--help`/`--version`) that internally get converted to the corresponding command. Add a small paragraph to Documentation/git.txt describing how these two options interact with each other and link to this help page for the sub-options that `--version` can take. Well, currently there is only one sub-option, but that could potentially increase in future versions of Git. Signed-off-by: Matthias Aßhauer Signed-off-by: Junio C Hamano --- Documentation/git-version.txt | 28 ++++++++++++++++++++++++++++ Documentation/git.txt | 4 ++++ 2 files changed, 32 insertions(+) create mode 100644 Documentation/git-version.txt diff --git a/Documentation/git-version.txt b/Documentation/git-version.txt new file mode 100644 index 0000000000..80fa7754a6 --- /dev/null +++ b/Documentation/git-version.txt @@ -0,0 +1,28 @@ +git-version(1) +============== + +NAME +---- +git-version - Display version information about Git + +SYNOPSIS +-------- +[verse] +'git version' [--build-options] + +DESCRIPTION +----------- +With no options given, the version of 'git' is printed on the standard output. + +Note that `git --version` is identical to `git version` because the +former is internally converted into the latter. + +OPTIONS +------- +--build-options:: + Include additional information about how git was built for diagnostic + purposes. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git.txt b/Documentation/git.txt index 6dd241ef83..95fe6f31b4 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -41,6 +41,10 @@ OPTIONS ------- --version:: Prints the Git suite version that the 'git' program came from. ++ +This option is internaly converted to `git version ...` and accepts +the same options as the linkgit:git-version[1] command. If `--help` is +also given, it takes precedence over `--version`. --help:: Prints the synopsis and a list of the most commonly used From ce125d431aaa7a12623a81267a221f64552ffd17 Mon Sep 17 00:00:00 2001 From: Jonathan Tan Date: Wed, 15 Sep 2021 11:59:19 -0700 Subject: [PATCH 50/71] submodule: extract path to submodule gitdir func We currently store each submodule gitdir in ".git/modules/", but this has problems with some submodule naming schemes, as described in a comment in submodule_name_to_gitdir() in this patch. Extract the determination of the location of a submodule's gitdir into its own function submodule_name_to_gitdir(). For now, the problem remains unsolved, but this puts us in a better position for finding a solution. This was motivated, at $DAYJOB, by a part of Android's repo hierarchy [1]. In particular, there is a repo "build", and several repos of the form "build/". This is based on earlier work by Brandon Williams [2]. [1] https://android.googlesource.com/platform/ [2] https://lore.kernel.org/git/20180808223323.79989-2-bmwill@google.com/ Signed-off-by: Jonathan Tan Signed-off-by: Junio C Hamano --- builtin/submodule--helper.c | 10 ++++- dir.c | 2 +- repository.c | 3 +- submodule.c | 77 ++++++++++++++++++++++++++----------- submodule.h | 7 ++++ 5 files changed, 72 insertions(+), 27 deletions(-) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 4da9781b99..29ca0bedf6 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -1704,18 +1704,24 @@ static int add_possible_reference_from_superproject( * standard layout with .git/(modules/)+/objects */ if (strip_suffix(odb->path, "/objects", &len)) { + struct repository alternate; char *sm_alternate; struct strbuf sb = STRBUF_INIT; struct strbuf err = STRBUF_INIT; strbuf_add(&sb, odb->path, len); + repo_init(&alternate, sb.buf, NULL); + /* * We need to end the new path with '/' to mark it as a dir, * otherwise a submodule name containing '/' will be broken * as the last part of a missing submodule reference would * be taken as a file name. */ - strbuf_addf(&sb, "/modules/%s/", sas->submodule_name); + strbuf_reset(&sb); + submodule_name_to_gitdir(&sb, &alternate, sas->submodule_name); + strbuf_addch(&sb, '/'); + repo_clear(&alternate); sm_alternate = compute_alternate_path(sb.buf, &err); if (sm_alternate) { @@ -1785,7 +1791,7 @@ static int clone_submodule(struct module_clone_data *clone_data) struct strbuf sb = STRBUF_INIT; struct child_process cp = CHILD_PROCESS_INIT; - strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), clone_data->name); + submodule_name_to_gitdir(&sb, the_repository, clone_data->name); sm_gitdir = absolute_pathdup(sb.buf); strbuf_reset(&sb); diff --git a/dir.c b/dir.c index 03c4d21267..5fe5d3a42e 100644 --- a/dir.c +++ b/dir.c @@ -3633,7 +3633,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree, strbuf_reset(&sub_wt); strbuf_reset(&sub_gd); strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path); - strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name); + submodule_name_to_gitdir(&sub_gd, &subrepo, sub->name); connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1); } diff --git a/repository.c b/repository.c index b2bf44c6fa..710a3b4bf8 100644 --- a/repository.c +++ b/repository.c @@ -213,8 +213,7 @@ int repo_submodule_init(struct repository *subrepo, * submodule would not have a worktree. */ strbuf_reset(&gitdir); - strbuf_repo_git_path(&gitdir, superproject, - "modules/%s", sub->name); + submodule_name_to_gitdir(&gitdir, superproject, sub->name); if (repo_init(subrepo, gitdir.buf, NULL)) { ret = -1; diff --git a/submodule.c b/submodule.c index 8e611fe1db..9382ddf0cc 100644 --- a/submodule.c +++ b/submodule.c @@ -1819,14 +1819,16 @@ out: void submodule_unset_core_worktree(const struct submodule *sub) { - char *config_path = xstrfmt("%s/modules/%s/config", - get_git_dir(), sub->name); + struct strbuf config_path = STRBUF_INIT; - if (git_config_set_in_file_gently(config_path, "core.worktree", NULL)) + submodule_name_to_gitdir(&config_path, the_repository, sub->name); + strbuf_addstr(&config_path, "/config"); + + if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL)) warning(_("Could not unset core.worktree setting in submodule '%s'"), sub->path); - free(config_path); + strbuf_release(&config_path); } static const char *get_super_prefix_or_empty(void) @@ -1922,20 +1924,22 @@ int submodule_move_head(const char *path, absorb_git_dir_into_superproject(path, ABSORB_GITDIR_RECURSE_SUBMODULES); } else { - char *gitdir = xstrfmt("%s/modules/%s", - get_git_dir(), sub->name); - connect_work_tree_and_git_dir(path, gitdir, 0); - free(gitdir); + struct strbuf gitdir = STRBUF_INIT; + submodule_name_to_gitdir(&gitdir, the_repository, + sub->name); + connect_work_tree_and_git_dir(path, gitdir.buf, 0); + strbuf_release(&gitdir); /* make sure the index is clean as well */ submodule_reset_index(path); } if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) { - char *gitdir = xstrfmt("%s/modules/%s", - get_git_dir(), sub->name); - connect_work_tree_and_git_dir(path, gitdir, 1); - free(gitdir); + struct strbuf gitdir = STRBUF_INIT; + submodule_name_to_gitdir(&gitdir, the_repository, + sub->name); + connect_work_tree_and_git_dir(path, gitdir.buf, 1); + strbuf_release(&gitdir); } } @@ -2050,7 +2054,7 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name) static void relocate_single_git_dir_into_superproject(const char *path) { char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL; - char *new_git_dir; + struct strbuf new_gitdir = STRBUF_INIT; const struct submodule *sub; if (submodule_uses_worktrees(path)) @@ -2068,14 +2072,13 @@ static void relocate_single_git_dir_into_superproject(const char *path) if (!sub) die(_("could not lookup name for submodule '%s'"), path); - new_git_dir = git_pathdup("modules/%s", sub->name); - if (validate_submodule_git_dir(new_git_dir, sub->name) < 0) + submodule_name_to_gitdir(&new_gitdir, the_repository, sub->name); + if (validate_submodule_git_dir(new_gitdir.buf, sub->name) < 0) die(_("refusing to move '%s' into an existing git dir"), real_old_git_dir); - if (safe_create_leading_directories_const(new_git_dir) < 0) - die(_("could not create directory '%s'"), new_git_dir); - real_new_git_dir = real_pathdup(new_git_dir, 1); - free(new_git_dir); + if (safe_create_leading_directories_const(new_gitdir.buf) < 0) + die(_("could not create directory '%s'"), new_gitdir.buf); + real_new_git_dir = real_pathdup(new_gitdir.buf, 1); fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"), get_super_prefix_or_empty(), path, @@ -2086,6 +2089,7 @@ static void relocate_single_git_dir_into_superproject(const char *path) free(old_git_dir); free(real_old_git_dir); free(real_new_git_dir); + strbuf_release(&new_gitdir); } /* @@ -2105,6 +2109,7 @@ void absorb_git_dir_into_superproject(const char *path, /* Not populated? */ if (!sub_git_dir) { const struct submodule *sub; + struct strbuf sub_gitdir = STRBUF_INIT; if (err_code == READ_GITFILE_ERR_STAT_FAILED) { /* unpopulated as expected */ @@ -2126,8 +2131,9 @@ void absorb_git_dir_into_superproject(const char *path, sub = submodule_from_path(the_repository, null_oid(), path); if (!sub) die(_("could not lookup name for submodule '%s'"), path); - connect_work_tree_and_git_dir(path, - git_path("modules/%s", sub->name), 0); + submodule_name_to_gitdir(&sub_gitdir, the_repository, sub->name); + connect_work_tree_and_git_dir(path, sub_gitdir.buf, 0); + strbuf_release(&sub_gitdir); } else { /* Is it already absorbed into the superprojects git dir? */ char *real_sub_git_dir = real_pathdup(sub_git_dir, 1); @@ -2278,9 +2284,36 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule) goto cleanup; } strbuf_reset(buf); - strbuf_git_path(buf, "%s/%s", "modules", sub->name); + submodule_name_to_gitdir(buf, the_repository, sub->name); } cleanup: return ret; } + +void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r, + const char *submodule_name) +{ + /* + * NEEDSWORK: The current way of mapping a submodule's name to + * its location in .git/modules/ has problems with some naming + * schemes. For example, if a submodule is named "foo" and + * another is named "foo/bar" (whether present in the same + * superproject commit or not - the problem will arise if both + * superproject commits have been checked out at any point in + * time), or if two submodule names only have different cases in + * a case-insensitive filesystem. + * + * There are several solutions, including encoding the path in + * some way, introducing a submodule..gitdir config in + * .git/config (not .gitmodules) that allows overriding what the + * gitdir of a submodule would be (and teach Git, upon noticing + * a clash, to automatically determine a non-clashing name and + * to write such a config), or introducing a + * submodule..gitdir config in .gitmodules that repo + * administrators can explicitly set. Nothing has been decided, + * so for now, just append the name at the end of the path. + */ + strbuf_repo_git_path(buf, r, "modules/"); + strbuf_addstr(buf, submodule_name); +} diff --git a/submodule.h b/submodule.h index 84640c49c1..c11e0807fc 100644 --- a/submodule.h +++ b/submodule.h @@ -124,6 +124,13 @@ int push_unpushed_submodules(struct repository *r, */ int submodule_to_gitdir(struct strbuf *buf, const char *submodule); +/* + * Given a submodule name, create a path to where the submodule's gitdir lives + * inside of the provided repository's 'modules' directory. + */ +void submodule_name_to_gitdir(struct strbuf *buf, struct repository *r, + const char *submodule_name); + /* * Make sure that no submodule's git dir is nested in a sibling submodule's. */ From 7c1200745bec3034c98d6e44bd5f620849dbe2ba Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 15 Sep 2021 13:24:52 -0400 Subject: [PATCH 51/71] t1400: avoid SIGPIPE race condition on fifo t1400.190 sometimes fails or even hangs because of the way it uses fifos. Our goal is to interactively read and write lines from update-ref, so we have two fifos, in and out. We open a descriptor connected to "in" and redirect output to that, so that update-ref does not see EOF as it would if we opened and closed it for each "echo" call. But we don't do the same for the output. This leads to a race where our "read response Signed-off-by: Junio C Hamano --- t/t1400-update-ref.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 1e754e258f..0d4f73acaa 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1603,11 +1603,13 @@ test_expect_success PIPE 'transaction flushes status updates' ' (git update-ref --stdin out &) && exec 9>in && + exec 8&-" && + test_when_finished "exec 8<&-" && echo "start" >&9 && echo "start: ok" >expected && - read line actual && test_cmp expected actual && @@ -1615,7 +1617,7 @@ test_expect_success PIPE 'transaction flushes status updates' ' echo prepare >&9 && echo "prepare: ok" >expected && - read line actual && test_cmp expected actual && @@ -1625,7 +1627,7 @@ test_expect_success PIPE 'transaction flushes status updates' ' echo commit >&9 && echo "commit: ok" >expected && - read line actual && test_cmp expected actual ' From afb32e81019c47e933368f126be3d6af657a2060 Mon Sep 17 00:00:00 2001 From: Kyle Zhao Date: Wed, 15 Sep 2021 09:09:23 +0000 Subject: [PATCH 52/71] pack-revindex.h: correct the time complexity descriptions Time complexities for pack_pos_to_midx and midx_to_pack_pos are swapped, correct it. Signed-off-by: Kyle Zhao Reviewed-by: Taylor Blau Signed-off-by: Junio C Hamano --- pack-revindex.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pack-revindex.h b/pack-revindex.h index 479b8f2f9c..74f4eae668 100644 --- a/pack-revindex.h +++ b/pack-revindex.h @@ -109,7 +109,7 @@ off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos); * If the reverse index has not yet been loaded, or the position is out of * bounds, this function aborts. * - * This function runs in time O(log N) with the number of objects in the MIDX. + * This function runs in constant time. */ uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos); @@ -120,7 +120,7 @@ uint32_t pack_pos_to_midx(struct multi_pack_index *m, uint32_t pos); * If the reverse index has not yet been loaded, or the position is out of * bounds, this function aborts. * - * This function runs in constant time. + * This function runs in time O(log N) with the number of objects in the MIDX. */ int midx_to_pack_pos(struct multi_pack_index *midx, uint32_t at, uint32_t *pos); From 637799bf0ab72a509e1f2b29ee6ab3367eefbff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= Date: Thu, 16 Sep 2021 01:55:22 -0700 Subject: [PATCH 53/71] tree-diff: fix leak when not HAVE_ALLOCA_H MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit b8ba412bf7 (tree-diff: avoid alloca for large allocations, 2016-06-07) adds a way to route some bigger allocations out of the stack and free them through the addition of two conveniently named macros, but leaves the calls to free the xalloca part, which could be also in the heap, if the system doesn't HAVE_ALLOCA_H (ex: macOS and other BSD). Add the missing free call, xalloca_free(), which is a noop if we allocated memory in the stack frame, but a real free() if we allocated in the heap instead, and while at it, change the expression to match in both macros for ease of readability. This avoids a leak reported by LSAN while running t0000 but that wouldn't fail the test (which is fixed in the next patch): SUMMARY: LeakSanitizer: 1034 byte(s) leaked in 15 allocation(s). Signed-off-by: Carlo Marcelo Arenas Belón Signed-off-by: Junio C Hamano --- tree-diff.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tree-diff.c b/tree-diff.c index 1572615bd9..437c98a70e 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -21,7 +21,9 @@ ALLOC_ARRAY((x), nr); \ } while(0) #define FAST_ARRAY_FREE(x, nr) do { \ - if ((nr) > 2) \ + if ((nr) <= 2) \ + xalloca_free((x)); \ + else \ free((x)); \ } while(0) From 66c0c44df617446763845a71a8fe0fab4cb848a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= Date: Thu, 16 Sep 2021 01:55:23 -0700 Subject: [PATCH 54/71] t0000: avoid masking git exit value through pipes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 9af0b8dbe2 (t0000-basic: more commit-tree tests., 2006-04-26) adds tests for commit-tree that mask the return exit from git as described in a378fee5b07 (Documentation: add shell guidelines, 2018-10-05). Fix the tests, to avoid pipes by using a temporary file instead. Signed-off-by: Carlo Marcelo Arenas Belón Signed-off-by: Junio C Hamano --- t/t0000-basic.sh | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index cb87768513..5c342de713 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -1271,28 +1271,29 @@ P=$(test_oid root) test_expect_success 'git commit-tree records the correct tree in a commit' ' commit0=$(echo NO | git commit-tree $P) && - tree=$(git show --pretty=raw $commit0 | - sed -n -e "s/^tree //p" -e "/^author /q") && + git show --pretty=raw $commit0 >out && + tree=$(sed -n -e "s/^tree //p" -e "/^author /q" out) && test "z$tree" = "z$P" ' test_expect_success 'git commit-tree records the correct parent in a commit' ' commit1=$(echo NO | git commit-tree $P -p $commit0) && - parent=$(git show --pretty=raw $commit1 | - sed -n -e "s/^parent //p" -e "/^author /q") && + git show --pretty=raw $commit1 >out && + parent=$(sed -n -e "s/^parent //p" -e "/^author /q" out) && test "z$commit0" = "z$parent" ' test_expect_success 'git commit-tree omits duplicated parent in a commit' ' commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) && - parent=$(git show --pretty=raw $commit2 | - sed -n -e "s/^parent //p" -e "/^author /q" | - sort -u) && + git show --pretty=raw $commit2 >out && + cat >match.sed <<-\EOF && + s/^parent //p + /^author /q + EOF + parent=$(sed -n -f match.sed out | sort -u) && test "z$commit0" = "z$parent" && - numparent=$(git show --pretty=raw $commit2 | - sed -n -e "s/^parent //p" -e "/^author /q" | - wc -l) && - test $numparent = 1 + git show --pretty=raw $commit2 >out && + test_stdout_line_count = 1 sed -n -f match.sed out ' test_expect_success 'update-index D/F conflict' ' From ddb1055343948e0d0bc81f8d20245f1ada6430a0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 23 Sep 2021 13:45:03 -0700 Subject: [PATCH 55/71] The eighth batch Signed-off-by: Junio C Hamano --- Documentation/RelNotes/2.34.0.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/RelNotes/2.34.0.txt b/Documentation/RelNotes/2.34.0.txt index 7ce5ab7cc2..fa8ecedb9e 100644 --- a/Documentation/RelNotes/2.34.0.txt +++ b/Documentation/RelNotes/2.34.0.txt @@ -48,6 +48,14 @@ UI, Workflows & Features entire directory outside the sparse cone to be removed, which is especially useful when the sparse patterns change. + * Taking advantage of the CGI interface, http-backend has been + updated to enable protocol v2 automatically when the other side + asks for it. + + * The credential-cache helper has been adjusted to Windows. + + * The error in "git help no-such-git-command" is handled better. + Performance, Internal Implementation, Development Support etc. @@ -119,6 +127,9 @@ Performance, Internal Implementation, Development Support etc. ask the file descriptors open for packfiles to be closed immediately before spawning commands that may trigger auto-gc. + * An oddball OPTION_ARGUMENT feature has been removed from the + parse-options API. + Fixes since v2.33 ----------------- @@ -245,6 +256,17 @@ Fixes since v2.33 subsystem has been cleaned up. (merge 35cf94eaf6 rs/no-mode-to-open-when-appending later to maint). + * "git update-ref --stdin" failed to flush its output as needed, + which potentially led the conversation to a deadlock. + (merge 7c1200745b ps/update-ref-batch-flush later to maint). + + * When "git am --abort" fails to abort correctly, it still exited + with exit status of 0, which has been corrected. + (merge c5ead19ea2 en/am-abort-fix later to maint). + + * Correct nr and alloc members of strvec struct to be of type size_t. + (merge 8d133a4653 jk/strvec-typefix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 1d9c8daef8 ab/bundle-doc later to maint). (merge 81483fe613 en/merge-strategy-docs later to maint). @@ -276,3 +298,8 @@ Fixes since v2.33 (merge 92a5d1c9b4 jc/prefix-filename-allocates later to maint). (merge d9a65b6c0a rs/setup-use-xopen-and-xdup later to maint). (merge e8f55568de jk/t5562-racefix later to maint). + (merge 8f0f110156 rs/drop-core-compression-vars later to maint). + (merge b6d8887d3d ma/doc-git-version later to maint). + (merge 66c0c44df6 cb/plug-leaks-in-alloca-emu-users later to maint). + (merge afb32e8101 kz/revindex-comment-fix later to maint). + (merge ae578de926 po/git-config-doc-mentions-help-c later to maint). From b4724242fa3342d939e7a0c4102d3695091db1f6 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:32:17 -0400 Subject: [PATCH 56/71] t7900: clean up some more broken refs The "incremental-repack task" test replaces the object directory with a known state. As a result, some of our refs point to objects that are not included in that state. Commit 3cf5f221be (t7900: clean up some broken refs, 2021-01-19) cleaned up some of those (that were causing warnings to stderr from the maintenance process). But there are a few more that were missed. These aren't hurting anything for now, but it's certainly an unexpected state to leave the test repository in, and it will become a problem if repack ever gets more picky about broken refs. Let's clean up those additional refs (which are all in refs/remotes, with nothing there that isn't broken), and add an extra "for-each-ref" call to assert that we've got everything. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t7900-maintenance.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 36a4218745..31245f6276 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -277,7 +277,7 @@ test_expect_success 'incremental-repack task' ' # Delete refs that have not been repacked in these packs. git for-each-ref --format="delete %(refname)" \ - refs/prefetch refs/tags >refs && + refs/prefetch refs/tags refs/remotes >refs && git update-ref --stdin packs-before && test_line_count = 3 packs-before && + # make sure we do not have any broken refs that were + # missed in the deletion above + git for-each-ref && + # the job repacks the two into a new pack, but does not # delete the old ones. git maintenance run --task=incremental-repack && From e9de7a52a5cb1d6647a33a3ce0eedb947e04f3fc Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:33:00 -0400 Subject: [PATCH 57/71] t5516: don't use HEAD ref for invalid ref-deletion tests A few tests in t5516 want to assert that we can delete a corrupted ref whose pointed-to object is missing. They do so by using the "main" branch, which is also pointed to by HEAD. This does work, but only because of a subtle assumption about the implementation. We do not block the deletion because of the invalid ref, but we _also_ do not notice that the deleted branch is pointed to by HEAD. And so the safety rule of "do not allow HEAD to be deleted in a non-bare repository" does not kick in, and the test passes. Let's instead use a non-HEAD branch. That still tests what we care about here (deleting a corrupt ref), but without implicitly depending on our failure to notice that we're deleting HEAD. That will future proof the test against that behavior changing. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t5516-fetch-push.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4db8edd9c8..b13553ecf4 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -662,10 +662,10 @@ test_expect_success 'push does not update local refs on failure' ' test_expect_success 'allow deleting an invalid remote ref' ' - mk_test testrepo heads/main && + mk_test testrepo heads/branch && rm -f testrepo/.git/objects/??/* && - git push testrepo :refs/heads/main && - (cd testrepo && test_must_fail git rev-parse --verify refs/heads/main) + git push testrepo :refs/heads/branch && + (cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch) ' @@ -706,25 +706,25 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho ' test_expect_success 'deleting dangling ref triggers hooks with correct args' ' - mk_test_with_hooks testrepo heads/main && + mk_test_with_hooks testrepo heads/branch && rm -f testrepo/.git/objects/??/* && - git push testrepo :refs/heads/main && + git push testrepo :refs/heads/branch && ( cd testrepo/.git && cat >pre-receive.expect <<-EOF && - $ZERO_OID $ZERO_OID refs/heads/main + $ZERO_OID $ZERO_OID refs/heads/branch EOF cat >update.expect <<-EOF && - refs/heads/main $ZERO_OID $ZERO_OID + refs/heads/branch $ZERO_OID $ZERO_OID EOF cat >post-receive.expect <<-EOF && - $ZERO_OID $ZERO_OID refs/heads/main + $ZERO_OID $ZERO_OID refs/heads/branch EOF cat >post-update.expect <<-EOF && - refs/heads/main + refs/heads/branch EOF test_cmp pre-receive.expect pre-receive.actual && From da5e0c6a00fd01112b10f3e47cf8044a37ec329d Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:34:04 -0400 Subject: [PATCH 58/71] t5600: provide detached HEAD for corruption failures When checking how git-clone behaves when it fails, we stimulate some failures by trying to do a clone from a local repository whose objects have been removed. Because these clones use local optimizations, there's a subtle dependency in how the corruption is handled on the sending side. If upload-pack does not show us the broken refs (which it does not currently), then we see only HEAD (which is itself broken), and clone that as a detached HEAD. When we try to write the ref, we notice that we never got the object and bail. But if upload-pack _does_ show us the broken refs (which it may in a future patch), then we'll realize that HEAD is a symref and just write that. You'd think we'd fail when writing out the refs themselves, but we don't; we do a bulk write and skip the connectivity check because of our --local optimizations. For the non-bare case, we do notice the problem when we try to checkout. But for a bare repository, we unexpectedly complete the clone successfully! At first glance this may seem like a bug. But the whole point of those local optimizations is to give up some safety for speed. If you want to be careful, you should be using "--no-local", which would notice that the pack did not transfer sufficient objects. We could do that in these tests, but part of the point is for them to fail at specific moments (and indeed, we have a later test that checks for transport failure). However, we can make this less subtle and future-proof it against changes on the upload-pack side by just having an explicit detached HEAD in the corrupted repo. Now we'll fail as expected during the ref write if any ref _or_ HEAD is corrupt, whether we're --bare or not. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t5600-clone-fail-cleanup.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh index 5bf10261d3..34b3df4027 100755 --- a/t/t5600-clone-fail-cleanup.sh +++ b/t/t5600-clone-fail-cleanup.sh @@ -35,7 +35,9 @@ test_expect_success 'create a repo to clone' ' ' test_expect_success 'create objects in repo for later corruption' ' - test_commit -C foo file + test_commit -C foo file && + git -C foo checkout --detach && + test_commit -C foo detached ' # source repository given to git clone should be relative to the From 2ac0cbc9b0bf9236b34eff4aa0160f5cfc3606c4 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:35:29 -0400 Subject: [PATCH 59/71] t5312: drop "verbose" helper t5312 has several uses of the "verbose" helper, as described in 8ad1652418 (t5304: use helper to report failure of "test foo = bar", 2014-10-10). Back then the "-x" trace option for tests was new, and was not as pleasant to use (e.g., some tests failed under "-x", we did not support BASH_XTRACEFD, etc). These days it is clear that "-x" is the preferred way to get extra output, and we don't need to mark up individual tests. Let's get rid of the uses of "verbose" here, as one step toward eradicating it totally. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t5312-prune-corruption.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index 11423b3cb2..165cc80fa4 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -31,21 +31,21 @@ test_expect_success 'create history reachable only from a bogus-named ref' ' test_expect_success 'pruning does not drop bogus object' ' test_when_finished "git hash-object -w -t commit saved" && test_might_fail git prune --expire=now && - verbose git cat-file -e $bogus + git cat-file -e $bogus ' test_expect_success 'put bogus object into pack' ' git tag reachable $bogus && git repack -ad && git tag -d reachable && - verbose git cat-file -e $bogus + git cat-file -e $bogus ' test_expect_success 'destructive repack keeps packed object' ' test_might_fail git repack -Ad --unpack-unreachable=now && - verbose git cat-file -e $bogus && + git cat-file -e $bogus && test_might_fail git repack -ad && - verbose git cat-file -e $bogus + git cat-file -e $bogus ' # subsequent tests will have different corruptions @@ -78,7 +78,7 @@ test_expect_success 'create history with missing tip commit' ' test_expect_success 'pruning with a corrupted tip does not drop history' ' test_when_finished "git hash-object -w -t commit saved" && test_might_fail git prune --expire=now && - verbose git cat-file -e $recoverable + git cat-file -e $recoverable ' test_expect_success 'pack-refs does not silently delete broken loose ref' ' From f805844676ac4d6f5def5c2ace1d0430c410e21e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:36:18 -0400 Subject: [PATCH 60/71] t5312: create bogus ref as necessary Some tests in t5312 create an illegally-named ref, and then see how various operations handle it. But between those operations, we also do some more setup (e.g., repacking), and we are subtly depending on how those setup steps react to the illegal ref. To future-proof us against those behaviors changing, let's instead create and clean up our bogus ref on demand in the tests that need it. This has two small extra advantages: - the tests are more stand-alone; we do not need an extra test to clean up the ref before moving on to other parts of the script - the creation and cleanup is together in one helper function. Because these depend on touching the refs in the filesystem directly, they may need to be tweaked for a world with alternate backends (they have not been noticed so far in the reftable work because with a non-file backend the tests don't fail; they simply become uninteresting noops because the broken ref isn't read at all). Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t5312-prune-corruption.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index 165cc80fa4..0704f3e930 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -18,18 +18,23 @@ test_expect_success 'disable reflogs' ' git reflog expire --expire=all --all ' +create_bogus_ref () { + test_when_finished 'rm -f .git/refs/heads/bogus..name' && + echo $bogus >.git/refs/heads/bogus..name +} + test_expect_success 'create history reachable only from a bogus-named ref' ' test_tick && git commit --allow-empty -m main && base=$(git rev-parse HEAD) && test_tick && git commit --allow-empty -m bogus && bogus=$(git rev-parse HEAD) && git cat-file commit $bogus >saved && - echo $bogus >.git/refs/heads/bogus..name && git reset --hard HEAD^ ' test_expect_success 'pruning does not drop bogus object' ' test_when_finished "git hash-object -w -t commit saved" && + create_bogus_ref && test_might_fail git prune --expire=now && git cat-file -e $bogus ' @@ -42,17 +47,13 @@ test_expect_success 'put bogus object into pack' ' ' test_expect_success 'destructive repack keeps packed object' ' + create_bogus_ref && test_might_fail git repack -Ad --unpack-unreachable=now && git cat-file -e $bogus && test_might_fail git repack -ad && git cat-file -e $bogus ' -# subsequent tests will have different corruptions -test_expect_success 'clean up bogus ref' ' - rm .git/refs/heads/bogus..name -' - # We create two new objects here, "one" and "two". Our # main branch points to "two", which is deleted, # corrupting the repository. But we'd like to make sure From 078eecbcbe04bf77e8b9afee01a58c905c3b3c50 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:36:42 -0400 Subject: [PATCH 61/71] t5312: test non-destructive repack In t5312, we create a state with a broken ref, and then make sure that destructive repacks don't silently ignore the breakage (where a destructive repack is one that might drop objects). But we don't check the behavior of non-destructive repacks at all (i.e., ones where we'd keep unreachable objects). So let's add a test to confirm the current behavior, which is that they are allowed (i.e., ignoring the breakage and considering any objects it points to as unreachable). This may change in the future, but we'd like for the test suite to alert us to that fact. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t5312-prune-corruption.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index 0704f3e930..c7010fb754 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -46,6 +46,11 @@ test_expect_success 'put bogus object into pack' ' git cat-file -e $bogus ' +test_expect_success 'non-destructive repack ignores bogus name' ' + create_bogus_ref && + git repack -adk +' + test_expect_success 'destructive repack keeps packed object' ' create_bogus_ref && test_might_fail git repack -Ad --unpack-unreachable=now && From 5b062e1f79b5121296678d91b2bbd032ca86a866 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:37:11 -0400 Subject: [PATCH 62/71] t5312: be more assertive about command failure When repacking or pruning in a corrupted repository, our tests in t5312 argue that it is OK to complete the operation or bail, as long as we don't actually delete the objects pointed to by the corruption. This isn't a wrong line of reasoning, but the tests are a bit permissive by using test_might_fail. The fact is that we _do_ bail currently, and if we ever stopped doing so, that would be worthy of a human investigating. So let's switch these to test_must_fail. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- t/t5312-prune-corruption.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index c7010fb754..1522a4ba8e 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -7,6 +7,9 @@ if we see, for example, a ref with a bogus name, it is OK either to bail out or to proceed using it as a reachable tip, but it is _not_ OK to proceed as if it did not exist. Otherwise we might silently delete objects that cannot be recovered. + +Note that we do assert command failure in these cases, because that is +what currently happens. If that changes, these tests should be revisited. ' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME @@ -35,7 +38,7 @@ test_expect_success 'create history reachable only from a bogus-named ref' ' test_expect_success 'pruning does not drop bogus object' ' test_when_finished "git hash-object -w -t commit saved" && create_bogus_ref && - test_might_fail git prune --expire=now && + test_must_fail git prune --expire=now && git cat-file -e $bogus ' @@ -53,9 +56,9 @@ test_expect_success 'non-destructive repack ignores bogus name' ' test_expect_success 'destructive repack keeps packed object' ' create_bogus_ref && - test_might_fail git repack -Ad --unpack-unreachable=now && + test_must_fail git repack -Ad --unpack-unreachable=now && git cat-file -e $bogus && - test_might_fail git repack -ad && + test_must_fail git repack -ad && git cat-file -e $bogus ' @@ -83,7 +86,7 @@ test_expect_success 'create history with missing tip commit' ' test_expect_success 'pruning with a corrupted tip does not drop history' ' test_when_finished "git hash-object -w -t commit saved" && - test_might_fail git prune --expire=now && + test_must_fail git prune --expire=now && git cat-file -e $recoverable ' From bf708add2eaf37d53fa8a908b5d8772806c54d1b Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:37:58 -0400 Subject: [PATCH 63/71] refs-internal.h: move DO_FOR_EACH_* flags next to each other There are currently two DO_FOR_EACH_* flags, which must not have their bits overlap. Yet they're defined hundreds of lines apart. Let's move them next to each other to make it clear that they are related and are a complete set (which matters if you are adding a new flag and would like to know what the next available bit is). Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- refs/refs-internal.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 3155708345..7b30910974 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -248,6 +248,14 @@ int refs_rename_ref_available(struct ref_store *refs, /* Include broken references in a do_for_each_ref*() iteration: */ #define DO_FOR_EACH_INCLUDE_BROKEN 0x01 +/* + * Only include per-worktree refs in a do_for_each_ref*() iteration. + * Normally this will be used with a files ref_store, since that's + * where all reference backends will presumably store their + * per-worktree refs. + */ +#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02 + /* * Reference iterators * @@ -498,14 +506,6 @@ int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter, each_repo_ref_fn fn, void *cb_data); -/* - * Only include per-worktree refs in a do_for_each_ref*() iteration. - * Normally this will be used with a files ref_store, since that's - * where all reference backends will presumably store their - * per-worktree refs. - */ -#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02 - struct ref_store; /* refs backends */ From 9aab952e855be1a567d4d0585afbdbde624e274f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:39:44 -0400 Subject: [PATCH 64/71] refs-internal.h: reorganize DO_FOR_EACH_* flag documentation The documentation for the DO_FOR_EACH_* flags is sprinkled over the refs-internal.h file. We define the two flags in one spot, and then describe them in more detail far away from there, in the definitions of refs_ref_iterator_begin() and ref_iterator_advance_fn(). Let's try to organize this a bit better: - convert the #defines to an enum. This makes it clear that they are related, and that the enum shows the complete set of flags. - combine all descriptions for each flag in a single spot, next to the flag's definition - use the enum rather than a bare int for functions which take the flags. This helps readers realize which flags can be used. - clarify the mention of flags for ref_iterator_advance_fn(). It does not take flags itself, but is meant to depend on ones set up earlier. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- refs.c | 10 ++++++---- refs/refs-internal.h | 46 ++++++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/refs.c b/refs.c index 8b9f7c3a80..c28bd6a818 100644 --- a/refs.c +++ b/refs.c @@ -1413,7 +1413,8 @@ int head_ref(each_ref_fn fn, void *cb_data) struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, int flags) + const char *prefix, int trim, + enum do_for_each_ref_flags flags) { struct ref_iterator *iter; @@ -1479,7 +1480,8 @@ static int do_for_each_ref_helper(struct repository *r, } static int do_for_each_ref(struct ref_store *refs, const char *prefix, - each_ref_fn fn, int trim, int flags, void *cb_data) + each_ref_fn fn, int trim, + enum do_for_each_ref_flags flags, void *cb_data) { struct ref_iterator *iter; struct do_for_each_ref_help hp = { fn, cb_data }; @@ -1516,7 +1518,7 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken) { - unsigned int flag = 0; + enum do_for_each_ref_flags flag = 0; if (broken) flag = DO_FOR_EACH_INCLUDE_BROKEN; @@ -1528,7 +1530,7 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken) { - unsigned int flag = 0; + enum do_for_each_ref_flags flag = 0; if (broken) flag = DO_FOR_EACH_INCLUDE_BROKEN; diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 7b30910974..2c4e1739f2 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -245,16 +245,30 @@ int refs_rename_ref_available(struct ref_store *refs, /* We allow "recursive" symbolic refs. Only within reason, though */ #define SYMREF_MAXDEPTH 5 -/* Include broken references in a do_for_each_ref*() iteration: */ -#define DO_FOR_EACH_INCLUDE_BROKEN 0x01 - /* - * Only include per-worktree refs in a do_for_each_ref*() iteration. - * Normally this will be used with a files ref_store, since that's - * where all reference backends will presumably store their - * per-worktree refs. + * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(), + * which feeds it). */ -#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02 +enum do_for_each_ref_flags { + /* + * Include broken references in a do_for_each_ref*() iteration, which + * would normally be omitted. This includes both refs that point to + * missing objects (a true repository corruption), ones with illegal + * names (which we prefer not to expose to callers), as well as + * dangling symbolic refs (i.e., those that point to a non-existent + * ref; this is not a corruption, but as they have no valid oid, we + * omit them from normal iteration results). + */ + DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0), + + /* + * Only include per-worktree refs in a do_for_each_ref*() iteration. + * Normally this will be used with a files ref_store, since that's + * where all reference backends will presumably store their + * per-worktree refs. + */ + DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1), +}; /* * Reference iterators @@ -357,16 +371,12 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator); * Return an iterator that goes over each reference in `refs` for * which the refname begins with prefix. If trim is non-zero, then * trim that many characters off the beginning of each refname. - * The output is ordered by refname. The following flags are supported: - * - * DO_FOR_EACH_INCLUDE_BROKEN: include broken references in - * the iteration. - * - * DO_FOR_EACH_PER_WORKTREE_ONLY: only produce REF_TYPE_PER_WORKTREE refs. + * The output is ordered by refname. */ struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, int flags); + const char *prefix, int trim, + enum do_for_each_ref_flags flags); /* * A callback function used to instruct merge_ref_iterator how to @@ -454,10 +464,8 @@ void base_ref_iterator_free(struct ref_iterator *iter); /* * backend-specific implementation of ref_iterator_advance. For symrefs, the * function should set REF_ISSYMREF, and it should also dereference the symref - * to provide the OID referent. If DO_FOR_EACH_INCLUDE_BROKEN is set, symrefs - * with non-existent referents and refs pointing to non-existent object names - * should also be returned. If DO_FOR_EACH_PER_WORKTREE_ONLY, only - * REF_TYPE_PER_WORKTREE refs should be returned. + * to provide the OID referent. It should respect do_for_each_ref_flags + * that were passed to refs_ref_iterator_begin(). */ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); From 8dccb2244c81258cf9f3480c4102d14e30978194 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:41:32 -0400 Subject: [PATCH 65/71] refs: add DO_FOR_EACH_OMIT_DANGLING_SYMREFS flag When the DO_FOR_EACH_INCLUDE_BROKEN flag is used, we include both actual corrupt refs (illegal names, missing objects), but also symrefs that point to nothing. This latter is not really a corruption, but just something that may happen normally. For example, the symref at refs/remotes/origin/HEAD may point to a tracking branch which is later deleted. (The local HEAD may also be unborn, of course, but we do not access it through ref iteration). Most callers of for_each_ref() etc, do not care. They don't pass INCLUDE_BROKEN, so don't see it at all. But for those which do pass it, this somewhat-normal state causes extra warnings (e.g., from for-each-ref) or even aborts operations (destructive repacks with GIT_REF_PARANOIA set). This patch just introduces the flag and the mechanism; there are no callers yet (and hence no tests). Two things to note on the implementation: - we actually skip any symref that does not resolve to a ref. This includes ones which point to an invalidly-named ref. You could argue this is a more serious breakage than simple dangling. But the overall effect is the same (we could not follow the symref), as well as the impact on things like REF_PARANOIA (either way, a symref we can't follow won't impact reachability, because we'll see the ref itself during iteration). The underlying resolution function doesn't distinguish these two cases (they both get REF_ISBROKEN). - we change the iterator in refs/files-backend.c where we check INCLUDE_BROKEN. There's a matching spot in refs/packed-backend.c, but we don't know need to do anything there. The packed backend does not support symrefs at all. The resulting set of flags might be a bit easier to follow if we broke this down into "INCLUDE_CORRUPT_REFS" and "INCLUDE_DANGLING_SYMREFS". But there are a few reasons not do so: - adding a new OMIT_DANGLING_SYMREFS flag lets us leave existing callers intact, without changing their behavior (and some of them really do want to see the dangling symrefs; e.g., t5505 has a test which expects us to report when a symref becomes dangling) - they're not actually independent. You cannot say "include dangling symrefs" without also including refs whose objects are not reachable, because dangling symrefs by definition do not have an object. We could tweak the implementation to distinguish this, but in practice nobody wants to ask for that. Adding the OMIT flag keeps the implementation simple and makes sure we don't regress the current behavior. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- refs/files-backend.c | 5 +++++ refs/refs-internal.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/refs/files-backend.c b/refs/files-backend.c index 74c0385873..1148c0cf09 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -744,6 +744,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE) continue; + if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) && + (iter->iter0->flags & REF_ISSYMREF) && + (iter->iter0->flags & REF_ISBROKEN)) + continue; + if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && !ref_resolves_to_object(iter->iter0->refname, iter->iter0->oid, diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 2c4e1739f2..96911fb26e 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -268,6 +268,12 @@ enum do_for_each_ref_flags { * per-worktree refs. */ DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1), + + /* + * Omit dangling symrefs from output; this only has an effect with + * INCLUDE_BROKEN, since they are otherwise not included at all. + */ + DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), }; /* From 6d751be4b66180679e8bc03fc22b14b4245067a8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:42:38 -0400 Subject: [PATCH 66/71] refs: omit dangling symrefs when using GIT_REF_PARANOIA Dangling symrefs aren't actually a corruption problem. It's perfectly fine for refs/remotes/origin/HEAD to point to an unborn branch. And in particular, if you are trying to establish reachability, a symref that points nowhere doesn't matter either way. Any ref it could point to will be examined during the rest of the traversal. It's possible that a symref pointing nowhere _could_ be a sign that the ref it was meant to point to was deleted accidentally (e.g., via corruption). But there is no particular reason to think that is true for any given case, and in the meantime, GIT_REF_PARANOIA kicking in automatically for some operations means they'll fail unnecessarily. So let's loosen it just a bit. The new test in t5312 shows off an example that is safe, but currently fails (and no longer does after this patch). Note that we don't do anything if the caller explicitly asked for DO_FOR_EACH_INCLUDE_BROKEN. In that case they may be looking for dangling symrefs themselves, and setting GIT_REF_PARANOIA should not _loosen_ things from what the caller asked for. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- refs.c | 12 ++++++++---- t/t5312-prune-corruption.sh | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/refs.c b/refs.c index c28bd6a818..e72510813e 100644 --- a/refs.c +++ b/refs.c @@ -1418,10 +1418,14 @@ struct ref_iterator *refs_ref_iterator_begin( { struct ref_iterator *iter; - if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); - if (ref_paranoia) - flags |= DO_FOR_EACH_INCLUDE_BROKEN; + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) { + if (ref_paranoia < 0) + ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); + if (ref_paranoia) { + flags |= DO_FOR_EACH_INCLUDE_BROKEN; + flags |= DO_FOR_EACH_OMIT_DANGLING_SYMREFS; + } + } iter = refs->be->iterator_begin(refs, prefix, flags); diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index 1522a4ba8e..d8ec5a7462 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -62,6 +62,13 @@ test_expect_success 'destructive repack keeps packed object' ' git cat-file -e $bogus ' +test_expect_success 'destructive repack not confused by dangling symref' ' + test_when_finished "git symbolic-ref -d refs/heads/dangling" && + git symbolic-ref refs/heads/dangling refs/heads/does-not-exist && + git repack -ad && + test_must_fail git cat-file -e $bogus +' + # We create two new objects here, "one" and "two". Our # main branch points to "two", which is deleted, # corrupting the repository. But we'd like to make sure From 968f12fdac2601086dea7e10db17f1c50d704a07 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:46:13 -0400 Subject: [PATCH 67/71] refs: turn on GIT_REF_PARANOIA by default The original point of the GIT_REF_PARANOIA flag was to include broken refs in iterations, so that possibly-destructive operations would not silently ignore them (and would generally instead try to operate on the oids and fail when the objects could not be accessed). We already turned this on by default for some dangerous operations, like "repack -ad" (where missing a reachability tip would mean dropping the associated history). But it was not on for general use, even though it could easily result in the spreading of corruption (e.g., imagine cloning a repository which simply omits some of its refs because their objects are missing; the result quietly succeeds even though you did not clone everything!). This patch turns on GIT_REF_PARANOIA by default. So a clone as mentioned above would actually fail (upload-pack tells us about the broken ref, and when we ask for the objects, pack-objects fails to deliver them). This may be inconvenient when working with a corrupted repository, but: - we are better off to err on the side of complaining about corruption, and then provide mechanisms for explicitly loosening safety. - this is only one type of corruption anyway. If we are missing any other objects in the history that _aren't_ ref tips, then we'd behave similarly (happily show the ref, but then barf when we started traversing). We retain the GIT_REF_PARANOIA variable, but simply default it to "1" instead of "0". That gives the user an escape hatch for loosening this when working with a corrupt repository. It won't work across a remote connection to upload-pack (because we can't necessarily set environment variables on the remote), but there the client has other options (e.g., choosing which refs to fetch). As a bonus, this also makes ref iteration faster in general (because we don't have to call has_object_file() for each ref), though probably not noticeably so in the general case. In a repo with a million refs, it shaved a few hundred milliseconds off of upload-pack's advertisement; that's noticeable, but most repos are not nearly that large. The possible downside here is that any operation which iterates refs but doesn't ever open their objects may now quietly claim to have X when the object is corrupted (e.g., "git rev-list new-branch --not --all" will treat a broken ref as uninteresting). But again, that's not really any different than corruption below the ref level. We might have refs/heads/old-branch as non-corrupt, but we are not actively checking that we have the entire reachable history. Or the pointed-to object could even be corrupted on-disk (but our "do we have it" check would still succeed). In that sense, this is merely bringing ref-corruption in line with general object corruption. One alternative implementation would be to actually check for broken refs, and then _immediately die_ if we see any. That would cause the "rev-list --not --all" case above to abort immediately. But in many ways that's the worst of all worlds: - it still spends time looking up the objects an extra time - it still doesn't catch corruption below the ref level - it's even more inconvenient; with the current implementation of GIT_REF_PARANOIA for something like upload-pack, we can make the advertisement and let the client choose a non-broken piece of history. If we bail as soon as we see a broken ref, they cannot even see the advertisement. The test changes here show some of the fallout. A non-destructive "git repack -adk" now fails by default (but we can override it). Deleting a broken ref now actually tells the hooks the correct "before" state, rather than a confusing null oid. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- Documentation/git.txt | 19 ++++++++++--------- refs.c | 2 +- t/t5312-prune-corruption.sh | 10 ++++++++-- t/t5516-fetch-push.sh | 7 ++++--- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index abace9eac2..d63c65e67d 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -867,15 +867,16 @@ for full details. end user, to be recorded in the body of the reflog. `GIT_REF_PARANOIA`:: - If set to `1`, include broken or badly named refs when iterating - over lists of refs. In a normal, non-corrupted repository, this - does nothing. However, enabling it may help git to detect and - abort some operations in the presence of broken refs. Git sets - this variable automatically when performing destructive - operations like linkgit:git-prune[1]. You should not need to set - it yourself unless you want to be paranoid about making sure - an operation has touched every ref (e.g., because you are - cloning a repository to make a backup). + If set to `0`, ignore broken or badly named refs when iterating + over lists of refs. Normally Git will try to include any such + refs, which may cause some operations to fail. This is usually + preferable, as potentially destructive operations (e.g., + linkgit:git-prune[1]) are better off aborting rather than + ignoring broken refs (and thus considering the history they + point to as not worth saving). The default value is `1` (i.e., + be paranoid about detecting and aborting all operations). You + should not normally need to set this to `0`, but it may be + useful when trying to salvage data from a corrupted repository. `GIT_ALLOW_PROTOCOL`:: If set to a colon-separated list of protocols, behave as if diff --git a/refs.c b/refs.c index e72510813e..ac19c689fa 100644 --- a/refs.c +++ b/refs.c @@ -1420,7 +1420,7 @@ struct ref_iterator *refs_ref_iterator_begin( if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) { if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); + ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 1); if (ref_paranoia) { flags |= DO_FOR_EACH_INCLUDE_BROKEN; flags |= DO_FOR_EACH_OMIT_DANGLING_SYMREFS; diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index d8ec5a7462..ea889c088a 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -49,11 +49,17 @@ test_expect_success 'put bogus object into pack' ' git cat-file -e $bogus ' -test_expect_success 'non-destructive repack ignores bogus name' ' +test_expect_success 'non-destructive repack bails on bogus ref' ' create_bogus_ref && - git repack -adk + test_must_fail git repack -adk ' +test_expect_success 'GIT_REF_PARANOIA=0 overrides safety' ' + create_bogus_ref && + GIT_REF_PARANOIA=0 git repack -adk +' + + test_expect_success 'destructive repack keeps packed object' ' create_bogus_ref && test_must_fail git repack -Ad --unpack-unreachable=now && diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index b13553ecf4..8212ca56dc 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -707,20 +707,21 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho test_expect_success 'deleting dangling ref triggers hooks with correct args' ' mk_test_with_hooks testrepo heads/branch && + orig=$(git -C testrepo rev-parse refs/heads/branch) && rm -f testrepo/.git/objects/??/* && git push testrepo :refs/heads/branch && ( cd testrepo/.git && cat >pre-receive.expect <<-EOF && - $ZERO_OID $ZERO_OID refs/heads/branch + $orig $ZERO_OID refs/heads/branch EOF cat >update.expect <<-EOF && - refs/heads/branch $ZERO_OID $ZERO_OID + refs/heads/branch $orig $ZERO_OID EOF cat >post-receive.expect <<-EOF && - $ZERO_OID $ZERO_OID refs/heads/branch + $orig $ZERO_OID refs/heads/branch EOF cat >post-update.expect <<-EOF && From 5d1f5b8cd4bec8fbb405e32b1208955c93240f17 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:46:37 -0400 Subject: [PATCH 68/71] repack, prune: drop GIT_REF_PARANOIA settings Now that GIT_REF_PARANOIA is the default, we don't need to selectively enable it for destructive operations. In fact, it's harmful to do so, because it overrides any GIT_REF_PARANOIA=0 setting that the user may have provided (because they're trying to work around some corruption). With these uses gone, we can further clean up the ref_paranoia global, and make it a static variable inside the refs code. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- builtin/prune.c | 1 - builtin/repack.c | 3 --- cache.h | 8 -------- environment.c | 1 - refs.c | 2 ++ 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/builtin/prune.c b/builtin/prune.c index 02c6ab7cba..485c9a3c56 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -143,7 +143,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = TIME_MAX; save_commit_buffer = 0; read_replace_refs = 0; - ref_paranoia = 1; repo_init_revisions(the_repository, &revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); diff --git a/builtin/repack.c b/builtin/repack.c index c1a209013b..cb9f4bfed3 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -586,15 +586,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_pushf(&cmd.args, "--unpack-unreachable=%s", unpack_unreachable); - strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); } else if (pack_everything & LOOSEN_UNREACHABLE) { strvec_push(&cmd.args, "--unpack-unreachable"); } else if (keep_unreachable) { strvec_push(&cmd.args, "--keep-unreachable"); strvec_push(&cmd.args, "--pack-loose-unreachable"); - } else { - strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); } } } else if (geometry) { diff --git a/cache.h b/cache.h index f6295f3b04..f445008895 100644 --- a/cache.h +++ b/cache.h @@ -994,14 +994,6 @@ extern const char *core_fsmonitor; extern int core_apply_sparse_checkout; extern int core_sparse_checkout_cone; -/* - * Include broken refs in all ref iterations, which will - * generally choke dangerous operations rather than letting - * them silently proceed without taking the broken ref into - * account. - */ -extern int ref_paranoia; - /* * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value). */ diff --git a/environment.c b/environment.c index b4ba4fa22d..7923ab21dd 100644 --- a/environment.c +++ b/environment.c @@ -31,7 +31,6 @@ int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ int warn_ambiguous_refs = 1; int warn_on_object_refname_ambiguity = 1; -int ref_paranoia = -1; int repository_format_precious_objects; int repository_format_worktree_config; const char *git_commit_encoding; diff --git a/refs.c b/refs.c index ac19c689fa..d0f4e8726b 100644 --- a/refs.c +++ b/refs.c @@ -1419,6 +1419,8 @@ struct ref_iterator *refs_ref_iterator_begin( struct ref_iterator *iter; if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) { + static int ref_paranoia = -1; + if (ref_paranoia < 0) ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 1); if (ref_paranoia) { From 1763334caf6c060f38b3310960b38cd3b1d54687 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:48:05 -0400 Subject: [PATCH 69/71] ref-filter: stop setting FILTER_REFS_INCLUDE_BROKEN Of the ref-filter callers, for-each-ref and git-branch both set the INCLUDE_BROKEN flag (but git-tag does not, which is a weird inconsistency). But now that GIT_REF_PARANOIA is on by default, that produces almost the same outcome for all three. The one exception is that GIT_REF_PARANOIA will omit dangling symrefs. That's a better behavior for these tools, as they would never include such a symref in the main output anyway (they can't, as it doesn't point to an object). Instead they issue a warning to stderr. But that warning is somewhat useless; a dangling symref is a perfectly reasonable thing to have in your repository, and is not a sign of corruption. It's much friendlier to just quietly ignore it. And in terms of robustness, the warning gains us little. It does not impact the exit code of either tool. So while the warning _might_ clue in a user that they have an unexpected broken symref, it would not help any kind of scripted use. This patch converts for-each-ref and git-branch to stop using the INCLUDE_BROKEN flag. That gives them more reasonable behavior, and harmonizes them with git-tag. We have to change one test to adapt to the situation. t1430 tries to trigger all of the REF_ISBROKEN behaviors from the underlying ref code. It uses for-each-ref to do so (because there isn't any other mechanism). That will no longer issue a warning about the symref which points to an invalid name, as it's considered dangling (and we can instead be sure that it's _not_ mentioned on stderr). Note that we do still complain about the illegally named "broken..symref"; its problem is not that it's dangling, but the name of the symref itself is illegal. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- builtin/branch.c | 2 +- builtin/for-each-ref.c | 2 +- t/t1430-bad-ref-name.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 03c7b7253a..0b7ed82654 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -427,7 +427,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin memset(&array, 0, sizeof(array)); - filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN); + filter_refs(&array, filter, filter->kind); if (filter->verbose) maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 89cb6307d4..642b4b888f 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -77,7 +77,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) filter.name_patterns = argv; filter.match_as_path = 1; - filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN); + filter_refs(&array, &filter, FILTER_REFS_ALL); ref_array_sort(sorting, &array); if (!maxcount || array.nr < maxcount) diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index b1839e0877..fa3aeb80f2 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -170,7 +170,7 @@ test_expect_success 'for-each-ref emits warnings for broken names' ' ! grep -e "badname" output && ! grep -e "broken\.\.\.symref" output && test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error && - test_i18ngrep "ignoring broken ref refs/heads/badname" error && + test_i18ngrep ! "ignoring broken ref refs/heads/badname" error && test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error ' From 2d653c50364aeccb604f6b4680190824debf637a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:48:20 -0400 Subject: [PATCH 70/71] ref-filter: drop broken-ref code entirely Now that none of our callers passes the INCLUDE_BROKEN flag, we can drop it entirely, along with the code to plumb it through to the for_each_fullref_in() functions. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- ref-filter.c | 11 ++++------- ref-filter.h | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ref-filter.c b/ref-filter.c index 93ce2a6ef2..e59bb4cf9c 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2405,13 +2405,10 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int { struct ref_filter_cbdata ref_cbdata; int ret = 0; - unsigned int broken = 0; ref_cbdata.array = array; ref_cbdata.filter = filter; - if (type & FILTER_REFS_INCLUDE_BROKEN) - broken = 1; filter->kind = type & FILTER_REFS_KIND_MASK; init_contains_cache(&ref_cbdata.contains_cache); @@ -2428,13 +2425,13 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int * of filter_ref_kind(). */ if (filter->kind == FILTER_REFS_BRANCHES) - ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, 0); else if (filter->kind == FILTER_REFS_REMOTES) - ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, 0); else if (filter->kind == FILTER_REFS_TAGS) - ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, 0); else if (filter->kind & FILTER_REFS_ALL) - ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, 0); if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) head_ref(ref_filter_handler, &ref_cbdata); } diff --git a/ref-filter.h b/ref-filter.h index c15dee8d6b..b636f4389d 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -13,7 +13,6 @@ #define QUOTE_PYTHON 4 #define QUOTE_TCL 8 -#define FILTER_REFS_INCLUDE_BROKEN 0x0001 #define FILTER_REFS_TAGS 0x0002 #define FILTER_REFS_BRANCHES 0x0004 #define FILTER_REFS_REMOTES 0x0008 From 67985e4e4aa85f11593b1aec35cf7cd7e9d02fba Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 24 Sep 2021 14:48:48 -0400 Subject: [PATCH 71/71] refs: drop "broken" flag from for_each_fullref_in() No callers pass in anything but "0" here. Likewise to our sibling functions. Note that some of them ferry along the flag, but none of their callers pass anything but "0" either. Nor is anybody likely to change that. Callers which really want to see all of the raw refs use for_each_rawref(). And anybody interested in iterating a subset of the refs will likely be happy to use the now-default behavior of showing broken refs, but omitting dangling symlinks. So we can get rid of this whole feature. Signed-off-by: Jeff King Reviewed-by: Jonathan Tan Signed-off-by: Junio C Hamano --- builtin/rev-parse.c | 4 ++-- ls-refs.c | 2 +- ref-filter.c | 19 +++++++++---------- refs.c | 22 ++++++---------------- refs.h | 9 +++------ revision.c | 2 +- 6 files changed, 22 insertions(+), 36 deletions(-) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 22c4e1a4ff..8480a59f57 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -863,8 +863,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--bisect")) { - for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0); - for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0); + for_each_fullref_in("refs/bisect/bad", show_reference, NULL); + for_each_fullref_in("refs/bisect/good", anti_reference, NULL); continue; } if (opt_with_value(arg, "--branches", &arg)) { diff --git a/ls-refs.c b/ls-refs.c index be09568910..7fe9675d3c 100644 --- a/ls-refs.c +++ b/ls-refs.c @@ -171,7 +171,7 @@ int ls_refs(struct repository *r, struct packet_reader *request) if (!data.prefixes.nr) strvec_push(&data.prefixes, ""); for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v, - send_ref, &data, 0); + send_ref, &data); packet_fflush(stdout); strvec_clear(&data.prefixes); strbuf_release(&data.buf); diff --git a/ref-filter.c b/ref-filter.c index e59bb4cf9c..e15f0c4bac 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2100,8 +2100,7 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname) */ static int for_each_fullref_in_pattern(struct ref_filter *filter, each_ref_fn cb, - void *cb_data, - int broken) + void *cb_data) { if (!filter->match_as_path) { /* @@ -2109,7 +2108,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * prefixes like "refs/heads/" etc. are stripped off, * so we have to look at everything: */ - return for_each_fullref_in("", cb, cb_data, broken); + return for_each_fullref_in("", cb, cb_data); } if (filter->ignore_case) { @@ -2118,16 +2117,16 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * so just return everything and let the caller * sort it out. */ - return for_each_fullref_in("", cb, cb_data, broken); + return for_each_fullref_in("", cb, cb_data); } if (!filter->name_patterns[0]) { /* no patterns; we have to look at everything */ - return for_each_fullref_in("", cb, cb_data, broken); + return for_each_fullref_in("", cb, cb_data); } return for_each_fullref_in_prefixes(NULL, filter->name_patterns, - cb, cb_data, broken); + cb, cb_data); } /* @@ -2425,13 +2424,13 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int * of filter_ref_kind(). */ if (filter->kind == FILTER_REFS_BRANCHES) - ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, 0); + ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata); else if (filter->kind == FILTER_REFS_REMOTES) - ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, 0); + ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata); else if (filter->kind == FILTER_REFS_TAGS) - ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, 0); + ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata); else if (filter->kind & FILTER_REFS_ALL) - ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, 0); + ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata); if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) head_ref(ref_filter_handler, &ref_cbdata); } diff --git a/refs.c b/refs.c index d0f4e8726b..2be0d0f057 100644 --- a/refs.c +++ b/refs.c @@ -1522,25 +1522,16 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data); } -int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken) +int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data) { - enum do_for_each_ref_flags flag = 0; - - if (broken) - flag = DO_FOR_EACH_INCLUDE_BROKEN; return do_for_each_ref(get_main_ref_store(the_repository), - prefix, fn, 0, flag, cb_data); + prefix, fn, 0, 0, cb_data); } int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, - each_ref_fn fn, void *cb_data, - unsigned int broken) + each_ref_fn fn, void *cb_data) { - enum do_for_each_ref_flags flag = 0; - - if (broken) - flag = DO_FOR_EACH_INCLUDE_BROKEN; - return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data); + return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data); } int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data) @@ -1632,8 +1623,7 @@ static void find_longest_prefixes(struct string_list *out, int for_each_fullref_in_prefixes(const char *namespace, const char **patterns, - each_ref_fn fn, void *cb_data, - unsigned int broken) + each_ref_fn fn, void *cb_data) { struct string_list prefixes = STRING_LIST_INIT_DUP; struct string_list_item *prefix; @@ -1648,7 +1638,7 @@ int for_each_fullref_in_prefixes(const char *namespace, for_each_string_list_item(prefix, &prefixes) { strbuf_addstr(&buf, prefix->string); - ret = for_each_fullref_in(buf.buf, fn, cb_data, broken); + ret = for_each_fullref_in(buf.buf, fn, cb_data); if (ret) break; strbuf_setlen(&buf, namespace_len); diff --git a/refs.h b/refs.h index 48970dfc7e..10e7696a64 100644 --- a/refs.h +++ b/refs.h @@ -342,10 +342,8 @@ int for_each_ref(each_ref_fn fn, void *cb_data); int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, - each_ref_fn fn, void *cb_data, - unsigned int broken); -int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, - unsigned int broken); + each_ref_fn fn, void *cb_data); +int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data); /** * iterate all refs in "patterns" by partitioning patterns into disjoint sets @@ -354,8 +352,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, * callers should be prepared to ignore references that they did not ask for. */ int for_each_fullref_in_prefixes(const char *namespace, const char **patterns, - each_ref_fn fn, void *cb_data, - unsigned int broken); + each_ref_fn fn, void *cb_data); /** * iterate refs from the respective area. */ diff --git a/revision.c b/revision.c index 0dabb5a0bc..b7a2baad0e 100644 --- a/revision.c +++ b/revision.c @@ -2548,7 +2548,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn, struct strbuf bisect_refs = STRBUF_INIT; int status; strbuf_addf(&bisect_refs, "refs/bisect/%s", term); - status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0); + status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data); strbuf_release(&bisect_refs); return status; }