From d861d34a6ed0d04cef99ad2bdf3fdd3b23cee63a Mon Sep 17 00:00:00 2001 From: Thomas Gummerer Date: Tue, 24 Apr 2018 22:56:32 +0100 Subject: worktree: remove extra members from struct add_opts There are two members of 'struct add_opts', which are only used inside the 'add()' function, but being part of 'struct add_opts' they are needlessly also passed to the 'add_worktree' function. Make them local to the 'add()' function to make it clearer where they are used. Signed-off-by: Thomas Gummerer Reviewed-by: Eric Sunshine Signed-off-by: Junio C Hamano diff --git a/builtin/worktree.c b/builtin/worktree.c index 7cef5b1..bf305c8 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -27,8 +27,6 @@ struct add_opts { int detach; int checkout; int keep_locked; - const char *new_branch; - int force_new_branch; }; static int show_only; @@ -363,10 +361,11 @@ static int add(int ac, const char **av, const char *prefix) const char *new_branch_force = NULL; char *path; const char *branch; + const char *new_branch = NULL; const char *opt_track = NULL; struct option options[] = { OPT__FORCE(&opts.force, N_("checkout even if already checked out in other worktree")), - OPT_STRING('b', NULL, &opts.new_branch, N_("branch"), + OPT_STRING('b', NULL, &new_branch, N_("branch"), N_("create a new branch")), OPT_STRING('B', NULL, &new_branch_force, N_("branch"), N_("create or reset a branch")), @@ -384,7 +383,7 @@ static int add(int ac, const char **av, const char *prefix) memset(&opts, 0, sizeof(opts)); opts.checkout = 1; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); - if (!!opts.detach + !!opts.new_branch + !!new_branch_force > 1) + if (!!opts.detach + !!new_branch + !!new_branch_force > 1) die(_("-b, -B, and --detach are mutually exclusive")); if (ac < 1 || ac > 2) usage_with_options(worktree_usage, options); @@ -395,33 +394,33 @@ static int add(int ac, const char **av, const char *prefix) if (!strcmp(branch, "-")) branch = "@{-1}"; - opts.force_new_branch = !!new_branch_force; - if (opts.force_new_branch) { + if (new_branch_force) { struct strbuf symref = STRBUF_INIT; - opts.new_branch = new_branch_force; + new_branch = new_branch_force; if (!opts.force && - !strbuf_check_branch_ref(&symref, opts.new_branch) && + !strbuf_check_branch_ref(&symref, new_branch) && ref_exists(symref.buf)) die_if_checked_out(symref.buf, 0); strbuf_release(&symref); } - if (ac < 2 && !opts.new_branch && !opts.detach) { + if (ac < 2 && !new_branch && !opts.detach) { int n; const char *s = worktree_basename(path, &n); - opts.new_branch = xstrndup(s, n); + new_branch = xstrndup(s, n); + UNLEAK(new_branch); if (guess_remote) { struct object_id oid; const char *remote = - unique_tracking_name(opts.new_branch, &oid); + unique_tracking_name(new_branch, &oid); if (remote) branch = remote; } } - if (ac == 2 && !opts.new_branch && !opts.detach) { + if (ac == 2 && !new_branch && !opts.detach) { struct object_id oid; struct commit *commit; const char *remote; @@ -430,25 +429,25 @@ static int add(int ac, const char **av, const char *prefix) if (!commit) { remote = unique_tracking_name(branch, &oid); if (remote) { - opts.new_branch = branch; + new_branch = branch; branch = remote; } } } - if (opts.new_branch) { + if (new_branch) { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; argv_array_push(&cp.args, "branch"); - if (opts.force_new_branch) + if (new_branch_force) argv_array_push(&cp.args, "--force"); - argv_array_push(&cp.args, opts.new_branch); + argv_array_push(&cp.args, 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; + branch = new_branch; } else if (opt_track) { die(_("--[no-]track can only be used if a new branch is created")); } -- cgit v0.10.2-6-g49f6 From 2c27002a0aaab03ab850244c66d116109e7d2b83 Mon Sep 17 00:00:00 2001 From: Thomas Gummerer Date: Tue, 24 Apr 2018 22:56:33 +0100 Subject: worktree: improve message when creating a new worktree Currently 'git worktree add' produces output like the following: Preparing ../foo (identifier foo) HEAD is now at 26da330922 The '../foo' is the path where the worktree is created, which the user has just given on the command line. The identifier is an internal implementation detail, which is not particularly relevant for the user and indeed isn't mentioned explicitly anywhere in the man page. Instead of this message, print a message that gives the user a bit more detail of what exactly 'git worktree' is doing. There are various dwim modes which perform some magic under the hood, which should be helpful to users. Just from the output of the command it is not always visible to users what exactly has happened. Help the users a bit more by modifying the "Preparing ..." message and adding some additional information of what 'git worktree add' did under the hood, while not displaying the identifier anymore. Currently there are several different cases: - 'git worktree add -b ...' or 'git worktree add <path>', both of which create a new branch, either through the user explicitly requesting it, or through 'git worktree add' implicitly creating it. This will end up with the following output: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add -B ...', which may either create a new branch if the branch with the given name does not exist yet, or resets an existing branch to the current HEAD, or the commit-ish given. Depending on which action is taken, we'll end up with the following output: Preparing worktree (resetting branch '<branch>'; was at caa68db14) HEAD is now at 26da330922 <title> or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title> - 'git worktree add --detach' or 'git worktree add <path> <commit-ish>', both of which create a new worktree with a detached HEAD, for which we will print the following output: Preparing worktree (detached HEAD 26da330922) HEAD is now at 26da330922 <title> - 'git worktree add <path> <local-branch>', which checks out the branch and prints the following output: Preparing worktree (checking out '<local-branch>') HEAD is now at 47007d5 <title> Additionally currently the "Preparing ..." line is printed to stderr, while the "HEAD is now at ..." line is printed to stdout by 'git reset --hard', which is used internally by 'git worktree add'. Fix this inconsistency by printing the "Preparing ..." message to stdout as well. As "Preparing ..." is not an error, stdout also seems like the more appropriate output stream. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> diff --git a/builtin/worktree.c b/builtin/worktree.c index bf305c8..39bf1ea 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -301,8 +301,6 @@ static int add_worktree(const char *path, const char *refname, strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); - fprintf_ln(stderr, _("Preparing %s (identifier %s)"), path, name); - argv_array_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf); argv_array_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path); cp.git_cmd = 1; @@ -355,6 +353,40 @@ done: return ret; } +static void print_preparing_worktree_line(int detach, + const char *branch, + const char *new_branch, + int force_new_branch) +{ + if (force_new_branch) { + struct commit *commit = lookup_commit_reference_by_name(new_branch); + if (!commit) + printf_ln(_("Preparing worktree (new branch '%s')"), new_branch); + else + printf_ln(_("Preparing worktree (resetting branch '%s'; was at %s)"), + new_branch, + find_unique_abbrev(commit->object.oid.hash, + DEFAULT_ABBREV)); + } else if (new_branch) { + printf_ln(_("Preparing worktree (new branch '%s')"), new_branch); + } else { + struct strbuf s = STRBUF_INIT; + if (!detach && !strbuf_check_branch_ref(&s, branch) && + ref_exists(s.buf)) + printf_ln(_("Preparing worktree (checking out '%s')"), + branch); + else { + struct commit *commit = lookup_commit_reference_by_name(branch); + if (!commit) + die(_("invalid reference: %s"), branch); + printf_ln(_("Preparing worktree (detached HEAD %s)"), + find_unique_abbrev(commit->object.oid.hash, + DEFAULT_ABBREV)); + } + strbuf_release(&s); + } +} + static int add(int ac, const char **av, const char *prefix) { struct add_opts opts; @@ -435,6 +467,8 @@ static int add(int ac, const char **av, const char *prefix) } } + print_preparing_worktree_line(opts.detach, branch, new_branch, !!new_branch_force); + if (new_branch) { struct child_process cp = CHILD_PROCESS_INIT; cp.git_cmd = 1; -- cgit v0.10.2-6-g49f6 From 6427f87186e53d9d4319d43e4efbe46bb93b7440 Mon Sep 17 00:00:00 2001 From: Thomas Gummerer <t.gummerer@gmail.com> Date: Tue, 24 Apr 2018 22:56:34 +0100 Subject: worktree: factor out dwim_branch function Factor out a dwim_branch function, which takes care of the dwim'ery in 'git worktree add <path>'. It's not too much code currently, but we're adding a new kind of dwim in a subsequent patch, at which point it makes more sense to have it as a separate function. Factor it out now to reduce the patch noise in the next patch. Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> diff --git a/builtin/worktree.c b/builtin/worktree.c index 39bf1ea..6bd32b6 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -387,6 +387,21 @@ static void print_preparing_worktree_line(int detach, } } +static const char *dwim_branch(const char *path, const char **new_branch) +{ + int n; + const char *s = worktree_basename(path, &n); + *new_branch = xstrndup(s, n); + UNLEAK(*new_branch); + if (guess_remote) { + struct object_id oid; + const char *remote = + unique_tracking_name(*new_branch, &oid); + return remote; + } + return NULL; +} + static int add(int ac, const char **av, const char *prefix) { struct add_opts opts; @@ -439,17 +454,9 @@ static int add(int ac, const char **av, const char *prefix) } if (ac < 2 && !new_branch && !opts.detach) { - int n; - const char *s = worktree_basename(path, &n); - new_branch = xstrndup(s, n); - UNLEAK(new_branch); - if (guess_remote) { - struct object_id oid; - const char *remote = - unique_tracking_name(new_branch, &oid); - if (remote) - branch = remote; - } + const char *s = dwim_branch(path, &new_branch); + if (s) + branch = s; } if (ac == 2 && !new_branch && !opts.detach) { -- cgit v0.10.2-6-g49f6 From f60a7b763fe070161b332d3878f81a7f09ab6e44 Mon Sep 17 00:00:00 2001 From: Thomas Gummerer <t.gummerer@gmail.com> Date: Tue, 24 Apr 2018 22:56:35 +0100 Subject: worktree: teach "add" to check out existing branches Currently 'git worktree add <path>' creates a new branch named after the basename of the path by default. If a branch with that name already exists, the command refuses to do anything, unless the '--force' option is given. However we can do a little better than that, and check the branch out if it is not checked out anywhere else. This will help users who just want to check an existing branch out into a new worktree, and save a few keystrokes. As the current behaviour is to simply 'die()' when a branch with the name of the basename of the path already exists, there are no backwards compatibility worries here. We will still 'die()' if the branch is checked out in another worktree, unless the --force flag is passed. Helped-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Reviewed-by: Eric Sunshine <sunshine@sunshineco.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt index 41585f5..5d54f36 100644 --- a/Documentation/git-worktree.txt +++ b/Documentation/git-worktree.txt @@ -61,8 +61,13 @@ $ 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. +then, as a convenience, the new worktree is associated with a branch +(call it `<branch>`) named after `$(basename <path>)`. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` does exist, it will be +checked out in the new worktree, if it's not checked out anywhere +else, otherwise the command will refuse to create the worktree (unless +`--force` is used). list:: diff --git a/builtin/worktree.c b/builtin/worktree.c index 6bd32b6..d3aeb48 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -391,8 +391,17 @@ static const char *dwim_branch(const char *path, const char **new_branch) { int n; const char *s = worktree_basename(path, &n); - *new_branch = xstrndup(s, n); - UNLEAK(*new_branch); + const char *branchname = xstrndup(s, n); + struct strbuf ref = STRBUF_INIT; + + UNLEAK(branchname); + if (!strbuf_check_branch_ref(&ref, branchname) && + ref_exists(ref.buf)) { + strbuf_release(&ref); + return branchname; + } + + *new_branch = branchname; if (guess_remote) { struct object_id oid; const char *remote = diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh index 2b95944..ad38507 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2025-worktree-add.sh @@ -198,13 +198,25 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' - test_commit c1 && - test_commit c2 && - git branch precious HEAD~1 && - test_must_fail git worktree add precious && - test_cmp_rev HEAD~1 precious && - test_path_is_missing precious +test_expect_success '"add" checks out existing branch of dwimd name' ' + git branch dwim HEAD~1 && + git worktree add dwim && + test_cmp_rev HEAD~1 dwim && + ( + cd dwim && + test_cmp_rev HEAD dwim + ) +' + +test_expect_success '"add <path>" dwim fails with checked out branch' ' + git checkout -b test-branch && + test_must_fail git worktree add test-branch && + test_path_is_missing test-branch +' + +test_expect_success '"add --force" with existing dwimd name doesnt die' ' + git checkout test-branch && + git worktree add --force test-branch ' test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' ' -- cgit v0.10.2-6-g49f6