[v3] fetch: optionally allow disabling FETCH_HEAD update
diff mbox series

Message ID xmqqh7t740vu.fsf_-_@gitster.c.googlers.com
State New
Headers show
Series
  • [v3] fetch: optionally allow disabling FETCH_HEAD update
Related show

Commit Message

Junio C Hamano Aug. 13, 2020, 4:37 a.m. UTC
If you run fetch but record the result in remote-tracking branches,
and either if you do nothing with the fetched refs (e.g. you are
merely mirroring) or if you always work from the remote-tracking
refs (e.g. you fetch and then merge origin/branchname separately),
you can get away with having no FETCH_HEAD at all.

Teach "git fetch" a command line option "--[no-]write-fetch-head".
The default is to write FETCH_HEAD, and the option is primarily
meant to be used with the "--no-" prefix to override this default,
because there is no matching fetch.writeFetchHEAD configuration
variable to flip the default to off (in which case, the positive
form may become necessary to defeat it).

Note that under "--dry-run" mode, FETCH_HEAD is never written;
otherwise you'd see list of objects in the file that you do not
actually have.  Passing `--write-fetch-head` does not force `git
fetch` to write the file.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

 * So, it becomes much smaller by punting the whole configuration
   thing, as we do not need the extra code for config parsing and
   there is no need for code to override the user configuration when
   driving "git fetch" from "git pull".

 Documentation/fetch-options.txt |  9 +++++++++
 builtin/fetch.c                 | 15 ++++++++++++---
 t/t5510-fetch.sh                | 15 +++++++++++++--
 3 files changed, 34 insertions(+), 5 deletions(-)

Comments

Derrick Stolee Aug. 14, 2020, 1:13 a.m. UTC | #1
On 8/13/2020 12:37 AM, Junio C Hamano wrote:
> If you run fetch but record the result in remote-tracking branches,
> and either if you do nothing with the fetched refs (e.g. you are
> merely mirroring) or if you always work from the remote-tracking
> refs (e.g. you fetch and then merge origin/branchname separately),
> you can get away with having no FETCH_HEAD at all.
> 
> Teach "git fetch" a command line option "--[no-]write-fetch-head".
> The default is to write FETCH_HEAD, and the option is primarily
> meant to be used with the "--no-" prefix to override this default,
> because there is no matching fetch.writeFetchHEAD configuration
> variable to flip the default to off (in which case, the positive
> form may become necessary to defeat it).
> 
> Note that under "--dry-run" mode, FETCH_HEAD is never written;
> otherwise you'd see list of objects in the file that you do not
> actually have.  Passing `--write-fetch-head` does not force `git
> fetch` to write the file.
> 
> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> 
>  * So, it becomes much smaller by punting the whole configuration
>    thing, as we do not need the extra code for config parsing and
>    there is no need for code to override the user configuration when
>    driving "git fetch" from "git pull".

Sounds good. I'll incorporate this patch into my next version,
except it seems you dropped this test:

diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
index 159afa7ac8..db1a381cd9 100755
--- a/t/t5521-pull-options.sh
+++ b/t/t5521-pull-options.sh
@@ -85,6 +85,13 @@ test_expect_success 'git pull --cleanup errors early on invalid argument' '
        test -s err)
 '
 
+test_expect_success 'git pull --no-write-fetch-head fails' '
+       mkdir clonedwfh &&
+       (cd clonedwfh && git init &&
+       test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err &&
+       test_must_be_empty out &&
+       test_i18ngrep "no-write-fetch-head" err)
+'
 
 test_expect_success 'git pull --force' '
        mkdir clonedoldstyle &&

which I changed "test_must_fail" to "test_expect_code 129" to
demonstrate that this is a usage error, not just any error.

Thanks,
-Stolee
Junio C Hamano Aug. 14, 2020, 1:32 a.m. UTC | #2
Derrick Stolee <stolee@gmail.com> writes:

>>  * So, it becomes much smaller by punting the whole configuration
>>    thing, as we do not need the extra code for config parsing and
>>    there is no need for code to override the user configuration when
>>    driving "git fetch" from "git pull".
>
> Sounds good. I'll incorporate this patch into my next version,
> except it seems you dropped this test:

Yup.  

As we are not touching "pull" at all, the test I removed would make
as much sense as a hypothetical one where its "git pull" is replaced
with "git cat-file" or any other unrelated subcommand, and that is
why I removed it.  But I am OK if you resurrect it---everybody thinks
"fetch" and "pull" are closely related after all.

Thanks.



> diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
> index 159afa7ac8..db1a381cd9 100755
> --- a/t/t5521-pull-options.sh
> +++ b/t/t5521-pull-options.sh
> @@ -85,6 +85,13 @@ test_expect_success 'git pull --cleanup errors early on invalid argument' '
>         test -s err)
>  '
>  
> +test_expect_success 'git pull --no-write-fetch-head fails' '
> +       mkdir clonedwfh &&
> +       (cd clonedwfh && git init &&
> +       test_expect_code 129 git pull --no-write-fetch-head "../parent" >out 2>err &&
> +       test_must_be_empty out &&
> +       test_i18ngrep "no-write-fetch-head" err)
> +'
>  
>  test_expect_success 'git pull --force' '
>         mkdir clonedoldstyle &&
>
> which I changed "test_must_fail" to "test_expect_code 129" to
> demonstrate that this is a usage error, not just any error.
>
> Thanks,
> -Stolee

Patch
diff mbox series

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 495bc8ab5a..b65a758661 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -64,6 +64,15 @@  documented in linkgit:git-config[1].
 --dry-run::
 	Show what would be done, without making any changes.
 
+ifndef::git-pull[]
+--[no-]write-fetch-head::
+	Write the list of remote refs fetched in the `FETCH_HEAD`
+	file directly under `$GIT_DIR`.  This is the default.
+	Passing `--no-write-fetch-head` from the command line tells
+	Git not to write the file.  Under `--dry-run` option, the
+	file is never written.
+endif::git-pull[]
+
 -f::
 --force::
 	When 'git fetch' is used with `<src>:<dst>` refspec it may
diff --git a/builtin/fetch.c b/builtin/fetch.c
index c8b9366d3c..cb38e6f5ec 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -56,6 +56,7 @@  static int prune_tags = -1; /* unspecified */
 #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 
 static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int write_fetch_head = 1;
 static int verbosity, deepen_relative, set_upstream;
 static int progress = -1;
 static int enable_auto_gc = 1;
@@ -162,6 +163,8 @@  static struct option builtin_fetch_options[] = {
 		    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
 	OPT_BOOL(0, "dry-run", &dry_run,
 		 N_("dry run")),
+	OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+		 N_("write fetched references to the FETCH_HEAD file")),
 	OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
 	OPT_BOOL('u', "update-head-ok", &update_head_ok,
 		    N_("allow updating of HEAD ref")),
@@ -895,7 +898,9 @@  static int store_updated_refs(const char *raw_url, const char *remote_name,
 	const char *what, *kind;
 	struct ref *rm;
 	char *url;
-	const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
+	const char *filename = (!write_fetch_head
+				? "/dev/null"
+				: git_path_fetch_head(the_repository));
 	int want_status;
 	int summary_width = transport_summary_width(ref_map);
 
@@ -1329,7 +1334,7 @@  static int do_fetch(struct transport *transport,
 	}
 
 	/* if not appending, truncate FETCH_HEAD */
-	if (!append && !dry_run) {
+	if (!append && write_fetch_head) {
 		retcode = truncate_fetch_head();
 		if (retcode)
 			goto cleanup;
@@ -1596,7 +1601,7 @@  static int fetch_multiple(struct string_list *list, int max_children)
 	int i, result = 0;
 	struct strvec argv = STRVEC_INIT;
 
-	if (!append && !dry_run) {
+	if (!append && write_fetch_head) {
 		int errcode = truncate_fetch_head();
 		if (errcode)
 			return errcode;
@@ -1797,6 +1802,10 @@  int cmd_fetch(int argc, const char **argv, const char *prefix)
 	if (depth || deepen_since || deepen_not.nr)
 		deepen = 1;
 
+	/* FETCH_HEAD never gets updated in --dry-run mode */
+	if (dry_run)
+		write_fetch_head = 0;
+
 	if (all) {
 		if (argc == 1)
 			die(_("fetch --all does not take a repository argument"));
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 9850ecde5d..5bd1f953af 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -539,13 +539,24 @@  test_expect_success 'fetch into the current branch with --update-head-ok' '
 
 '
 
-test_expect_success 'fetch --dry-run' '
-
+test_expect_success 'fetch --dry-run does not touch FETCH_HEAD' '
 	rm -f .git/FETCH_HEAD &&
 	git fetch --dry-run . &&
 	! test -f .git/FETCH_HEAD
 '
 
+test_expect_success '--no-write-fetch-head does not touch FETCH_HEAD' '
+	rm -f .git/FETCH_HEAD &&
+	git fetch --no-write-fetch-head . &&
+	! test -f .git/FETCH_HEAD
+'
+
+test_expect_success '--write-fetch-head gets defeated by --dry-run' '
+	rm -f .git/FETCH_HEAD &&
+	git fetch --dry-run --write-fetch-head . &&
+	! test -f .git/FETCH_HEAD
+'
+
 test_expect_success "should be able to fetch with duplicate refspecs" '
 	mkdir dups &&
 	(