Currently git worktree add creates a new branch, that matches the HEAD of whichever worktree we were on when calling "git worktree add". Add a --guess flag to git worktree add, that makes it behave 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. Signed-off-by: Thomas Gummerer <t.gummerer@xxxxxxxxx> --- I'm a bit torn about hiding his behind an additional flag in git worktree add or not. I would like to have the feature without the additional flag, but it might break some peoples expectations, so dunno. builtin/worktree.c | 14 +++++++++- t/t2025-worktree-add.sh | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/builtin/worktree.c b/builtin/worktree.c index 7b9307aa58..5740d1f8a3 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "checkout.h" #include "config.h" #include "builtin.h" #include "dir.h" @@ -341,6 +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 dwim_new_branch = 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"), @@ -350,6 +352,7 @@ 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, "guess", &dwim_new_branch, N_("checkout upstream branch if there's a unique match")), OPT_END() }; @@ -363,6 +366,7 @@ static int add(int ac, const char **av, const char *prefix) path = prefix_filename(prefix, av[0]); branch = ac < 2 ? "HEAD" : av[1]; + dwim_new_branch = ac < 2 ? dwim_new_branch : 0; if (!strcmp(branch, "-")) branch = "@{-1}"; @@ -387,13 +391,21 @@ static int add(int ac, const char **av, const char *prefix) } if (opts.new_branch) { + struct object_id oid; + const char *remote; struct child_process cp = CHILD_PROCESS_INIT; + + remote = unique_tracking_name(opts.new_branch, &oid); + cp.git_cmd = 1; argv_array_push(&cp.args, "branch"); if (opts.force_new_branch) argv_array_push(&cp.args, "--force"); argv_array_push(&cp.args, opts.new_branch); - argv_array_push(&cp.args, branch); + if (dwim_new_branch && remote) + argv_array_push(&cp.args, remote); + else + argv_array_push(&cp.args, branch); if (run_command(&cp)) return -1; branch = opts.new_branch; diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh index b5c47ac602..b37c279787 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2025-worktree-add.sh @@ -6,6 +6,16 @@ test_description='test git worktree add' . "$TEST_DIRECTORY"/lib-rebase.sh +# 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 +} + test_expect_success 'setup' ' test_commit init ' @@ -314,4 +324,64 @@ test_expect_success 'rename a branch under bisect not allowed' ' test_must_fail git branch -M under-bisect bisect-with-new-name ' +test_expect_success 'git worktree add does not dwim' ' + test_when_finished rm -rf repo_a && + test_when_finished rm -rf repo_b && + test_when_finished rm -rf foo && + git init repo_a && + ( + cd repo_a && + test_commit a_master && + git checkout -b foo && + test_commit a_foo + ) && + git init repo_b && + ( + cd repo_b && + test_commit b_master && + git remote add repo_a ../repo_a && + git config remote.repo_a.fetch \ + "+refs/heads/*:refs/remotes/other_a/*" && + git fetch --all && + git worktree add ../foo + ) && + ( + cd foo && + ! test_branch_upstream foo repo_a foo && + git rev-parse other_a/foo >expect && + git rev-parse foo >actual && + ! test_cmp expect actual + ) +' + +test_expect_success 'git worktree add --guess dwims' ' + test_when_finished rm -rf repo_a && + test_when_finished rm -rf repo_b && + test_when_finished rm -rf foo && + git init repo_a && + ( + cd repo_a && + test_commit a_master && + git checkout -b foo && + test_commit a_foo + ) && + git init repo_b && + ( + cd repo_b && + test_commit b_master && + git remote add repo_a ../repo_a && + git config remote.repo_a.fetch \ + "+refs/heads/*:refs/remotes/other_a/*" && + git fetch --all && + git worktree add --guess ../foo + ) && + ( + cd foo && + test_branch_upstream foo repo_a foo && + git rev-parse other_a/foo >expect && + git rev-parse foo >actual && + test_cmp expect actual + ) +' + test_done -- 2.15.0.403.gc27cc4dac6