worktree: add --guess-remote flag to add subcommand

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. Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Thomas Gummerer committed Nov 29, 2017 at 20:04 UTC 71d6682d8ca9870cbe457cf55c83402c33b41030
3 files changed +46
Documentation/git-worktree.txt
+7
@@ -115,6 +115,13 @@ OPTIONS
115 such as configuring sparse-checkout. See "Sparse checkout"
116 in linkgit:git-read-tree[1].
117
118 +--[no-]guess-remote::
119 + With `worktree add <path>`, without `<commit-ish>`, instead
120 + of creating a new branch from HEAD, if there exists a tracking
121 + branch in exactly one remote matching the basename of `<path>,
122 + base the new branch on the remote-tracking branch, and mark
123 + the remote-tracking branch as "upstream" from the new branch.
124 +
125 --[no-]track::
126 When creating a new branch, if `<commit-ish>` is a branch,
127 mark it as "upstream" from the new branch. This is the
builtin/worktree.c
+10
@@ -343,6 +343,7 @@ static int add(int ac, const char **av, const char *prefix)
343 char *path;
344 const char *branch;
345 const char *opt_track = NULL;
346 + int guess_remote = 0;
347 struct option options[] = {
348 OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
349 OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -355,6 +356,8 @@ static int add(int ac, const char **av, const char *prefix)
356 OPT_PASSTHRU(0, "track", &opt_track, NULL,
357 N_("set up tracking mode (see git-branch(1))"),
358 PARSE_OPT_NOARG | PARSE_OPT_OPTARG),
359 + OPT_BOOL(0, "guess-remote", &guess_remote,
360 + N_("try to match the new branch name with a remote-tracking branch")),
361 OPT_END()
362 };
363
@@ -389,6 +392,13 @@ static int add(int ac, const char **av, const char *prefix)
392 int n;
393 const char *s = worktree_basename(path, &n);
394 opts.new_branch = xstrndup(s, n);
395 + if (guess_remote) {
396 + struct object_id oid;
397 + const char *remote =
398 + unique_tracking_name(opts.new_branch, &oid);
399 + if (remote)
400 + branch = remote;
401 + }
402 }
403
404 if (ac == 2 && !opts.new_branch && !opts.detach) {
t/t2025-worktree-add.sh
+29
@@ -384,4 +384,33 @@ test_expect_success '"add" <path> <branch> dwims' '
384 )
385 '
386
387 +test_expect_success 'git worktree add does not match remote' '
388 + test_when_finished rm -rf repo_a repo_b foo &&
389 + setup_remote_repo repo_a repo_b &&
390 + (
391 + cd repo_b &&
392 + git worktree add ../foo
393 + ) &&
394 + (
395 + cd foo &&
396 + test_must_fail git config "branch.foo.remote" &&
397 + test_must_fail git config "branch.foo.merge" &&
398 + ! test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
399 + )
400 +'
401 +
402 +test_expect_success 'git worktree add --guess-remote sets up tracking' '
403 + test_when_finished rm -rf repo_a repo_b foo &&
404 + setup_remote_repo repo_a repo_b &&
405 + (
406 + cd repo_b &&
407 + git worktree add --guess-remote ../foo
408 + ) &&
409 + (
410 + cd foo &&
411 + test_branch_upstream foo repo_a foo &&
412 + test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
413 + )
414 +'
415 +
416 test_done