Subject: Re: [PATCH v3] checkout & worktree: introduce checkout.implicitRemote

From: Ævar Arnfjörð Bjarmason <[email-masked]>
Date: Thu, 31 May 2018 09:45:52 +0200

On Thu, May 24 2018, Ævar Arnfjörð Bjarmason wrote:

>  struct tracking_name_data {
>  	/* const */ char *src_ref;
>  	char *dst_ref;
>  	struct object_id *dst_oid;
>  	int unique;
> +	const char *implicit_remote;
> +	char *implicit_dst_ref;
>  };

There's a bug here that I'll fix in a v3. We need to have a implicit_*
variant for dst_oid as well. Currently this will be buggy and check out
origin/<branch>, but then check the index out to the tree of whatever
the last <someremote>/<branch> we iterated over was.

Easiy fix and I already have it locally, I just want to improve some of
the testing. I missed it because in my tests I'd just re-add the same
remote again, so the trees were the same.

>  static int check_tracking_name(struct remote *remote, void *cb_data)
> @@ -20,6 +23,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
>  		free(query.dst);
>  		return 0;
>  	}
> +	if (cb->implicit_remote && !strcmp(remote->name, cb->implicit_remote))
> +		cb->implicit_dst_ref = xstrdup(query.dst);
>  	if (cb->dst_ref) {
>  		free(query.dst);
>  		cb->unique = 0;
> @@ -31,13 +36,21 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
>
>  const char *unique_tracking_name(const char *name, struct object_id *oid)
>  {
> -	struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
> +	const char *implicit_remote = NULL;
> +	struct tracking_name_data cb_data = { NULL, NULL, NULL, 1, NULL, NULL };
> +	if (!git_config_get_string_const("checkout.implicitremote", &implicit_remote))
> +		cb_data.implicit_remote = implicit_remote;
>  	cb_data.src_ref = xstrfmt("refs/heads/%s", name);
>  	cb_data.dst_oid = oid;
>  	for_each_remote(check_tracking_name, &cb_data);
>  	free(cb_data.src_ref);
> -	if (cb_data.unique)
> +	free((char *)implicit_remote);
> +	if (cb_data.unique) {
> +		free(cb_data.implicit_dst_ref);
>  		return cb_data.dst_ref;
> +	}
>  	free(cb_data.dst_ref);
> +	if (cb_data.implicit_dst_ref)
> +		return cb_data.implicit_dst_ref;
>  	return NULL;
>  }
> diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh
> index 3e5ac81bd2..da6bd74bbc 100755
> --- a/t/t2024-checkout-dwim.sh
> +++ b/t/t2024-checkout-dwim.sh
> @@ -68,6 +68,16 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '
>  	test_branch master
>  '
>
> +test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.implicitRemote #1' '
> +	git checkout -B master &&
> +	test_might_fail git branch -D foo &&
> +
> +	git -c checkout.implicitRemote=repo_a checkout foo &&
> +	test_branch foo &&
> +	test_cmp_rev remotes/repo_a/foo HEAD &&
> +	test_branch_upstream foo repo_a foo
> +'
> +
>  test_expect_success 'checkout of branch from a single remote succeeds #1' '
>  	git checkout -B master &&
>  	test_might_fail git branch -D bar &&
> diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh
> index 2240498924..271a6413f0 100755
> --- a/t/t2025-worktree-add.sh
> +++ b/t/t2025-worktree-add.sh
> @@ -402,6 +402,24 @@ test_expect_success '"add" <path> <branch> dwims' '
>  	)
>  '
>
> +test_expect_success '"add" <path> <branch> dwims with checkout.implicitRemote' '
> +	test_when_finished rm -rf repo_upstream repo_dwim foo &&
> +	setup_remote_repo repo_upstream repo_dwim &&
> +	git init repo_dwim &&
> +	(
> +		cd repo_dwim &&
> +		git remote add repo_upstream2 ../repo_upstream &&
> +		git fetch repo_upstream2 &&
> +		test_must_fail git worktree add ../foo foo &&
> +		git -c checkout.implicitRemote=repo_upstream worktree add ../foo foo
> +	) &&
> +	(
> +		cd foo &&
> +		test_branch_upstream foo repo_upstream foo &&
> +		test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
> +	)
> +'
> +
>  test_expect_success 'git worktree add does not match remote' '
>  	test_when_finished rm -rf repo_a repo_b foo &&
>  	setup_remote_repo repo_a repo_b &&