Skip to contentSkip to conversation

worktree: make add <path> dwim

PATCH Superseded Thomas Gummerer 4 participants 36 messages Nov 22, 2017 – Nov 29, 2017 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): mailto:[email protected]

#1 lore raw

v4 cover make git worktree add dwim more

The previous rounds were at
https://public-inbox.org/git/[email protected]/,
https://public-inbox.org/git/[email protected]/ and
https://public-inbox.org/git/[email protected]/.

Thanks Eric for the review of the previous round.

The main change is that the --[no-]track flag now works generally instead of just disabling the new dwim mode.

Interdiff below:

Documentation/git-worktree.txt +12 -5
index eedead2c4c..abc8f1f50d 100644
@@ -56,22 +56,23 @@ If <branch> is not found, and neither `-b` nor `-B` nor `--detach` are
 used, but there does exist a tracking branch in exactly one remote
 (call it <remote>) with a matching name, treat as equivalent to
 ------------
-$ git worktree add -b <branch> <path> <remote>/<branch>
+$ git worktree add --track -b <branch> <path> <remote>/<branch>
 ------------
 +
 If `<branch>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, if there exists a tracking branch in exactly
-one remote (call it <remote>) matching the basename of the path
-(call it <branch>), treat it as equivalent to
+one remote (call it `<remote>`) matching the basename of the path
+(call it `<branch>`), treat it as equivalent to
 ------------
-$ git worktree add -b <branch> <path> <remote>/<branch>
+$ git worktree add --track -b <branch> <path> <remote>/<branch>
 ------------
-If no tracking branch exists in exactly one remote, <branch> is
+If no tracking branch exists in exactly one remote, `<branch>` is
 created based on HEAD, as if `-b $(basename <path>)` was specified.
 +
 To disable the behaviour of trying to match the basename of <path> to
 a remote, and always create a new branch from HEAD, the `--no-track`
 flag can be passed to `git worktree add`.
+
 list::
 
 List details of each worktree.  The main worktree is listed first, followed by
@@ -123,6 +124,12 @@ OPTIONS
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
 
+--[no-]track::
+	With `--track` `<branch>` is set as "tracking" branch for
+	`<new-branch>`.  This is the default if `<branch>` is a remote
+	tracking branch, and can be suppressed with `--no-track`.  See
+	also linkgit:git-branch[1].
+
 --lock::
 	Keep the working tree locked after creation. This is the
 	equivalent of `git worktree lock` after `git worktree add`,
builtin/worktree.c +14 -7
index b2a6dd020c..cbcceb0385 100644
@@ -342,7 +342,7 @@ static int add(int ac, const char **av, const char *prefix)
 	const char *new_branch_force = NULL;
 	char *path;
 	const char *branch;
-	int track_dwim = 1;
+	const char *opt_track = NULL;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -352,7 +352,9 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
 		OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 		OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
-		OPT_BOOL(0, "track", &track_dwim, N_("checkout upstream branch if there's a unique match")),
+		OPT_PASSTHRU(0, "track", &opt_track, NULL,
+			     N_("set up tracking mode (see git-branch(1))"),
+			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
 		OPT_END()
 	};
 
@@ -387,7 +389,7 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
-		if (track_dwim) {
+		if (!opt_track || strcmp(opt_track, "--no-track")) {
 			struct object_id oid;
 			const char *remote =
 				unique_tracking_name(opts.new_branch, &oid);
@@ -402,11 +404,12 @@ static int add(int ac, const char **av, const char *prefix)
 		const char *remote;
 
 		commit = lookup_commit_reference_by_name(branch);
-		if (!commit)
+		if (!commit) {
 			remote = unique_tracking_name(branch, &oid);
-		if (!commit && remote) {
-			opts.new_branch = branch;
-			branch = remote;
+			if (remote) {
+				opts.new_branch = branch;
+				branch = remote;
+			}
 		}
 	}
 
@@ -418,9 +421,13 @@ static int add(int ac, const char **av, const char *prefix)
 			argv_array_push(&cp.args, "--force");
 		argv_array_push(&cp.args, opts.new_branch);
 		argv_array_push(&cp.args, branch);
+		if (opt_track)
+			argv_array_push(&cp.args, opt_track);
 		if (run_command(&cp))
 			return -1;
 		branch = opts.new_branch;
+	} else if (opt_track) {
+		die(_("--[no-]track can only be used if a new branch is created"));
 	}
 
 	UNLEAK(path);
t/t2025-worktree-add.sh +63 -61

171 lines not on this page · raw message

Thomas Gummerer (4):
checkout: factor out functions to new lib file
worktree: add --[no-]track option to the add subcommand
worktree: make add <path> <branch> dwim
worktree: make add <path> dwim

 Documentation/git-worktree.txt |  27 ++++++++++-
 Makefile                       |   1 +
 builtin/checkout.c             |  41 +---------------
 builtin/worktree.c             |  31 ++++++++++++
 checkout.c                     |  42 ++++++++++++++++
 checkout.h                     |  13 +++++
 t/t2025-worktree-add.sh        | 108 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 221 insertions(+), 42 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Signature · 1 line
2.15.0.345.gf926f18f3
#2 lore raw

PATCH v4 1/4 checkout: factor out functions to new lib file

Factor the functions out, so they can be re-used from other places. In particular these functions will be re-used in builtin/worktree.c to make git worktree add dwim more.

While there add some docs to the function.

---
 Makefile           |  1 +
 builtin/checkout.c | 41 +----------------------------------------
 checkout.c         | 42 ++++++++++++++++++++++++++++++++++++++++++
 checkout.h         | 13 +++++++++++++
 4 files changed, 57 insertions(+), 40 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Makefile +1 -0
index cd75985991..8d603c7443 100644
@@ -757,6 +757,7 @@ LIB_OBJS += branch.o
 LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
+LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
builtin/checkout.c +1 -40

52 lines not on this page · raw message

checkout.c new file +42 -0

42 lines not on this page · raw message

checkout.h new file +13 -0
new file mode 100644
index 0000000000..9980711179
@@ -0,0 +1,13 @@
+#ifndef CHECKOUT_H
+#define CHECKOUT_H
+
+#include "cache.h"
+
+/*
+ * Check if the branch name uniquely matches a branch name on a remote
+ * tracking branch.  Return the name of the remote if such a branch
+ * exists, NULL otherwise.
+ */
+extern const char *unique_tracking_name(const char *name, struct object_id *oid);
+
+#endif /* CHECKOUT_H */
Signature · 1 line
2.15.0.345.gf926f18f3
#3 lore raw

Re: [PATCH v4 1/4] checkout: factor out functions to new lib file

Quoted 33 lines · Thomas Gummerer

Factor the functions out, so they can be re-used from other places. In particular these functions will be re-used in builtin/worktree.c to make git worktree add dwim more.

While there add some docs to the function.

---
 Makefile           |  1 +
 builtin/checkout.c | 41 +----------------------------------------
 checkout.c         | 42 ++++++++++++++++++++++++++++++++++++++++++
 checkout.h         | 13 +++++++++++++
 4 files changed, 57 insertions(+), 40 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Makefile +1 -0
index cd75985991..8d603c7443 100644
@@ -757,6 +757,7 @@ LIB_OBJS += branch.o
 LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
+LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
builtin/checkout.c +0 -0
index fc4f8fd2ea..9e1cfd10b3 100644

builtin/checkout.c line 6

Quoted 6 lines
builtin/checkout.c +1 -0
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "config.h"
+#include "checkout.h"
 #include "lockfile.h"
 #include "parse-options.h"
 #include "refs.h"

With this, and also ...

checkout.c line 2

Quoted 8 lines
checkout.c new file +2 -0
new file mode 100644
index 0000000000..b0c744d37a
@@ -0,0 +1,42 @@
+#include "cache.h"
+#include "remote.h"

... with this, I sort of expected that builtin/checkout.c no longer has to include "remote.h" but can now rely on the common helpers in this new file to perform anything remote-related operation. But it seems that it is not the case (yet).

Just recording my observation for future reference, as we might also want to move report_tracking(), etc., to this new file in the future.

#4 lore raw

PATCH v4 2/4 worktree: add --[no-]track option to the add subcommand

In reply to Thomas Gummerer

Currently 'git worktree add' sets up tracking branches if '<branch>' is a remote tracking branch, and doesn't set them up otherwise, as is the default for 'git branch'.

This may or may not be what the user wants. Allow overriding this behaviour with a --[no-]track flag that gets passed through to 'git branch'.

We already respect branch.autoSetupMerge, as 'git worktree' just calls 'git branch' internally.

---
 Documentation/git-worktree.txt |  6 +++++
 builtin/worktree.c             |  8 ++++++
 t/t2025-worktree-add.sh        | 55 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
Documentation/git-worktree.txt +6 -0
index b472acc356..798a642f84 100644
@@ -107,6 +107,12 @@ OPTIONS
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
 
+--[no-]track::
+	With `--track` `<branch>` is set as "tracking" branch for
+	`<new-branch>`.  This is the default if `<branch>` is a remote
+	tracking branch, and can be suppressed with `--no-track`.  See
+	also linkgit:git-branch[1].
+
 --lock::
 	Keep the working tree locked after creation. This is the
 	equivalent of `git worktree lock` after `git worktree add`,
builtin/worktree.c +8 -0
index 7b9307aa58..8f9446d43c 100644
@@ -341,6 +341,7 @@ static int add(int ac, const char **av, const char *prefix)
 	const char *new_branch_force = NULL;
 	char *path;
 	const char *branch;
+	const char *opt_track = NULL;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -350,6 +351,9 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
 		OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 		OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+		OPT_PASSTHRU(0, "track", &opt_track, NULL,
+			     N_("set up tracking mode (see git-branch(1))"),
+			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
 		OPT_END()
 	};
 
@@ -394,9 +398,13 @@ static int add(int ac, const char **av, const char *prefix)
 			argv_array_push(&cp.args, "--force");
 		argv_array_push(&cp.args, opts.new_branch);
 		argv_array_push(&cp.args, branch);
+		if (opt_track)
+			argv_array_push(&cp.args, opt_track);
 		if (run_command(&cp))
 			return -1;
 		branch = opts.new_branch;
+	} else if (opt_track) {
+		die(_("--[no-]track can only be used if a new branch is created"));
 	}
 
 	UNLEAK(path);
t/t2025-worktree-add.sh +55 -0

60 lines not on this page · raw message

Signature · 1 line
2.15.0.345.gf926f18f3
#5 lore raw

Re: [PATCH v4 2/4] worktree: add --[no-]track option to the add subcommand

Quoted 7 lines · Thomas Gummerer
t/t2025-worktree-add.sh +0 -0
index b5c47ac602..53042ce565 100755
@@ -313,5 +313,60 @@ test_expect_success 'checkout a branch under bisect' '
 test_expect_success 'rename a branch under bisect not allowed' '
 	test_must_fail git branch -M under-bisect bisect-with-new-name

t/t2025-worktree-add.sh test_expect_success 'checkout a branch under bisect' ' · line 324

Quoted 10 lines
t/t2025-worktree-add.sh +9 -0
 '
+# Is branch "refs/heads/$1" set to pull from "$2/$3"?
+test_branch_upstream () {
+	printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
+	{
+		git config "branch.$1.remote" &&
+		git config "branch.$1.merge"
+	} >actual.upstream &&
+	test_cmp expect.upstream actual.upstream
+}

OK.

t/t2025-worktree-add.sh test_expect_success 'checkout a branch under bisect' ' · line 333

Quoted 8 lines
t/t2025-worktree-add.sh +8 -0
+test_expect_success '--track sets up tracking' '
+	test_when_finished rm -rf track &&
+	git worktree add --track -b track track master &&
+	git config "branch.track.merge" &&
+	(
+		test_branch_upstream track . master
+	)
+'

Is this "git config" necessary, or is it a remnant of a debugging session? It is tested in the helper that branch.track.merge is set to something, and otherwise the helper would fail the same way as this standalnoe "git config" would, no?

Quoted 21 lines
t/t2025-worktree-add.sh +21 -0
+# setup remote repository $1 and repository $2 with $1 set up as
+# remote.  The remote has two branches, master and foo.
+setup_remote_repo () {
+	git init $1 &&
+	(
+		cd $1 &&
+		test_commit $1_master &&
+		git checkout -b foo &&
+		test_commit upstream_foo
+	) &&
+	git init $2 &&
+	(
+		cd $2 &&
+		test_commit $2_master &&
+		git remote add $1 ../$1 &&
+		git config remote.$1.fetch \
+			"refs/heads/*:refs/remotes/$1/*" &&
+		git fetch --all
+	)
+}
+

t/t2025-worktree-add.sh test_expect_success 'checkout a branch under bisect' ' · line 365

Quoted 10 lines
t/t2025-worktree-add.sh +10 -0
+test_expect_success '--no-track avoids setting up tracking' '
+	test_when_finished rm -rf repo_upstream repo_local foo &&
+	setup_remote_repo repo_upstream repo_local &&
+	(
+		cd repo_local &&
+		git worktree add --no-track -b foo ../foo repo_upstream/foo
+	) &&
+	(
+		cd foo &&
+		! test_branch_upstream foo repo_upstream foo &&

It is true that this test helper must yield failure. But what you expect probably is more than that, no? For example, the test helper would fail even if branch.foo.remote is set to the upstream as long as branch.foo.merge is not set to point at their foo, but what you really want to make sure is that neither configuration variable is set.

Quoted 7 lines
t/t2025-worktree-add.sh +5 -0
+		git rev-parse repo_upstream/foo >expect &&
+		git rev-parse foo >actual &&
+		test_cmp expect actual
+	)
+'
 
 test_done
#6 lore raw

Re: [PATCH v4 2/4] worktree: add --[no-]track option to the add subcommand

Quoted 22 lines · Junio C Hamano
Quoted 7 lines · Thomas Gummerer
t/t2025-worktree-add.sh +0 -0
index b5c47ac602..53042ce565 100755
@@ -313,5 +313,60 @@ test_expect_success 'checkout a branch under bisect' '
 test_expect_success 'rename a branch under bisect not allowed' '
 	test_must_fail git branch -M under-bisect bisect-with-new-name

t/t2025-worktree-add.sh test_expect_success 'checkout a branch under bisect' ' · line 324

Quoted 10 lines
t/t2025-worktree-add.sh +9 -0
 '
+# Is branch "refs/heads/$1" set to pull from "$2/$3"?
+test_branch_upstream () {
+	printf "%s\n" "$2" "refs/heads/$3" >expect.upstream &&
+	{
+		git config "branch.$1.remote" &&
+		git config "branch.$1.merge"
+	} >actual.upstream &&
+	test_cmp expect.upstream actual.upstream
+}

OK.

Quoted 13 lines

t/t2025-worktree-add.sh test_expect_success 'checkout a branch under bisect' ' · line 333

Quoted 8 lines
t/t2025-worktree-add.sh +8 -0
+test_expect_success '--track sets up tracking' '
+	test_when_finished rm -rf track &&
+	git worktree add --track -b track track master &&
+	git config "branch.track.merge" &&
+	(
+		test_branch_upstream track . master
+	)
+'

Is this "git config" necessary, or is it a remnant of a debugging session? It is tested in the helper that branch.track.merge is set to something, and otherwise the helper would fail the same way as this standalnoe "git config" would, no?

It's a remnant of a debugging session, sorry. It would indeed fail in the same way, so just leaving the 'test_branch_upstream' is enough. Also looking at that, there's no need for it to be in a subshell, will fix that as well.

Quoted 30 lines
Quoted 30 lines
t/t2025-worktree-add.sh +30 -0
+# setup remote repository $1 and repository $2 with $1 set up as
+# remote.  The remote has two branches, master and foo.
+setup_remote_repo () {
+	git init $1 &&
+	(
+		cd $1 &&
+		test_commit $1_master &&
+		git checkout -b foo &&
+		test_commit upstream_foo
+	) &&
+	git init $2 &&
+	(
+		cd $2 &&
+		test_commit $2_master &&
+		git remote add $1 ../$1 &&
+		git config remote.$1.fetch \
+			"refs/heads/*:refs/remotes/$1/*" &&
+		git fetch --all
+	)
+}
+
+test_expect_success '--no-track avoids setting up tracking' '
+	test_when_finished rm -rf repo_upstream repo_local foo &&
+	setup_remote_repo repo_upstream repo_local &&
+	(
+		cd repo_local &&
+		git worktree add --no-track -b foo ../foo repo_upstream/foo
+	) &&
+	(
+		cd foo &&
Quoted 8 lines

t/t2025-worktree-add.sh test_expect_success 'checkout a branch under bisect' ' · line 365

Quoted 1 line
t/t2025-worktree-add.sh +1 -0
+		! test_branch_upstream foo repo_upstream foo &&

It is true that this test helper must yield failure. But what you expect probably is more than that, no? For example, the test helper would fail even if branch.foo.remote is set to the upstream as long as branch.foo.merge is not set to point at their foo, but what you really want to make sure is that neither configuration variable is set.

Yeah you're right, this test is a bit too loose. Will fix that in the re-roll. Thanks!

Quoted 7 lines
Quoted 7 lines
t/t2025-worktree-add.sh +5 -0
+		git rev-parse repo_upstream/foo >expect &&
+		git rev-parse foo >actual &&
+		test_cmp expect actual
+	)
+'
 
 test_done
#7 lore raw

PATCH v4 3/4 worktree: make add <path> <branch> dwim

In reply to Thomas Gummerer

Currently 'git worktree add <path> <branch>', errors out when 'branch'
is not a local branch. It has no additional dwim'ing features that one
might expect.

Make it behave more like 'git checkout <branch>' when the branch doesn't exist locally, but a remote tracking branch uniquely matches the desired branch name, i.e. create a new branch from the remote tracking branch and set the upstream to the remote tracking branch.

As 'git worktree add' currently just dies in this situation, there are no backwards compatibility worries when introducing this feature.

---
 Documentation/git-worktree.txt |  7 +++++++
 builtin/worktree.c             | 16 ++++++++++++++++
 t/t2025-worktree-add.sh        | 21 +++++++++++++++++++++
 3 files changed, 44 insertions(+)
Documentation/git-worktree.txt +7 -0
index 798a642f84..45642d3b7f 100644
@@ -52,6 +52,13 @@ is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
 specified as `<branch>`; it is synonymous with `@{-1}`.
 +
+If <branch> is not found, and neither `-b` nor `-B` nor `--detach` are
+used, but there does exist a tracking branch in exactly one remote
+(call it <remote>) with a matching name, treat as equivalent to
+------------
+$ git worktree add --track -b <branch> <path> <remote>/<branch>
+------------
++
 If `<branch>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
builtin/worktree.c +16 -0
index 8f9446d43c..e9cc3f3872 100644
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "checkout.h"
 #include "config.h"
 #include "builtin.h"
 #include "dir.h"
@@ -390,6 +391,21 @@ static int add(int ac, const char **av, const char *prefix)
 		opts.new_branch = xstrndup(s, n);
 	}
 
+	if (ac == 2 && !opts.new_branch && !opts.detach) {
+		struct object_id oid;
+		struct commit *commit;
+		const char *remote;
+
+		commit = lookup_commit_reference_by_name(branch);
+		if (!commit) {
+			remote = unique_tracking_name(branch, &oid);
+			if (remote) {
+				opts.new_branch = branch;
+				branch = remote;
+			}
+		}
+	}
+
 	if (opts.new_branch) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		cp.git_cmd = 1;
t/t2025-worktree-add.sh +21 -0
index 53042ce565..ea144938a9 100755
@@ -369,4 +369,25 @@ test_expect_success '--no-track avoids setting up tracking' '
 	)
 '
 
+test_expect_success '"add" <path> <non-existent-branch> fails' '
+	test_must_fail git worktree add foo non-existent
+'
+
+test_expect_success '"add" <path> <branch> dwims' '
+	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 worktree add ../foo foo
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_upstream foo &&
+		git rev-parse repo_upstream/foo >expect &&
+		git rev-parse foo >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
Signature · 1 line
2.15.0.345.gf926f18f3
#8 lore raw

Re: [PATCH v4 3/4] worktree: make add <path> <branch> dwim

Quoted 9 lines · Thomas Gummerer

Currently 'git worktree add <path> <branch>', errors out when 'branch'
is not a local branch. It has no additional dwim'ing features that one
might expect.

Make it behave more like 'git checkout <branch>' when the branch doesn't exist locally, but a remote tracking branch uniquely matches the desired branch name, i.e. create a new branch from the remote tracking branch and set the upstream to the remote tracking branch.

Quoted 5 lines

As 'git worktree add' currently just dies in this situation, there are no backwards compatibility worries when introducing this feature.

---

This step makes sense and I did not spot anything controversial.

#9 lore raw

PATCH v4 4/4 worktree: make add <path> dwim

In reply to Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

Make 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name uniquely matches the branch name of a remote tracking branch, and if so check out that branch and set the upstream to the remote tracking branch.

This is a change of behaviour compared to the current behaviour, where we create a new branch matching HEAD. However as 'git worktree' is still an experimental feature, and it's easy to notice/correct the behaviour in case it's not what the user desired it's probably okay to break existing behaviour here.

In order to also satisfy users who want the current behaviour of creating a new branch from HEAD, add a '--no-track' flag, which disables the new behaviour, and keeps the old behaviour of creating a new branch from the head of the current worktree.

---
 Documentation/git-worktree.txt | 14 ++++++++++++--
 builtin/worktree.c             |  7 +++++++
 t/t2025-worktree-add.sh        | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 51 insertions(+), 2 deletions(-)
Documentation/git-worktree.txt +12 -2
index 45642d3b7f..abc8f1f50d 100644
@@ -60,8 +60,18 @@ $ git worktree add --track -b <branch> <path> <remote>/<branch>
 ------------
 +
 If `<branch>` is omitted and neither `-b` nor `-B` nor `--detach` used,
-then, as a convenience, a new branch based at HEAD is created automatically,
-as if `-b $(basename <path>)` was specified.
+then, as a convenience, if there exists a tracking branch in exactly
+one remote (call it `<remote>`) matching the basename of the path
+(call it `<branch>`), treat it as equivalent to
+------------
+$ git worktree add --track -b <branch> <path> <remote>/<branch>
+------------
+If no tracking branch exists in exactly one remote, `<branch>` is
+created based on HEAD, as if `-b $(basename <path>)` was specified.
++
+To disable the behaviour of trying to match the basename of <path> to
+a remote, and always create a new branch from HEAD, the `--no-track`
+flag can be passed to `git worktree add`.
 
 list::
 
builtin/worktree.c +7 -0
index e9cc3f3872..cbcceb0385 100644
@@ -389,6 +389,13 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
+		if (!opt_track || strcmp(opt_track, "--no-track")) {
+			struct object_id oid;
+			const char *remote =
+				unique_tracking_name(opts.new_branch, &oid);
+			if (remote)
+				branch = remote;
+		}
 	}
 
 	if (ac == 2 && !opts.new_branch && !opts.detach) {
t/t2025-worktree-add.sh +32 -0
index ea144938a9..6fd3da4036 100755
@@ -390,4 +390,36 @@ test_expect_success '"add" <path> <branch> dwims' '
 	)
 '
 
+test_expect_success 'git worktree add --no-track does not set up tracking' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git worktree add --no-track ../foo
+	) &&
+	(
+		cd foo &&
+		! test_branch_upstream foo repo_a foo &&
+		git rev-parse repo_a/foo >expect &&
+		git rev-parse foo >actual &&
+		! test_cmp expect actual
+	)
+'
+
+test_expect_success 'git worktree add sets up tracking' '
+	test_when_finished rm -rf repo_a repo_b &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git worktree add ../foo
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_a foo &&
+		git rev-parse repo_a/foo >expect &&
+		git rev-parse foo >actual &&
+		test_cmp expect actual
+	)
+'
+
 test_done
Signature · 1 line
2.15.0.345.gf926f18f3
#10 lore raw
Quoted 9 lines · Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

Make 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name uniquely matches the branch name of a remote tracking branch, and if so check out that branch and set the upstream to the remote tracking branch.

Quoted 5 lines

This is a change of behaviour compared to the current behaviour, where we create a new branch matching HEAD. However as 'git worktree' is still an experimental feature, and it's easy to notice/correct the behaviour in case it's not what the user desired it's probably okay to break existing behaviour here.

Is it "easy to notice"? I doubt it. Even if you assume that everybody uses bash prompt that shows the name of the branch, the user sees the same name of the branch in either mode.

Quoted 4 lines

In order to also satisfy users who want the current behaviour of creating a new branch from HEAD, add a '--no-track' flag, which disables the new behaviour, and keeps the old behaviour of creating a new branch from the head of the current worktree.

I am not sure if this is a good match for "--track/--no-track"; which branch is to be checked out (either "automatically from the unique remote-tracking branch" or "the current one") is one choice, and whether the resulting branch is marked explicitly as integrating with the remote or not is another choice within one branch of the first choice. IOW, this makes it impossible to say "create the branch based on the unique remote-tracking branch, but do not add the two branch.*.{merge,remote} variables".

Also, you have several mention of "remote tracking branch" in these patches. Please consistently spell them as "remote-tracking branch" to be consistent with Documentation/glossary-content.txt and avoid a casual/careful reference to "tracking branch" if possible, unless it is quite clear to the readers that you are being loose for the sake of brevity. Some people used "tracking branch" to mean the local branch that is marked as the branch to integrate with the work on a branch at a remote that caused user confusion in the past.

That is

refs/remotes/origin/topic is a remote-tracking branch for the branch 'topic' that came from the 'origin' remote.

when you have branch.foo.remote=origin and branch.foo.merge=refs/heads/topic, then your local branch foo is marked to integrate with the 'topic' branch at the 'origin' remote.

and these two are quite different things that people in the past and over time loosely used a phrase "tracking branch" to cause confusion.

#11 lore raw
Quoted 17 lines · Junio C Hamano
Quoted 9 lines · Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

Make 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name uniquely matches the branch name of a remote tracking branch, and if so check out that branch and set the upstream to the remote tracking branch.

Quoted 5 lines

This is a change of behaviour compared to the current behaviour, where we create a new branch matching HEAD. However as 'git worktree' is still an experimental feature, and it's easy to notice/correct the behaviour in case it's not what the user desired it's probably okay to break existing behaviour here.

Quoted 3 lines

Is it "easy to notice"? I doubt it. Even if you assume that everybody uses bash prompt that shows the name of the branch, the user sees the same name of the branch in either mode.

With "easy" I meant at creation time, looking at the output of 'git worktree add', which with the new version shows that the the new branch has been set up to track the remote branch, and also shows the commit HEAD now points to.

This would be the output in the new version:

     $ git worktree add ../bla
     Branch 'bla' set up to track remote branch 'bla' from 'origin'.
     Preparing ../bla (identifier bla)
     HEAD is now at 4aade43 bla

vs. the output without the changed behaviour:

     $ git worktree add ../bla
     Preparing ../bla (identifier bla)
     HEAD is now at 0f215c9 initial import

Of course that assumes that it's used directly, not in scripts, and that users will actually read the output of the command when they invoke it. Maybe these are not safe assumptions to make though, and we'd rather not have this on by default then. As I mentioned previously I would prefer having this as default, but I'm happy to hide this behaviour behind a flag if we want to be more careful about introducing this. Dunno?

Quoted 5 lines
Quoted 4 lines

In order to also satisfy users who want the current behaviour of creating a new branch from HEAD, add a '--no-track' flag, which disables the new behaviour, and keeps the old behaviour of creating a new branch from the head of the current worktree.

Quoted 8 lines

I am not sure if this is a good match for "--track/--no-track"; which branch is to be checked out (either "automatically from the unique remote-tracking branch" or "the current one") is one choice, and whether the resulting branch is marked explicitly as integrating with the remote or not is another choice within one branch of the first choice. IOW, this makes it impossible to say "create the branch based on the unique remote-tracking branch, but do not add the two branch.*.{merge,remote} variables".

Hmm good point. Maybe we'll need another flag for this. Maybe --[no-]guess-remote would work, and a corresponding worktree.guessRemote config would work? That's the best I could come up with, better suggestions are definitely welcome.

Quoted 8 lines

Also, you have several mention of "remote tracking branch" in these patches. Please consistently spell them as "remote-tracking branch" to be consistent with Documentation/glossary-content.txt and avoid a casual/careful reference to "tracking branch" if possible, unless it is quite clear to the readers that you are being loose for the sake of brevity. Some people used "tracking branch" to mean the local branch that is marked as the branch to integrate with the work on a branch at a remote that caused user confusion in the past.

I must admit I wasn't aware of Documentation/glossary-content.txt and have seen "tracking branch" in other places, so I was just repeating the pattern.

Quoted 5 lines

That is

refs/remotes/origin/topic is a remote-tracking branch for the branch 'topic' that came from the 'origin' remote.

Quoted 7 lines

when you have branch.foo.remote=origin and branch.foo.merge=refs/heads/topic, then your local branch foo is marked to integrate with the 'topic' branch at the 'origin' remote.

and these two are quite different things that people in the past and over time loosely used a phrase "tracking branch" to cause confusion.

Thanks for the clarification, will fix in the re-roll.

#12 lore raw
Quoted 13 lines · Thomas Gummerer

This would be the output in the new version:

     $ git worktree add ../bla
     Branch 'bla' set up to track remote branch 'bla' from 'origin'.
     Preparing ../bla (identifier bla)
     HEAD is now at 4aade43 bla

vs. the output without the changed behaviour:

     $ git worktree add ../bla
     Preparing ../bla (identifier bla)
     HEAD is now at 0f215c9 initial import
Quoted 7 lines

Of course that assumes that it's used directly, not in scripts, and that users will actually read the output of the command when they invoke it. Maybe these are not safe assumptions to make though, and we'd rather not have this on by default then. As I mentioned previously I would prefer having this as default, but I'm happy to hide this behaviour behind a flag if we want to be more careful about introducing this. Dunno?

Speaking as a simple user, I find the current behavior of Git worktree add very frustrating; I am constantly wanting to create worktrees for other peoples' branches so I can look at the code there without messing up my workspace, and it's really inconvenient to do that now.

Also, the current special handling of the directory name as a putative branch name is not helpful for me because many of the branches I need to examine use "/" as their separator. I don't begrudge making that feature more "DWIM" for those that can use it, but hopefully some help is forthcoming for those who can't.

For example, I need to create a local worktree for the remote rel/1.0 branch... what do I do?

What I want to work is this:

    git worktree add ../1.0 rel/1.0

and have it create a worktree at ../1.0, then do the equivalent of "git checkout rel/1.0" which includes setting up to track the remote branch.
But of course this doesn't work at all; I get:

fatal: invalid reference: rel/1.0

Personally I would think it odd to have to add an extra flag to get what I would expect would be "normal" behavior (checkout).

But maybe that's just me.

#13 lore raw
Quoted 44 lines · Paul Smith
Quoted 13 lines · Thomas Gummerer

This would be the output in the new version:

     $ git worktree add ../bla
     Branch 'bla' set up to track remote branch 'bla' from 'origin'.
     Preparing ../bla (identifier bla)
     HEAD is now at 4aade43 bla

vs. the output without the changed behaviour:

     $ git worktree add ../bla
     Preparing ../bla (identifier bla)
     HEAD is now at 0f215c9 initial import
Quoted 7 lines

Of course that assumes that it's used directly, not in scripts, and that users will actually read the output of the command when they invoke it. Maybe these are not safe assumptions to make though, and we'd rather not have this on by default then. As I mentioned previously I would prefer having this as default, but I'm happy to hide this behaviour behind a flag if we want to be more careful about introducing this. Dunno?

Speaking as a simple user, I find the current behavior of Git worktree add very frustrating; I am constantly wanting to create worktrees for other peoples' branches so I can look at the code there without messing up my workspace, and it's really inconvenient to do that now.

Also, the current special handling of the directory name as a putative branch name is not helpful for me because many of the branches I need to examine use "/" as their separator. I don't begrudge making that feature more "DWIM" for those that can use it, but hopefully some help is forthcoming for those who can't.

For example, I need to create a local worktree for the remote rel/1.0 branch... what do I do?

What I want to work is this:

    git worktree add ../1.0 rel/1.0

and have it create a worktree at ../1.0, then do the equivalent of "git checkout rel/1.0" which includes setting up to track the remote branch.
But of course this doesn't work at all; I get:

Quoted 6 lines

fatal: invalid reference: rel/1.0

Personally I would think it odd to have to add an extra flag to get what I would expect would be "normal" behavior (checkout).

But maybe that's just me.

This part is getting done in 3/4, and is definitely going to work without an additional flag, so this is (hopefully) soon going to work just as you want :)

This is less controversial because as you mentioned this currently doesn't work at all, so there are no backwards compatibility worries.

For the other case of

git worktree add ../foo

however we currently document one behaviour, which I would like to change (I usually have branches without a / in that I want to look at) we currently document one behaviour, which I'd like to change. So in that case we are a bit worried about backwards compatibility, and how this will affect current users that have a certain expectation of how the command is supposed to work, hence the discussion of whether to hide the new behaviour behind a flag or not.

Either way, if we do put the behaviour behind a flag, I'll also add a configuration variable, which can be set to enable the new behaviour so one doesn't have to type out the flag all the time.

#14 lore raw

On November 25, 2017 3:06 PM Thomas Gummerer wrote:
<big snip>

Quoted 1 line

however we currently document one behaviour, which I would like to change

(I usually have branches

Quoted 1 line

without a / in that I want to look at) we currently document one behaviour,

which I'd like to change. So

Quoted 1 line

in that case we are a bit worried about backwards compatibility, and how

this will affect current users

Quoted 1 line

that have a certain expectation of how the command is supposed to work,

hence the discussion of

Quoted 3 lines

whether to hide the new behaviour behind a flag or not.

Either way, if we do put the behaviour behind a flag, I'll also add a

configuration variable, which can

Quoted 1 line

be set to enable the new behaviour so one doesn't have to type out the flag

all the time.

To be consistent with other commands, you could put path after -- and the ambiguity with refs containing '/' goes away, as refs before the -- would always be considered refs while after you have paths.

What I don't like is the current add syntax of:
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]

where the path-spec precedes branch making things a bit icky. It might be better to have an alternate syntax of:

git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] [<branch>] -- <path>

But since we only have one path, that may be redundant. Just a thought, as -- avoids a lot of interpretation evils. While we're here, I wonder whether <branch> should be changed to <ref-spec> for more general use. Consider release identifiers using tags, and using the tag instead of branch to define commit on which the worktree is based.

Cheers,
Randall

-- Brief whoami: NonStop&UNIX developer since approximately UNIX(421664400)/NonStop(211288444200000000)
-- In my real life, I talk too much.

#15 lore raw
Quoted 25 lines · Randall S. Becker

On November 25, 2017 3:06 PM Thomas Gummerer wrote:
<big snip>

Quoted 10 lines

however we currently document one behaviour, which I would like to change (I usually have branches
without a / in that I want to look at) we currently document one behaviour, which I'd like to change. So
in that case we are a bit worried about backwards compatibility, and how this will affect current users
that have a certain expectation of how the command is supposed to work, hence the discussion of
whether to hide the new behaviour behind a flag or not.

Quoted 4 lines

Either way, if we do put the behaviour behind a flag, I'll also add a configuration variable, which can
be set to enable the new behaviour so one doesn't have to type out the flag all the time.

To be consistent with other commands, you could put path after -- and the ambiguity with refs containing '/' goes away, as refs before the -- would always be considered refs while after you have paths.

What I don't like is the current add syntax of:
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]

Quoted 7 lines

where the path-spec precedes branch making things a bit icky. It might be better to have an alternate syntax of:

git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] [<branch>] -- <path>

Hmm I don't think there is any ambiguity there, the first argument always needs to be a path, and the second one is always a commit-ish. So this way there is no disambiguation needed.

I'm not convinced the alternative syntax would buy us much, at least not in the context of what this series is trying to do.

Quoted 5 lines

But since we only have one path, that may be redundant. Just a thought, as -- avoids a lot of interpretation evils. While we're here, I wonder whether <branch> should be changed to <ref-spec> for more general use. Consider release identifiers using tags, and using the tag instead of branch to define commit on which the worktree is based.

'git worktree add' can already take a commit-ish, it's just not documented that way. I'll add a patch updating the documentation to the series.

Quoted 11 lines

Cheers,
Randall

-- Brief whoami: NonStop&UNIX developer since approximately UNIX(421664400)/NonStop(211288444200000000)
-- In my real life, I talk too much.

#16 lore raw
In reply to Thomas Gummerer
Quoted 3 lines · Thomas Gummerer

This part is getting done in 3/4, and is definitely going to work without an additional flag, so this is (hopefully) soon going to work just as you want :)

Yay! Thanks!

#17 lore raw
In reply to Thomas Gummerer
Quoted 4 lines · Thomas Gummerer

Of course that assumes that it's used directly, not in scripts, and that users will actually read the output of the command when they invoke it. Maybe these are not safe assumptions to make though, and we'd rather not have this on by default then.

None of these is a safe assumption to make. With a transition plan, we could change the default eventually, but if this feature is already used by real users, the proposed change without any transition plan will hurt them and make them hate us, I am afraid.

#18 lore raw
Quoted 11 lines · Junio C Hamano
Quoted 4 lines · Thomas Gummerer

Of course that assumes that it's used directly, not in scripts, and that users will actually read the output of the command when they invoke it. Maybe these are not safe assumptions to make though, and we'd rather not have this on by default then.

None of these is a safe assumption to make. With a transition plan, we could change the default eventually, but if this feature is already used by real users, the proposed change without any transition plan will hurt them and make them hate us, I am afraid.

Fair enough, I definitely wouldn't want users to hate us. I guess I'll add this to the list of things that I'd like to change when git 3.0 comes along.

How about once we make the decision when git 3.0 happens, adding a warning:

warning: The current behaviour of creating a branch from HEAD when no <branch> is given will change in git 3.0, to try and match the new branch name to a remote-tracking branch. To get rid of this warning in the meantime, set git.guessRemote to true or false, depending on whether you'd like to enable the new behaviour now, or would like to keep the existing behaviour. (see also --guess-remote in man git-worktree)

or something along the lines, and once we decide to release git 3.0 we flip the default? The warning would be displayed whenever the new behaviour would be invoked, but no config option or flag is set.

Either way I'm going to add a flag and a config option to hide this behaviour behind for now.

Thanks!

#19 lore raw

v5 cover make git worktree add dwim more

In reply to Thomas Gummerer

The previous rounds can be found at
https://public-inbox.org/git/[email protected]/,
https://public-inbox.org/git/[email protected]/,
https://public-inbox.org/git/[email protected]/ and
https://public-inbox.org/git/[email protected]/.

Thanks Junio for the review of the previous round and Randall for the suggestion of documenting that git worktree add can take a commit-ish, not just a branch.

The main changes in this round are hiding the new behaviour for 'git worktree <path>' behind a flag, and adding a config option to turn the new behaviour on by default. It's also no longer relying on the --[no]-track flag, but using a new --[no-]guess-remote flag instead.

Interdiff between this and the previous round below:

Documentation/config.txt +10 -0
index 5f65fa9234..4966d90ebb 100644
@@ -3425,3 +3425,13 @@ web.browser::
 	Specify a web browser that may be used by some commands.
 	Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
 	may use it.
+
+worktree.guessRemote::
+	With `add`, if no branch argument, and neither of `-b` nor
+	`-B` nor `--detach` are given, the command defaults to
+	creating a new branch from HEAD.  If `worktree.guessRemote` is
+	set to true, `worktree add` tries to find a remote-tracking
+	branch whose name uniquely matches the new branch name.  If
+	such a branch exists, it is checked out and set as "upstream"
+	for the new branch.  If no such match can be found, it falls
+	back to creating a new branch from the current HEAD.
Documentation/git-worktree.txt +30 -29

104 lines not on this page · raw message

builtin/worktree.c +16 -1
index 83c73ecb0d..426aea8761 100644
@@ -33,8 +33,19 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
+static int guess_remote;
 static timestamp_t expire;
 
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "worktree.guessremote")) {
+		guess_remote = git_config_bool(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
 	struct stat st;
@@ -355,9 +366,13 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_PASSTHRU(0, "track", &opt_track, NULL,
 			     N_("set up tracking mode (see git-branch(1))"),
 			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
+		OPT_BOOL(0, "guess-remote", &guess_remote,
+			 N_("try to match the new branch name with a remote-tracking branch")),
 		OPT_END()
 	};
 
+	git_config(git_worktree_config, NULL);
+
 	memset(&opts, 0, sizeof(opts));
 	opts.checkout = 1;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
@@ -389,7 +404,7 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
-		if (!opt_track || strcmp(opt_track, "--no-track")) {
+		if (guess_remote) {
 			struct object_id oid;
 			const char *remote =
 				unique_tracking_name(opts.new_branch, &oid);
checkout.c +1 -0
index b0c744d37a..ac42630f74 100644
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "remote.h"
+#include "checkout.h"
 
 struct tracking_name_data {
 	/* const */ char *src_ref;
t/t2025-worktree-add.sh +44 -22

105 lines not on this page · raw message

Thomas Gummerer (6):
checkout: factor out functions to new lib file
worktree: add can be created from any commit-ish
worktree: add --[no-]track option to the add subcommand
worktree: make add <path> <branch> dwim
worktree: add --guess-remote flag to add subcommand add worktree.guessRemote config option

 Documentation/config.txt       |  10 ++++
 Documentation/git-worktree.txt |  44 ++++++++++----
 Makefile                       |   1 +
 builtin/checkout.c             |  41 +------------
 builtin/worktree.c             |  46 +++++++++++++++
 checkout.c                     |  43 ++++++++++++++
 checkout.h                     |  13 +++++
 t/t2025-worktree-add.sh        | 130 +++++++++++++++++++++++++++++++++++++++++
 8 files changed, 278 insertions(+), 50 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Signature · 1 line
2.15.0.426.gb06021eeb
#20 lore raw

PATCH v5 1/6 checkout: factor out functions to new lib file

Factor the functions out, so they can be re-used from other places. In particular these functions will be re-used in builtin/worktree.c to make git worktree add dwim more.

While there add some docs to the function.

---
 Makefile           |  1 +
 builtin/checkout.c | 41 +----------------------------------------
 checkout.c         | 43 +++++++++++++++++++++++++++++++++++++++++++
 checkout.h         | 13 +++++++++++++
 4 files changed, 58 insertions(+), 40 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Makefile +1 -0
index e53750ca01..a80a8fcca9 100644
@@ -759,6 +759,7 @@ LIB_OBJS += branch.o
 LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
+LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
builtin/checkout.c +1 -40

52 lines not on this page · raw message

checkout.c new file +43 -0

43 lines not on this page · raw message

checkout.h new file +13 -0
new file mode 100644
index 0000000000..9980711179
@@ -0,0 +1,13 @@
+#ifndef CHECKOUT_H
+#define CHECKOUT_H
+
+#include "cache.h"
+
+/*
+ * Check if the branch name uniquely matches a branch name on a remote
+ * tracking branch.  Return the name of the remote if such a branch
+ * exists, NULL otherwise.
+ */
+extern const char *unique_tracking_name(const char *name, struct object_id *oid);
+
+#endif /* CHECKOUT_H */
Signature · 1 line
2.15.0.426.gb06021eeb
#21 lore raw

PATCH v5 2/6 worktree: add can be created from any commit-ish

In reply to Thomas Gummerer

Currently 'git worktree add' is documented to take an optional <branch> argument, which is checked out in the new worktree. However it is more generally possible to use a commit-ish as the optional argument, and check that out into the new worktree.

Document that this is a possibility, as new users of git worktree add might find it helpful.

---
 Documentation/git-worktree.txt | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
Documentation/git-worktree.txt +10 -10
index b472acc356..121895209d 100644
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
 SYNOPSIS
 --------
 [verse]
-'git worktree add' [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]
+'git worktree add' [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
@@ -45,14 +45,14 @@ specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
-add <path> [<branch>]::
+add <path> [<commit-ish>]::
 
-Create `<path>` and checkout `<branch>` into it. The new working directory
+Create `<path>` and checkout `<commit-ish>` into it. The new working directory
 is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
-specified as `<branch>`; it is synonymous with `@{-1}`.
+specified as `<commit-ish>`; it is synonymous with `@{-1}`.
 +
-If `<branch>` is omitted and neither `-b` nor `-B` nor `--detach` used,
+If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
 
@@ -84,25 +84,25 @@ OPTIONS
 
 -f::
 --force::
-	By default, `add` refuses to create a new working tree when `<branch>`
+	By default, `add` refuses to create a new working tree when `<commit-ish>` is a branch name and
 	is already checked out by another working tree. This option overrides
 	that safeguard.
 
 -b <new-branch>::
 -B <new-branch>::
 	With `add`, create a new branch named `<new-branch>` starting at
-	`<branch>`, and check out `<new-branch>` into the new working tree.
-	If `<branch>` is omitted, it defaults to HEAD.
+	`<commit-ish>`, and check out `<new-branch>` into the new working tree.
+	If `<commit-ish>` is omitted, it defaults to HEAD.
 	By default, `-b` refuses to create a new branch if it already
 	exists. `-B` overrides this safeguard, resetting `<new-branch>` to
-	`<branch>`.
+	`<commit-ish>`.
 
 --detach::
 	With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
 	in linkgit:git-checkout[1].
 
 --[no-]checkout::
-	By default, `add` checks out `<branch>`, however, `--no-checkout` can
+	By default, `add` checks out `<commit-ish>`, however, `--no-checkout` can
 	be used to suppress checkout in order to make customizations,
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
Signature · 1 line
2.15.0.426.gb06021eeb
#22 lore raw

PATCH v5 3/6 worktree: add --[no-]track option to the add subcommand

In reply to Thomas Gummerer

Currently 'git worktree add' sets up tracking branches if '<branch>' is a remote tracking branch, and doesn't set them up otherwise, as is the default for 'git branch'.

This may or may not be what the user wants. Allow overriding this behaviour with a --[no-]track flag that gets passed through to 'git branch'.

We already respect branch.autoSetupMerge, as 'git worktree' just calls 'git branch' internally.

---
 Documentation/git-worktree.txt |  6 +++++
 builtin/worktree.c             |  8 +++++++
 t/t2025-worktree-add.sh        | 51 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+)
Documentation/git-worktree.txt +6 -0
index 121895209d..15e58b18f7 100644
@@ -107,6 +107,12 @@ OPTIONS
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
 
+--[no-]track::
+	When creating a new branch, if `<commit-ish>` is a branch,
+	mark it as "upstream" from the new branch.  This is the
+	default if `<commit-ish>` is a remote-tracking branch.  See
+	"--track" in linkgit:git-branch[1] for details.
+
 --lock::
 	Keep the working tree locked after creation. This is the
 	equivalent of `git worktree lock` after `git worktree add`,
builtin/worktree.c +8 -0
index ed043d5f1c..ea9678cac8 100644
@@ -341,6 +341,7 @@ static int add(int ac, const char **av, const char *prefix)
 	const char *new_branch_force = NULL;
 	char *path;
 	const char *branch;
+	const char *opt_track = NULL;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -350,6 +351,9 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
 		OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 		OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+		OPT_PASSTHRU(0, "track", &opt_track, NULL,
+			     N_("set up tracking mode (see git-branch(1))"),
+			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
 		OPT_END()
 	};
 
@@ -394,9 +398,13 @@ static int add(int ac, const char **av, const char *prefix)
 			argv_array_push(&cp.args, "--force");
 		argv_array_push(&cp.args, opts.new_branch);
 		argv_array_push(&cp.args, branch);
+		if (opt_track)
+			argv_array_push(&cp.args, opt_track);
 		if (run_command(&cp))
 			return -1;
 		branch = opts.new_branch;
+	} else if (opt_track) {
+		die(_("--[no-]track can only be used if a new branch is created"));
 	}
 
 	UNLEAK(path);
t/t2025-worktree-add.sh +51 -0

56 lines not on this page · raw message

Signature · 1 line
2.15.0.426.gb06021eeb
#23 lore raw

PATCH v5 4/6 worktree: make add <path> <branch> dwim

In reply to Thomas Gummerer

Currently 'git worktree add <path> <branch>', errors out when 'branch' is not a local branch. It has no additional dwim'ing features that one might expect.

Make it behave more like 'git checkout <branch>' when the branch doesn't exist locally, but a remote tracking branch uniquely matches the desired branch name, i.e. create a new branch from the remote tracking branch and set the upstream to the remote tracking branch.

As 'git worktree add' currently just dies in this situation, there are no backwards compatibility worries when introducing this feature.

---
 Documentation/git-worktree.txt |  8 ++++++++
 builtin/worktree.c             | 16 ++++++++++++++++
 t/t2025-worktree-add.sh        | 19 +++++++++++++++++++
 3 files changed, 43 insertions(+)
Documentation/git-worktree.txt +8 -0
index 15e58b18f7..3044d305a6 100644
@@ -52,6 +52,14 @@ is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
 specified as `<commit-ish>`; it is synonymous with `@{-1}`.
 +
+If <commit-ish> is a branch name (call it `<branch>` and is not found,
+and neither `-b` nor `-B` nor `--detach` are used, but there does
+exist a tracking branch in exactly one remote (call it `<remote>`)
+with a matching name, treat as equivalent to
+------------
+$ git worktree add --track -b <branch> <path> <remote>/<branch>
+------------
++
 If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
builtin/worktree.c +16 -0
index ea9678cac8..7021d02585 100644
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "checkout.h"
 #include "config.h"
 #include "builtin.h"
 #include "dir.h"
@@ -390,6 +391,21 @@ static int add(int ac, const char **av, const char *prefix)
 		opts.new_branch = xstrndup(s, n);
 	}
 
+	if (ac == 2 && !opts.new_branch && !opts.detach) {
+		struct object_id oid;
+		struct commit *commit;
+		const char *remote;
+
+		commit = lookup_commit_reference_by_name(branch);
+		if (!commit) {
+			remote = unique_tracking_name(branch, &oid);
+			if (remote) {
+				opts.new_branch = branch;
+				branch = remote;
+			}
+		}
+	}
+
 	if (opts.new_branch) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		cp.git_cmd = 1;
t/t2025-worktree-add.sh +19 -0
index 72e8b62927..96ebc63d04 100755
@@ -365,4 +365,23 @@ test_expect_success '--no-track avoids setting up tracking' '
 	)
 '
 
+test_expect_success '"add" <path> <non-existent-branch> fails' '
+	test_must_fail git worktree add foo non-existent
+'
+
+test_expect_success '"add" <path> <branch> dwims' '
+	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 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_done
Signature · 1 line
2.15.0.426.gb06021eeb
#24 lore raw

PATCH v5 5/6 worktree: add --guess-remote flag to add subcommand

In reply to Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

It's sometimes useful to have 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name uniquely matches the branch name of a remote-tracking branch, and if so check out that branch and set the upstream to the remote-tracking branch.

Add a new --guess-remote option that enables exactly that behaviour.

---
 Documentation/git-worktree.txt |  7 +++++++
 builtin/worktree.c             | 10 ++++++++++
 t/t2025-worktree-add.sh        | 29 +++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+)
Documentation/git-worktree.txt +7 -0
index 3044d305a6..a81cfb2229 100644
@@ -115,6 +115,13 @@ OPTIONS
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
 
+--[no-]guess-remote::
+	With `add`, instead of creating a new branch from HEAD when
+	`<commit-ish>` is not given, if there exists a tracking branch
+	in exactly one remote matching the basename of the path, base
+	the new branch on the remote-tracking branch, and mark the
+	remote-tracking branch as "upstream" from the new branch.
+
 --[no-]track::
 	When creating a new branch, if `<commit-ish>` is a branch,
 	mark it as "upstream" from the new branch.  This is the
builtin/worktree.c +10 -0
index 7021d02585..15cb1600ee 100644
@@ -343,6 +343,7 @@ static int add(int ac, const char **av, const char *prefix)
 	char *path;
 	const char *branch;
 	const char *opt_track = NULL;
+	int guess_remote = 0;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -355,6 +356,8 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_PASSTHRU(0, "track", &opt_track, NULL,
 			     N_("set up tracking mode (see git-branch(1))"),
 			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
+		OPT_BOOL(0, "guess-remote", &guess_remote,
+			 N_("try to match the new branch name with a remote-tracking branch")),
 		OPT_END()
 	};
 
@@ -389,6 +392,13 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
+		if (guess_remote) {
+			struct object_id oid;
+			const char *remote =
+				unique_tracking_name(opts.new_branch, &oid);
+			if (remote)
+				branch = remote;
+		}
 	}
 
 	if (ac == 2 && !opts.new_branch && !opts.detach) {
t/t2025-worktree-add.sh +29 -0
index 96ebc63d04..d25c774cb7 100755
@@ -384,4 +384,33 @@ test_expect_success '"add" <path> <branch> dwims' '
 	)
 '
 
+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 &&
+	(
+		cd repo_b &&
+		git worktree add ../foo
+	) &&
+	(
+		cd foo &&
+		test_must_fail git config "branch.foo.remote" &&
+		test_must_fail git config "branch.foo.merge" &&
+		! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
+test_expect_success 'git worktree add --guess-remote sets up tracking' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git worktree add --guess-remote ../foo
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_a foo &&
+		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
 test_done
Signature · 1 line
2.15.0.426.gb06021eeb
#25 lore raw

Re: [PATCH v5 5/6] worktree: add --guess-remote flag to add subcommand

Quoted 9 lines · Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

It's sometimes useful to have 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name uniquely matches the branch name of a remote-tracking branch, and if so check out that branch and set the upstream to the remote-tracking branch.

This paragraph was a bit hard to sympathize because it was not obvious that the new feature still assumes how <path> is used to compute the name of the new branch. Perhaps if it were written like so:

check if the new branch name, derived from the basename of the <path>, uniquely matches the branch name of ...

I would not have had to read it twice to understand what was going on.

Documentation/git-worktree.txt OPTIONS · line 124

Quoted 7 lines

+--[no-]guess-remote::

Documentation/git-worktree.txt +6 -0
+	With `add`, instead of creating a new branch from HEAD when
+	`<commit-ish>` is not given, if there exists a tracking branch
+	in exactly one remote matching the basename of the path, base
+	the new branch on the remote-tracking branch, and mark the
+	remote-tracking branch as "upstream" from the new branch.
+

Would

	git worktree add --guess-remote <path> <branch>

be an error? It is allowed as long as <branch> and the basename of the <path> matches? The option is silently ignored? Something else?

I am reacting to "with add" part of this desciption. I wouldn't be asking if it said "With worktree add <path> without <branch>", as that would make the scenario I am wondering about automatically "undefined". Yes, we should strive for leaving things undefined as little as practically possible, but at least saying something like "without <branch>" explicitly there would make sure that readers know in what scenario this option is meant to be used a bit better.

builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 402

Quoted 12 lines
builtin/worktree.c +7 -0
@@ -389,6 +392,13 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
+		if (guess_remote) {
+			struct object_id oid;
+			const char *remote =
+				unique_tracking_name(opts.new_branch, &oid);
+			if (remote)
+				branch = remote;
+		}
 	}

I think the answer is "silently ignored", as the above hunk is inside "if (ac < 2 && !opts.new_branch && !opts.detach)".

#26 lore raw

Re: [PATCH v5 5/6] worktree: add --guess-remote flag to add subcommand

Quoted 17 lines · Junio C Hamano
Quoted 9 lines · Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

It's sometimes useful to have 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name uniquely matches the branch name of a remote-tracking branch, and if so check out that branch and set the upstream to the remote-tracking branch.

This paragraph was a bit hard to sympathize because it was not obvious that the new feature still assumes how <path> is used to compute the name of the new branch. Perhaps if it were written like so:

Quoted 5 lines

check if the new branch name, derived from the basename of the <path>, uniquely matches the branch name of ...

I would not have had to read it twice to understand what was going on.

Sorry about that, will re-phrase.

Quoted 16 lines

Documentation/git-worktree.txt OPTIONS · line 124

Quoted 7 lines

+--[no-]guess-remote::

Documentation/git-worktree.txt +6 -0
+	With `add`, instead of creating a new branch from HEAD when
+	`<commit-ish>` is not given, if there exists a tracking branch
+	in exactly one remote matching the basename of the path, base
+	the new branch on the remote-tracking branch, and mark the
+	remote-tracking branch as "upstream" from the new branch.
+

Would

	git worktree add --guess-remote <path> <branch>

be an error? It is allowed as long as <branch> and the basename of the <path> matches? The option is silently ignored? Something else?

Quoted 7 lines

I am reacting to "with add" part of this desciption. I wouldn't be asking if it said "With worktree add <path> without <branch>", as that would make the scenario I am wondering about automatically "undefined". Yes, we should strive for leaving things undefined as little as practically possible, but at least saying something like "without <branch>" explicitly there would make sure that readers know in what scenario this option is meant to be used a bit better.

As you mentioned below it's silently ignored. The main reason for not erroring out is that it would get a little bit (although not too much) more annoying once the config variable is introduced. If it's strongly preferred to error out when <branch> is given I can change it to that.

Either way I'll update the documentation.

Thanks!

Quoted 16 lines

builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 402

Quoted 12 lines
builtin/worktree.c +7 -0
@@ -389,6 +392,13 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
+		if (guess_remote) {
+			struct object_id oid;
+			const char *remote =
+				unique_tracking_name(opts.new_branch, &oid);
+			if (remote)
+				branch = remote;
+		}
 	}

I think the answer is "silently ignored", as the above hunk is inside "if (ac < 2 && !opts.new_branch && !opts.detach)".

#27 lore raw

PATCH v5 6/6 add worktree.guessRemote config option

In reply to Thomas Gummerer

Some users might want to have the --guess-remote option introduced in the previous commit on by default, so they don't have to type it out every time they create a new worktree.

Add a config option worktree.guessRemote that allows users to configure the default behaviour for themselves.

---
 Documentation/config.txt       | 10 ++++++++++
 Documentation/git-worktree.txt |  3 +++
 builtin/worktree.c             | 14 +++++++++++++-
 t/t2025-worktree-add.sh        | 31 +++++++++++++++++++++++++++++++
 4 files changed, 57 insertions(+), 1 deletion(-)
Documentation/config.txt +10 -0
index 5f65fa9234..4966d90ebb 100644
@@ -3425,3 +3425,13 @@ web.browser::
 	Specify a web browser that may be used by some commands.
 	Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
 	may use it.
+
+worktree.guessRemote::
+	With `add`, if no branch argument, and neither of `-b` nor
+	`-B` nor `--detach` are given, the command defaults to
+	creating a new branch from HEAD.  If `worktree.guessRemote` is
+	set to true, `worktree add` tries to find a remote-tracking
+	branch whose name uniquely matches the new branch name.  If
+	such a branch exists, it is checked out and set as "upstream"
+	for the new branch.  If no such match can be found, it falls
+	back to creating a new branch from the current HEAD.
Documentation/git-worktree.txt +3 -0
index a81cfb2229..fd841886ef 100644
@@ -121,6 +121,9 @@ OPTIONS
 	in exactly one remote matching the basename of the path, base
 	the new branch on the remote-tracking branch, and mark the
 	remote-tracking branch as "upstream" from the new branch.
++
+This can also be set up as the default behaviour by using the
+`worktree.guessRemote` config option.
 
 --[no-]track::
 	When creating a new branch, if `<commit-ish>` is a branch,
builtin/worktree.c +13 -1
index 15cb1600ee..426aea8761 100644
@@ -33,8 +33,19 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
+static int guess_remote;
 static timestamp_t expire;
 
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "worktree.guessremote")) {
+		guess_remote = git_config_bool(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
 	struct stat st;
@@ -343,7 +354,6 @@ static int add(int ac, const char **av, const char *prefix)
 	char *path;
 	const char *branch;
 	const char *opt_track = NULL;
-	int guess_remote = 0;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -361,6 +371,8 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
+	git_config(git_worktree_config, NULL);
+
 	memset(&opts, 0, sizeof(opts));
 	opts.checkout = 1;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
t/t2025-worktree-add.sh +31 -0
index d25c774cb7..6ce9b9c070 100755
@@ -413,4 +413,35 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '
 	)
 '
 
+test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git config worktree.guessRemote true &&
+		git worktree add ../foo
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_a foo &&
+		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
+test_expect_success 'git worktree --no-guess-remote option overrides config' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git config worktree.guessRemote true &&
+		git worktree add --no-guess-remote ../foo
+	) &&
+	(
+		cd foo &&
+		test_must_fail git config "branch.foo.remote" &&
+		test_must_fail git config "branch.foo.merge" &&
+		! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
 test_done
Signature · 1 line
2.15.0.426.gb06021eeb
#28 lore raw

Re: [PATCH v5 6/6] add worktree.guessRemote config option

Documentation/config.txt web.browser:: · line 3437

Quoted 9 lines · Thomas Gummerer
Documentation/config.txt +9 -0
+worktree.guessRemote::
+	With `add`, if no branch argument, and neither of `-b` nor
+	`-B` nor `--detach` are given, the command defaults to
+	creating a new branch from HEAD.  If `worktree.guessRemote` is
+	set to true, `worktree add` tries to find a remote-tracking
+	branch whose name uniquely matches the new branch name.  If
+	such a branch exists, it is checked out and set as "upstream"
+	for the new branch.  If no such match can be found, it falls
+	back to creating a new branch from the current HEAD.

Unlike the part I commented on in the previous step, this one is clear that the feature only kicks in for 'add <path>' without anything else, which is good.

Quoted 11 lines
builtin/worktree.c +1 -0
index 15cb1600ee..426aea8761 100644
@@ -33,8 +33,19 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
+static int guess_remote;
 static timestamp_t expire;
 

builtin/worktree.c struct add_opts { · line 48

Quoted 10 lines
builtin/worktree.c +10 -0
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "worktree.guessremote")) {
+		guess_remote = git_config_bool(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+

It is a lot more consistent with the established practice if this function had

	return git_default_config(var, value, cb);

instead of "return 0" at the end, and then have the call to

	git_config(git_default_config, NULL);

we have in cmd_worktree() replaced with

	git_config(git_worktree_config, NULL);

That would avoid having to scan the entire set of config keys once in cmd_worktree() and then again in add(), the latter one only looking for a single variable.

#29 lore raw

Re: [PATCH v5 6/6] add worktree.guessRemote config option

Quoted 47 lines · Junio C Hamano

Documentation/config.txt web.browser:: · line 3437

Quoted 9 lines · Thomas Gummerer
Documentation/config.txt +9 -0
+worktree.guessRemote::
+	With `add`, if no branch argument, and neither of `-b` nor
+	`-B` nor `--detach` are given, the command defaults to
+	creating a new branch from HEAD.  If `worktree.guessRemote` is
+	set to true, `worktree add` tries to find a remote-tracking
+	branch whose name uniquely matches the new branch name.  If
+	such a branch exists, it is checked out and set as "upstream"
+	for the new branch.  If no such match can be found, it falls
+	back to creating a new branch from the current HEAD.

Unlike the part I commented on in the previous step, this one is clear that the feature only kicks in for 'add <path>' without anything else, which is good.

Quoted 11 lines
builtin/worktree.c +1 -0
index 15cb1600ee..426aea8761 100644
@@ -33,8 +33,19 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
+static int guess_remote;
 static timestamp_t expire;
 

builtin/worktree.c struct add_opts { · line 48

Quoted 10 lines
builtin/worktree.c +10 -0
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "worktree.guessremote")) {
+		guess_remote = git_config_bool(var, value);
+		return 0;
+	}
+
+	return 0;
+}
+

It is a lot more consistent with the established practice if this function had

	return git_default_config(var, value, cb);

instead of "return 0" at the end, and then have the call to

	git_config(git_default_config, NULL);
Quoted 7 lines

we have in cmd_worktree() replaced with

	git_config(git_worktree_config, NULL);

That would avoid having to scan the entire set of config keys once in cmd_worktree() and then again in add(), the latter one only looking for a single variable.

Makes sense, I missed that. I'll fix it in the re-roll. I'll wait a few days to see if there are any more comments on the series and then re-roll it with the suggested changes.

Thanks for the review!

#30 lore raw

v6 cover make git worktree add dwim more

In reply to Thomas Gummerer

The previous rounds were at
https://public-inbox.org/git/[email protected]/,
https://public-inbox.org/git/[email protected]/,
https://public-inbox.org/git/[email protected]/,
https://public-inbox.org/git/[email protected]/ and
https://public-inbox.org/git/[email protected].

Thanks Junio for the review of the last round!

Changes since the last round:

- rephrased documentation and commit messaegs a bit use the
- established pattern and call git_config only once, instead of calling it multiple times.

Interdiff below:

Documentation/git-worktree.txt +5 -5
index fd841886ef..89ad0faecf 100644
@@ -116,11 +116,11 @@ OPTIONS
 	in linkgit:git-read-tree[1].
 
 --[no-]guess-remote::
-	With `add`, instead of creating a new branch from HEAD when
-	`<commit-ish>` is not given, if there exists a tracking branch
-	in exactly one remote matching the basename of the path, base
-	the new branch on the remote-tracking branch, and mark the
-	remote-tracking branch as "upstream" from the new branch.
+	With `worktree add <path>`, withouth `<commit-ish>`, instead
+	of creating a new branch from HEAD, if there exists a tracking
+	branch in exactly one remote matching the basename of `<path>,
+	base the new branch on the remote-tracking branch, and mark
+	the remote-tracking branch as "upstream" from the new branch.
 +
 This can also be set up as the default behaviour by using the
 `worktree.guessRemote` config option.
builtin/worktree.c +2 -4
index 426aea8761..002a569a11 100644
@@ -43,7 +43,7 @@ static int git_worktree_config(const char *var, const char *value, void *cb)
 		return 0;
 	}
 
-	return 0;
+	return git_default_config(var, value, cb);
 }
 
 static int prune_worktree(const char *id, struct strbuf *reason)
@@ -371,8 +371,6 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_worktree_config, NULL);
-
 	memset(&opts, 0, sizeof(opts));
 	opts.checkout = 1;
 	ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
@@ -603,7 +601,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_worktree_config, NULL);
 
 	if (ac < 2)
 		usage_with_options(worktree_usage, options);

Thomas Gummerer (6):
checkout: factor out functions to new lib file
worktree: add can be created from any commit-ish
worktree: add --[no-]track option to the add subcommand
worktree: make add <path> <branch> dwim
worktree: add --guess-remote flag to add subcommand add worktree.guessRemote config option

 Documentation/config.txt       |  10 ++++
 Documentation/git-worktree.txt |  44 ++++++++++----
 Makefile                       |   1 +
 builtin/checkout.c             |  41 +------------
 builtin/worktree.c             |  46 ++++++++++++++-
 checkout.c                     |  43 ++++++++++++++
 checkout.h                     |  13 +++++
 t/t2025-worktree-add.sh        | 130 +++++++++++++++++++++++++++++++++++++++++
 8 files changed, 277 insertions(+), 51 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Signature · 1 line
2.15.0.426.gb06021eeb
#31 lore raw

PATCH v6 1/6 checkout: factor out functions to new lib file

Factor the functions out, so they can be re-used from other places. In particular these functions will be re-used in builtin/worktree.c to make git worktree add dwim more.

While there add some docs to the function.

---
 Makefile           |  1 +
 builtin/checkout.c | 41 +----------------------------------------
 checkout.c         | 43 +++++++++++++++++++++++++++++++++++++++++++
 checkout.h         | 13 +++++++++++++
 4 files changed, 58 insertions(+), 40 deletions(-)
 create mode 100644 checkout.c
 create mode 100644 checkout.h
Makefile +1 -0
index e53750ca01..a80a8fcca9 100644
@@ -759,6 +759,7 @@ LIB_OBJS += branch.o
 LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
+LIB_OBJS += checkout.o
 LIB_OBJS += color.o
 LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
builtin/checkout.c +1 -40

52 lines not on this page · raw message

checkout.c new file +43 -0

43 lines not on this page · raw message

checkout.h new file +13 -0
new file mode 100644
index 0000000000..9980711179
@@ -0,0 +1,13 @@
+#ifndef CHECKOUT_H
+#define CHECKOUT_H
+
+#include "cache.h"
+
+/*
+ * Check if the branch name uniquely matches a branch name on a remote
+ * tracking branch.  Return the name of the remote if such a branch
+ * exists, NULL otherwise.
+ */
+extern const char *unique_tracking_name(const char *name, struct object_id *oid);
+
+#endif /* CHECKOUT_H */
Signature · 1 line
2.15.0.426.gb06021eeb
#32 lore raw

PATCH v6 2/6 worktree: add can be created from any commit-ish

In reply to Thomas Gummerer

Currently 'git worktree add' is documented to take an optional <branch> argument, which is checked out in the new worktree. However it is more generally possible to use a commit-ish as the optional argument, and check that out into the new worktree.

Document that this is a possibility, as new users of git worktree add might find it helpful.

---
 Documentation/git-worktree.txt | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
Documentation/git-worktree.txt +10 -10
index b472acc356..121895209d 100644
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
 SYNOPSIS
 --------
 [verse]
-'git worktree add' [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]
+'git worktree add' [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
@@ -45,14 +45,14 @@ specifying `--reason` to explain why the working tree is locked.
 
 COMMANDS
 --------
-add <path> [<branch>]::
+add <path> [<commit-ish>]::
 
-Create `<path>` and checkout `<branch>` into it. The new working directory
+Create `<path>` and checkout `<commit-ish>` into it. The new working directory
 is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
-specified as `<branch>`; it is synonymous with `@{-1}`.
+specified as `<commit-ish>`; it is synonymous with `@{-1}`.
 +
-If `<branch>` is omitted and neither `-b` nor `-B` nor `--detach` used,
+If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
 
@@ -84,25 +84,25 @@ OPTIONS
 
 -f::
 --force::
-	By default, `add` refuses to create a new working tree when `<branch>`
+	By default, `add` refuses to create a new working tree when `<commit-ish>` is a branch name and
 	is already checked out by another working tree. This option overrides
 	that safeguard.
 
 -b <new-branch>::
 -B <new-branch>::
 	With `add`, create a new branch named `<new-branch>` starting at
-	`<branch>`, and check out `<new-branch>` into the new working tree.
-	If `<branch>` is omitted, it defaults to HEAD.
+	`<commit-ish>`, and check out `<new-branch>` into the new working tree.
+	If `<commit-ish>` is omitted, it defaults to HEAD.
 	By default, `-b` refuses to create a new branch if it already
 	exists. `-B` overrides this safeguard, resetting `<new-branch>` to
-	`<branch>`.
+	`<commit-ish>`.
 
 --detach::
 	With `add`, detach HEAD in the new working tree. See "DETACHED HEAD"
 	in linkgit:git-checkout[1].
 
 --[no-]checkout::
-	By default, `add` checks out `<branch>`, however, `--no-checkout` can
+	By default, `add` checks out `<commit-ish>`, however, `--no-checkout` can
 	be used to suppress checkout in order to make customizations,
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
Signature · 1 line
2.15.0.426.gb06021eeb
#33 lore raw

PATCH v6 3/6 worktree: add --[no-]track option to the add subcommand

In reply to Thomas Gummerer

Currently 'git worktree add' sets up tracking branches if '<branch>' is a remote tracking branch, and doesn't set them up otherwise, as is the default for 'git branch'.

This may or may not be what the user wants. Allow overriding this behaviour with a --[no-]track flag that gets passed through to 'git branch'.

We already respect branch.autoSetupMerge, as 'git worktree' just calls 'git branch' internally.

---
 Documentation/git-worktree.txt |  6 +++++
 builtin/worktree.c             |  8 +++++++
 t/t2025-worktree-add.sh        | 51 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+)
Documentation/git-worktree.txt +6 -0
index 121895209d..15e58b18f7 100644
@@ -107,6 +107,12 @@ OPTIONS
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
 
+--[no-]track::
+	When creating a new branch, if `<commit-ish>` is a branch,
+	mark it as "upstream" from the new branch.  This is the
+	default if `<commit-ish>` is a remote-tracking branch.  See
+	"--track" in linkgit:git-branch[1] for details.
+
 --lock::
 	Keep the working tree locked after creation. This is the
 	equivalent of `git worktree lock` after `git worktree add`,
builtin/worktree.c +8 -0
index ed043d5f1c..ea9678cac8 100644
@@ -341,6 +341,7 @@ static int add(int ac, const char **av, const char *prefix)
 	const char *new_branch_force = NULL;
 	char *path;
 	const char *branch;
+	const char *opt_track = NULL;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -350,6 +351,9 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
 		OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
 		OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
+		OPT_PASSTHRU(0, "track", &opt_track, NULL,
+			     N_("set up tracking mode (see git-branch(1))"),
+			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
 		OPT_END()
 	};
 
@@ -394,9 +398,13 @@ static int add(int ac, const char **av, const char *prefix)
 			argv_array_push(&cp.args, "--force");
 		argv_array_push(&cp.args, opts.new_branch);
 		argv_array_push(&cp.args, branch);
+		if (opt_track)
+			argv_array_push(&cp.args, opt_track);
 		if (run_command(&cp))
 			return -1;
 		branch = opts.new_branch;
+	} else if (opt_track) {
+		die(_("--[no-]track can only be used if a new branch is created"));
 	}
 
 	UNLEAK(path);
t/t2025-worktree-add.sh +51 -0

56 lines not on this page · raw message

Signature · 1 line
2.15.0.426.gb06021eeb
#34 lore raw

PATCH v6 4/6 worktree: make add <path> <branch> dwim

In reply to Thomas Gummerer

Currently 'git worktree add <path> <branch>', errors out when 'branch' is not a local branch. It has no additional dwim'ing features that one might expect.

Make it behave more like 'git checkout <branch>' when the branch doesn't exist locally, but a remote tracking branch uniquely matches the desired branch name, i.e. create a new branch from the remote tracking branch and set the upstream to the remote tracking branch.

As 'git worktree add' currently just dies in this situation, there are no backwards compatibility worries when introducing this feature.

---
 Documentation/git-worktree.txt |  8 ++++++++
 builtin/worktree.c             | 16 ++++++++++++++++
 t/t2025-worktree-add.sh        | 19 +++++++++++++++++++
 3 files changed, 43 insertions(+)
Documentation/git-worktree.txt +8 -0
index 15e58b18f7..3044d305a6 100644
@@ -52,6 +52,14 @@ is linked to the current repository, sharing everything except working
 directory specific files such as HEAD, index, etc. `-` may also be
 specified as `<commit-ish>`; it is synonymous with `@{-1}`.
 +
+If <commit-ish> is a branch name (call it `<branch>` and is not found,
+and neither `-b` nor `-B` nor `--detach` are used, but there does
+exist a tracking branch in exactly one remote (call it `<remote>`)
+with a matching name, treat as equivalent to
+------------
+$ git worktree add --track -b <branch> <path> <remote>/<branch>
+------------
++
 If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, a new branch based at HEAD is created automatically,
 as if `-b $(basename <path>)` was specified.
builtin/worktree.c +16 -0
index ea9678cac8..7021d02585 100644
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "checkout.h"
 #include "config.h"
 #include "builtin.h"
 #include "dir.h"
@@ -390,6 +391,21 @@ static int add(int ac, const char **av, const char *prefix)
 		opts.new_branch = xstrndup(s, n);
 	}
 
+	if (ac == 2 && !opts.new_branch && !opts.detach) {
+		struct object_id oid;
+		struct commit *commit;
+		const char *remote;
+
+		commit = lookup_commit_reference_by_name(branch);
+		if (!commit) {
+			remote = unique_tracking_name(branch, &oid);
+			if (remote) {
+				opts.new_branch = branch;
+				branch = remote;
+			}
+		}
+	}
+
 	if (opts.new_branch) {
 		struct child_process cp = CHILD_PROCESS_INIT;
 		cp.git_cmd = 1;
t/t2025-worktree-add.sh +19 -0
index 72e8b62927..96ebc63d04 100755
@@ -365,4 +365,23 @@ test_expect_success '--no-track avoids setting up tracking' '
 	)
 '
 
+test_expect_success '"add" <path> <non-existent-branch> fails' '
+	test_must_fail git worktree add foo non-existent
+'
+
+test_expect_success '"add" <path> <branch> dwims' '
+	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 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_done
Signature · 1 line
2.15.0.426.gb06021eeb
#35 lore raw

PATCH v6 5/6 worktree: add --guess-remote flag to add subcommand

In reply to Thomas Gummerer

Currently 'git worktree add <path>' creates a new branch named after the basename of the <path>, that matches the HEAD of whichever worktree we were on when calling "git worktree add <path>".

It's sometimes useful to have 'git worktree add <path> behave more like the dwim machinery in 'git checkout <new-branch>', i.e. check if the new branch name, derived from the basename of the <path>, uniquely matches the branch name of a remote-tracking branch, and if so check out that branch and set the upstream to the remote-tracking branch.

Add a new --guess-remote option that enables exactly that behaviour.

---
 Documentation/git-worktree.txt |  7 +++++++
 builtin/worktree.c             | 10 ++++++++++
 t/t2025-worktree-add.sh        | 29 +++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+)
Documentation/git-worktree.txt +7 -0
index 3044d305a6..a4ffee5e08 100644
@@ -115,6 +115,13 @@ OPTIONS
 	such as configuring sparse-checkout. See "Sparse checkout"
 	in linkgit:git-read-tree[1].
 
+--[no-]guess-remote::
+	With `worktree add <path>`, withouth `<commit-ish>`, instead
+	of creating a new branch from HEAD, if there exists a tracking
+	branch in exactly one remote matching the basename of `<path>,
+	base the new branch on the remote-tracking branch, and mark
+	the remote-tracking branch as "upstream" from the new branch.
+
 --[no-]track::
 	When creating a new branch, if `<commit-ish>` is a branch,
 	mark it as "upstream" from the new branch.  This is the
builtin/worktree.c +10 -0
index 7021d02585..15cb1600ee 100644
@@ -343,6 +343,7 @@ static int add(int ac, const char **av, const char *prefix)
 	char *path;
 	const char *branch;
 	const char *opt_track = NULL;
+	int guess_remote = 0;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -355,6 +356,8 @@ static int add(int ac, const char **av, const char *prefix)
 		OPT_PASSTHRU(0, "track", &opt_track, NULL,
 			     N_("set up tracking mode (see git-branch(1))"),
 			     PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
+		OPT_BOOL(0, "guess-remote", &guess_remote,
+			 N_("try to match the new branch name with a remote-tracking branch")),
 		OPT_END()
 	};
 
@@ -389,6 +392,13 @@ static int add(int ac, const char **av, const char *prefix)
 		int n;
 		const char *s = worktree_basename(path, &n);
 		opts.new_branch = xstrndup(s, n);
+		if (guess_remote) {
+			struct object_id oid;
+			const char *remote =
+				unique_tracking_name(opts.new_branch, &oid);
+			if (remote)
+				branch = remote;
+		}
 	}
 
 	if (ac == 2 && !opts.new_branch && !opts.detach) {
t/t2025-worktree-add.sh +29 -0
index 96ebc63d04..d25c774cb7 100755
@@ -384,4 +384,33 @@ test_expect_success '"add" <path> <branch> dwims' '
 	)
 '
 
+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 &&
+	(
+		cd repo_b &&
+		git worktree add ../foo
+	) &&
+	(
+		cd foo &&
+		test_must_fail git config "branch.foo.remote" &&
+		test_must_fail git config "branch.foo.merge" &&
+		! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
+test_expect_success 'git worktree add --guess-remote sets up tracking' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git worktree add --guess-remote ../foo
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_a foo &&
+		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
 test_done
Signature · 1 line
2.15.0.426.gb06021eeb
#36 lore raw

PATCH v6 6/6 add worktree.guessRemote config option

In reply to Thomas Gummerer

Some users might want to have the --guess-remote option introduced in the previous commit on by default, so they don't have to type it out every time they create a new worktree.

Add a config option worktree.guessRemote that allows users to configure the default behaviour for themselves.

---
 Documentation/config.txt       | 10 ++++++++++
 Documentation/git-worktree.txt |  3 +++
 builtin/worktree.c             | 14 ++++++++++++--
 t/t2025-worktree-add.sh        | 31 +++++++++++++++++++++++++++++++
 4 files changed, 56 insertions(+), 2 deletions(-)
Documentation/config.txt +10 -0
index 5f65fa9234..4966d90ebb 100644
@@ -3425,3 +3425,13 @@ web.browser::
 	Specify a web browser that may be used by some commands.
 	Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
 	may use it.
+
+worktree.guessRemote::
+	With `add`, if no branch argument, and neither of `-b` nor
+	`-B` nor `--detach` are given, the command defaults to
+	creating a new branch from HEAD.  If `worktree.guessRemote` is
+	set to true, `worktree add` tries to find a remote-tracking
+	branch whose name uniquely matches the new branch name.  If
+	such a branch exists, it is checked out and set as "upstream"
+	for the new branch.  If no such match can be found, it falls
+	back to creating a new branch from the current HEAD.
Documentation/git-worktree.txt +3 -0
index a4ffee5e08..89ad0faecf 100644
@@ -121,6 +121,9 @@ OPTIONS
 	branch in exactly one remote matching the basename of `<path>,
 	base the new branch on the remote-tracking branch, and mark
 	the remote-tracking branch as "upstream" from the new branch.
++
+This can also be set up as the default behaviour by using the
+`worktree.guessRemote` config option.
 
 --[no-]track::
 	When creating a new branch, if `<commit-ish>` is a branch,
builtin/worktree.c +12 -2
index 15cb1600ee..002a569a11 100644
@@ -33,8 +33,19 @@ struct add_opts {
 
 static int show_only;
 static int verbose;
+static int guess_remote;
 static timestamp_t expire;
 
+static int git_worktree_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "worktree.guessremote")) {
+		guess_remote = git_config_bool(var, value);
+		return 0;
+	}
+
+	return git_default_config(var, value, cb);
+}
+
 static int prune_worktree(const char *id, struct strbuf *reason)
 {
 	struct stat st;
@@ -343,7 +354,6 @@ static int add(int ac, const char **av, const char *prefix)
 	char *path;
 	const char *branch;
 	const char *opt_track = NULL;
-	int guess_remote = 0;
 	struct option options[] = {
 		OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
 		OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -591,7 +601,7 @@ int cmd_worktree(int ac, const char **av, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_default_config, NULL);
+	git_config(git_worktree_config, NULL);
 
 	if (ac < 2)
 		usage_with_options(worktree_usage, options);
t/t2025-worktree-add.sh +31 -0
index d25c774cb7..6ce9b9c070 100755
@@ -413,4 +413,35 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '
 	)
 '
 
+test_expect_success 'git worktree add with worktree.guessRemote sets up tracking' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git config worktree.guessRemote true &&
+		git worktree add ../foo
+	) &&
+	(
+		cd foo &&
+		test_branch_upstream foo repo_a foo &&
+		test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
+test_expect_success 'git worktree --no-guess-remote option overrides config' '
+	test_when_finished rm -rf repo_a repo_b foo &&
+	setup_remote_repo repo_a repo_b &&
+	(
+		cd repo_b &&
+		git config worktree.guessRemote true &&
+		git worktree add --no-guess-remote ../foo
+	) &&
+	(
+		cd foo &&
+		test_must_fail git config "branch.foo.remote" &&
+		test_must_fail git config "branch.foo.merge" &&
+		! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
+	)
+'
+
 test_done
Signature · 1 line
2.15.0.426.gb06021eeb