worktree: teach "add" to check out existing branches
PATCH 1/1 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 brach 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.
---
This is a follow-up to
https://public-inbox.org/git/20171118181345.GC32324@hank/, where this was first suggested, but I didn't want to do it as part of that series. Now I finally got around to implementing it.
Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 22 +++++++++++++++------- t/t2025-worktree-add.sh | 15 ++++++++++++--- 3 files changed, 34 insertions(+), 12 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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. list::
builtin/worktree.c +15 -7
@@ -411,13 +411,21 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) {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; +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + branch = branchname; + opts.checkout =1 ; + }else { + opts.new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts.new_branch, &oid); +if (remote) + branch = remote; + } } }
t/t2025-worktree-add.sh +12 -3
@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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" no auto-vivify with --detach and <branch> omitted' '
Signature · 1 line
2.16.0.312.g896df04e46
Quoted 11 lines · Thomas Gummerer
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 brach with the name
^^^^^
Quoted 37 lines · Thomas Gummerer
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 brach 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.Signed-off-by Thomas Gummerer <[email protected]>---
This is a follow-up to
https://public-inbox.org/git/20171118181345.GC32324@hank/, where this was first suggested, but I didn't want to do it as part of that series. Now I finally got around to implementing it.Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 22 +++++++++++++++------- t/t2025-worktree-add.sh | 15 ++++++++++++--- 3 files changed, 34 insertions(+), 12 deletions(-)Documentation/git-worktree.txt +0
-0@@ -61,8 +61,13 @@ $ git worktree add --track -b <branch> <path> <remote>/<branch> ------------ +
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 70
Quoted 10 lines
Documentation/git-worktree.txt +7
-2If `<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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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.
It starts getting a bit too magical to me. We probably should print something "switching to branch..." or "creating new branch ..." to let people know what decision was taken, unless --quiet is given. Yeah I know --quiet does not exist. You don't need to add it now either since nobody has asked for it.
More or less related to this, there was a question [1] whether we could do better than $(basename <path>) for determining branch name. Since you're doing start to check if a branch exists, people may start asking to check for branch "foo/bar" from the path abc/foo/bar instead of just branch "bar".
Quoted 10 lines
Documentation/git-worktree.txt +0
-0list::builtin/worktree.c +0
-0@@ -411,13 +411,21 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) {int n;
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 415
Quoted 10 lines
builtin/worktree.c +2
-7const 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; +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ;
Perhaps a blank line after this to separate var declarations and the rest.
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 418
Quoted 3 lines
builtin/worktree.c +3
-0+if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + branch = branchname;
Hmm.. do we need UNLEAK(branch);? Maybe you should try valgrind, I'm not sure.
Quoted 46 lines
builtin/worktree.c +10
-0+ opts.checkout =1 ; + }else { + opts.new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts.new_branch, &oid); +if (remote) + branch = remote; + } } }t/t2025-worktree-add.sh +12
-4@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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" no auto-vivify with --detach and <branch> omitted' ' --2.16.0.312.g896df04e46
Signature · 1 line
Duy
Quoted 11 lines · Duy Nguyen
Quoted 10 lines · Thomas Gummerer
[...]
+
If<commit-ish>is omitted and neither-bnor-Bnor--detachused,Documentation/git-worktree.txt +5
-2-then, as a convenience, a new branch based at HEAD is created automatically, -as if `-b $(basename <path>)` was specified. +then, as a convenience, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +it will be checked out in the new worktree, if it's not checked out
Quoted 8 lines
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 70
Quoted 2 lines
Documentation/git-worktree.txt +2
-0+anywhere else, otherwise the command will refuse to create the +worktree.It starts getting a bit too magical to me. We probably should print something "switching to branch..." or "creating new branch ..." to let people know what decision was taken, unless --quiet is given. Yeah I know --quiet does not exist. You don't need to add it now either since nobody has asked for it.
I think that's a good idea. I'll add that, thanks.
Quoted 5 lines
More or less related to this, there was a question [1] whether we could do better than $(basename <path>) for determining branch name. Since you're doing start to check if a branch exists, people may start asking to check for branch "foo/bar" from the path abc/foo/bar instead of just branch "bar".
Thanks for pointing me at this. I feel like we might get ourselves some backwards compatibility worries when doing that. Lets say someone has a branch 'feature/a', using 'git worktree feature/a' would currently create a new branch 'a', and does not die.
I'd guess most users would want 'feature/a' checked out in that case, but we can't exactly be sure we won't break anyones workflow here. With your suggestion I guess that would be mitigated somehow as it shows which action is taken, but dunno.
Should we hide this behaviour behind a flag, and plan for it eventually becoming the default?
Quoted 16 lines
[1] https://github.com/git-for-windows/git/issues/1390
Quoted 14 lines
list::
builtin/worktree.c +0
-3@@ -411,13 +411,21 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) {int n;const char *s =worktree_basename (path, &n); - opts.new_branch =xstrndup (s, n); -if (guess_remote) { -struct object_id oid;
Quoted 8 lines
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 415
Quoted 6 lines
builtin/worktree.c +2
-4-const char *remote = -unique_tracking_name (opts.new_branch, &oid); -if (remote) - branch = remote; +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ;Perhaps a blank line after this to separate var declarations and the rest.
Will add.
Quoted 5 lines
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 418
Quoted 3 lines
builtin/worktree.c +3
-0+if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + branch = branchname;Hmm.. do we need UNLEAK(branch);? Maybe you should try valgrind, I'm not sure.
Good question, I'll have a look, thanks.
Quoted 51 lines
Quoted 46 lines
builtin/worktree.c +10
-0+ opts.checkout =1 ; + }else { + opts.new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts.new_branch, &oid); +if (remote) + branch = remote; + } } }t/t2025-worktree-add.sh +12
-4@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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" no auto-vivify with --detach and <branch> omitted' ' --2.16.0.312.g896df04e46
Signature · 1 line
Duy
v2 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererThe previous round was at <[email protected]>. Thanks Duy for the comments on the previous round.
In addition to the additional functionality, this series now includes improvements to the output of the "git worktree add" command. It doesn't include any new magic to guess the branchname, as was suggested, as that would be a bigger change in the behaviour of git worktree, and is not a particular itch I have right now, so I'd prefer to keep it separate.
Thomas Gummerer (3):
worktree: improve message when creating a new worktree
worktree: be clearer when "add" dwim-ery kicks in
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 45 +++++++++++++++++++++++++++++++++--------- t/t2025-worktree-add.sh | 15 +++++++++++--- 3 files changed, 55 insertions(+), 14 deletions(-)
Signature · 1 line
2.16.1.101.gde0f0111ea
PATCH v2 1/3 worktree: improve message when creating a new worktree
Currently 'git worktree add' produces output like the following, when '--no-checkout' is not given:
Preparing foo (identifier foo)
HEAD is now at 26da330922 <title>
where the first line is written to stderr, and the second line coming from 'git reset --hard' is written to stdout, even though both lines are supposed to tell the user what has happened. In addition to someone not familiar with 'git worktree', this might seem as if the current HEAD was modified, not the HEAD in the new working tree.
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
Fix these inconsistencies by making the 'git reset --hard' call quiet, and printing the message ourselves instead.
---
We might want to do something similar for the 'git branch' command in the 'add()' function, which currently prints some output if a branch is set up to track a remote. I couldn't find a good way to convey that information in the output here, without making it too verbose, and it's probably not great to loose that output either.
If anyone has any suggestions for that, I'd be glad to hear them :)
builtin/worktree.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
builtin/worktree.c +11 -2
@@ -303,7 +303,7 @@ 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); +fprintf (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);
@@ -320,10 +320,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +fprintf (stderr,_ (", setting HEAD to %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)
Signature · 1 line
2.16.1.101.gde0f0111ea
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
Quoted 14 lines · Thomas Gummerer
builtin/worktree.c +1
-1@@ -303,7 +303,7 @@ 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); +fprintf (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);
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 323
Quoted 4 lines
builtin/worktree.c +1
-0@@ -320,10 +320,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; + fprintf(stderr, _(", setting HEAD to %s"),
As a former translator, I'm not thrilled to see a sentence broken into two pieces like this. I'm not a Japanese translator, but I think this sentence is translated differently when the translator sees the whole line "Preparing ..., setting ...".
Since the code between the first fprintf and this one should not take long to execute, perhaps we can just delete the first printf and print a whole [*] sentence here?
I think the purpose of "Preparing..." in the first place is to show something when git is busy checkout out the worktree. As long as we print it before git-reset, we should be good.
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 329
Quoted 6 lines
builtin/worktree.c +6
-0+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV)); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf);
[*] Yes I know it's not "whole" because of this piece. But this one is more or less a separate sentence already so it's probably ok.
Is it a bit too long to print everything in one line though? CMIT_FMT_ONELINE could already fill 70 columns easily.
Quoted 13 lines
builtin/worktree.c +3
-1+fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)Signature · 1 line
2.16.1.101.gde0f0111ea
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
Quoted 18 lines · Duy Nguyen
Quoted 17 lines · Thomas Gummerer
builtin/worktree.c +1
-1@@ -303,7 +303,7 @@ 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); +fprintf (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);@@ -320,10 +320,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done;
Quoted 6 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 323
Quoted 1 line
builtin/worktree.c +1
-0+ fprintf(stderr, _(", setting HEAD to %s"),As a former translator, I'm not thrilled to see a sentence broken into two pieces like this. I'm not a Japanese translator, but I think this sentence is translated differently when the translator sees the whole line "Preparing ..., setting ...".
Good point, I didn't think about the sentence structure other languages could require here.
Quoted 18 lines
Since the code between the first fprintf and this one should not take long to execute, perhaps we can just delete the first printf and print a whole [*] sentence here?
I think the purpose of "Preparing..." in the first place is to show something when git is busy checkout out the worktree. As long as we print it before git-reset, we should be good.
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 329
Quoted 6 lines
builtin/worktree.c +6
-0+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV)); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf);[*] Yes I know it's not "whole" because of this piece. But this one is more or less a separate sentence already so it's probably ok.
Quoted 2 lines
Is it a bit too long to print everything in one line though?
CMIT_FMT_ONELINEcould already fill 70 columns easily.
Yeah, it does get a bit long. Maybe just splitting the sentences here would be the best solution, then we also won't have to worry about the split not being good for translation. I'll do that.
Quoted 13 lines
Quoted 13 lines
builtin/worktree.c +3
-1+fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)Signature · 1 line
2.16.1.101.gde0f0111ea
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
In reply to Duy NguyenQuoted 25 lines · Duy Nguyen
Quoted 14 lines · Thomas Gummerer
builtin/worktree.c +1
-1@@ -303,7 +303,7 @@ 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); +fprintf (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);builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 323
Quoted 4 lines
builtin/worktree.c +1
-0@@ -320,10 +320,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; + fprintf(stderr, _(", setting HEAD to %s"),As a former translator, I'm not thrilled to see a sentence broken into two pieces like this. I'm not a Japanese translator, but I think this sentence is translated differently when the translator sees the whole line "Preparing ..., setting ...".
Quoted 3 lines
Since the code between the first fprintf and this one should not take long to execute, perhaps we can just delete the first printf and print a whole [*] sentence here?
Yeah, also it is a bit troubling that "Preparing" is an incomplete line, and when update-ref/symblic-ref call fails, will stay to be incomplete.
Quoted 4 lines
I think the purpose of "Preparing..." in the first place is to show something when git is busy checkout out the worktree. As long as we print it before git-reset, we should be good.
Quoted 9 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 329
Quoted 6 lines
builtin/worktree.c +6
-0+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV)); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf);[*] Yes I know it's not "whole" because of this piece. But this one is more or less a separate sentence already so it's probably ok.
Doing all the additional fprintf/fputc at the same place (instead of across update-ref/symbolic-ref) would make it easier to follow and also allow it to make "whole" even with that piece. Prepare the abbreviated commit->object.oid.hash and FMT_ONELINE in the same strbuf and say that the HEAD is there. I think it also makes sense to split it into two sentences, so from that point of view, just dropping the change in the "Preparing" hunk and then saying "HEAD is now at '9876fdea title of the commit'" after update/symbolic-ref may also be a good change. That automatically removes the "what hapepns to the rest of the incomplete line when run_command() fails?" issue.
Quoted 16 lines
Is it a bit too long to print everything in one line though?
CMIT_FMT_ONELINEcould already fill 70 columns easily.Quoted 13 lines
builtin/worktree.c +3
-1+fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)Signature · 1 line
2.16.1.101.gde0f0111ea
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
In reply to Duy NguyenQuoted 8 lines · Duy Nguyen
As a former translator, I'm not thrilled to see a sentence broken into two pieces like this. I'm not a Japanese translator, but I think this sentence is translated differently when the translator sees the whole line "Preparing ..., setting ...".
I think the purpose of "Preparing..." in the first place is to show something when git is busy checkout out the worktree. As long as we print it before git-reset, we should be good.
The original message was "Enter <worktree>" which had the potential to confuse someone into thinking the working directory had changed[1], so it was changed to "Preparing...". The reason for keeping that message (rather than dropping it outright) was to provide context to messages printed after it, especially messages such as "HEAD is now at..." which might otherwise confuse the reader into thinking that HEAD in the current worktree changed rather than HEAD in the new worktree[2,3].
[1]: https://public-inbox.org/git/[email protected]/
[2]: https://public-inbox.org/git/CAPig+cRSHwmmF9cCUbRrDCCW4kvg9PeOUxP5VQpSGfxzMxHWOQ@mail.gmail.com/
[3]: https://public-inbox.org/git/CAPig+cSLs4-uKicVMBSKnEro_FYD722Hs1_U6qzTriM8Ciok2Q@mail.gmail.com/
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
Quoted 10 lines · Eric Sunshine
Quoted 8 lines · Duy Nguyen
As a former translator, I'm not thrilled to see a sentence broken into two pieces like this. I'm not a Japanese translator, but I think this sentence is translated differently when the translator sees the whole line "Preparing ..., setting ...".
I think the purpose of "Preparing..." in the first place is to show something when git is busy checkout out the worktree. As long as we print it before git-reset, we should be good.
Quoted 8 lines
The original message was "Enter <worktree>" which had the potential to confuse someone into thinking the working directory had changed[1], so it was changed to "Preparing...". The reason for keeping that message (rather than dropping it outright) was to provide context to messages printed after it, especially messages such as "HEAD is now at..." which might otherwise confuse the reader into thinking that HEAD in the current worktree changed rather than HEAD in the new worktree[2,3].
Thanks for the background! In that light, since we're already customizing the "HEAD is now at..." message, is it worth dropping the message now, maybe using something like "Worktree HEAD is now at ...", or something similar?
This would loose the information about the identifier of the worktree, but from a coarse look at the man page it doesn't seem like we advertise that widely (The only thing related to it I could find is
If the last path components in the working tree's path is unique among working trees, it can be used to identify worktrees. For example if you only have two working trees, at "/abc/def/ghi" and "/abc/def/ggg", then "ghi" or "def/ghi" is enough to point to the former working tree.
for which we don't need to print the identifier anywhere. We don't seem to mention the case where the last part is unique, and we add a number to make the identifier unique.
So given that maybe it would even be better to hide the part about the identifier, as it seems more like an implementation detail than relevant to the end user?
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
Quoted 9 lines · Thomas Gummerer
This would loose the information about the identifier of the worktree, but from a coarse look at the man page it doesn't seem like we advertise that widely
...
So given that maybe it would even be better to hide the part about the identifier, as it seems more like an implementation detail than relevant to the end user?
Exactly. I'd rather hide it. I haven't found any good reason that a user needs to know these IDs unless they poke into $GIT_DIR/worktrees, but they should not need to.
Signature · 1 line
Duy
Re: [PATCH v2 1/3] worktree: improve message when creating a new worktree
Quoted 7 lines · Duy Nguyen
Quoted 6 lines · Thomas Gummerer
This would loose the information about the identifier of the worktree, but from a coarse look at the man page it doesn't seem like we advertise that widely
...
Quoted 7 lines
Quoted 3 lines
So given that maybe it would even be better to hide the part about the identifier, as it seems more like an implementation detail than relevant to the end user?
Exactly. I'd rather hide it. I haven't found any good reason that a user needs to know these IDs unless they poke into $GIT_DIR/worktrees, but they should not need to.
Just FYI. I mentioned elsewhere [1] the possibility of a new extended ref syntax to refer to e.g. HEAD from a different worktree. Such syntax may make use of this id (which also means we might need to give the user control over these ids and not just always auto generate them).
But that's for the future. If/When that thing comes, we can worry about displaying id here then. For now I still think it's ok to hide it.
[1] https://public-inbox.org/git/20180210095952.GA9035@ash/T/#u
Signature · 1 line
Duy
PATCH v2 2/3 worktree: be clearer when "add" dwim-ery kicks in
In reply to Thomas GummererCurrently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in the next commit, which introduces a new kind of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
--- builtin/worktree.c | 3 +++ 1 file changed, 3 insertions(+)
builtin/worktree.c +3 -0
@@ -320,6 +320,9 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +if (opts->new_branch) +fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ (", setting HEAD to %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
Signature · 1 line
2.16.1.101.gde0f0111ea
PATCH v2 3/3 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 31 +++++++++++++++++++++++-------- t/t2025-worktree-add.sh | 15 ++++++++++++--- 3 files changed, 42 insertions(+), 13 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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. list::
builtin/worktree.c +23 -8
@@ -29,6 +29,7 @@ struct add_opts {int keep_locked;const char *new_branch;int force_new_branch; +int checkout_existing_branch; };static int show_only;
@@ -320,7 +321,10 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; -if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf (stderr,_ (", checking out existing branch '%s'" ), + refname); +else if (opts->new_branch)fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch); fprintf(stderr, _(", setting HEAD to %s"),
@@ -423,14 +427,25 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) {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; +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + branch = branchname; + opts.checkout_existing_branch =1 ; +UNLEAK (branch); + }else { + opts.new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts.new_branch, &oid); +if (remote) + branch = remote; + } } +strbuf_release (&ref); }if (ac ==2 && !opts.new_branch && !opts.detach) {
t/t2025-worktree-add.sh +12 -3
@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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" no auto-vivify with --detach and <branch> omitted' '
Signature · 1 line
2.16.1.101.gde0f0111ea
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 328
Quoted 6 lines · Thomas Gummerer
builtin/worktree.c +4
-1-if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf (stderr,_ (", checking out existing branch '%s'" ), + refname); +else if (opts->new_branch)fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch);
I wonder if "creating branch" and "checkout out branch" are enough.
Quoted 15 lines
builtin/worktree.c +4
-7@@ -423,14 +427,25 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) {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; +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + + if (!strbuf_check_branch_ref(&ref, branchname) &&
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 443
Quoted 10 lines
builtin/worktree.c +10
-0+ ref_exists(ref.buf)) { + branch = branchname; + opts.checkout_existing_branch = 1; + UNLEAK(branch); + } else { + opts.new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts.new_branch, &oid);
Deep indentation may be a sign that it's time to move all this code to a separate function, maybe dwim_branch() or something.
Quoted 8 lines
builtin/worktree.c +4
-0+if (remote) + branch = remote; + } } +strbuf_release (&ref); }if (ac ==2 && !opts.new_branch && !opts.detach) {
Quoted 9 lines · Duy Nguyen
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 328
Quoted 6 lines · Thomas Gummerer
builtin/worktree.c +4
-1-if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf (stderr,_ (", checking out existing branch '%s'" ), + refname); +else if (opts->new_branch)fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch);I wonder if "creating branch" and "checkout out branch" are enough.
Yeah, I think new/existing are redundant. If these were shown at the same time, then the extra adjectives would help differenciating them visually, but they are never shown at the same time, so...
Quoted 9 lines · Duy Nguyen
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 328
Quoted 6 lines · Thomas Gummerer
builtin/worktree.c +4
-1-if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf (stderr,_ (", checking out existing branch '%s'" ), + refname); +else if (opts->new_branch)fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch);I wonder if "creating branch" and "checkout out branch" are enough.
I thought printing the branch name might be a good idea just to show more clearly what the dwim did. Especially if we ever introduce new heuristics, for example to cater for the case you mentioned in an earlier email [1], I thought it might be helpful to also print the branch name. Especially because it makes it clear that the dwim the reporter of the github issue hoped for didn't kick in at this point.
That said, I don't really think the branchname is of much use for my usage of 'git worktree add', so I may be overestimating the need for it. I'm happy to remove it if that's preferred.
[1]: <CACsJy8D9LS7e=cVE3Fq2qOnxK5++nFg2vjuhkNtRO-Bx0X1j6w@mail.gmail.com>
Quoted 20 lines
Quoted 20 lines
builtin/worktree.c +9
-7@@ -423,14 +427,25 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) {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; +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + branch = branchname; + opts.checkout_existing_branch =1 ; +UNLEAK (branch); + }else {
Quoted 8 lines
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 443
Quoted 5 lines
builtin/worktree.c +5
-0+ opts.new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts.new_branch, &oid);Deep indentation may be a sign that it's time to move all this code to a separate function, maybe
dwim_branch()or something.
Makes sense, I'll factor it out. Thanks!
Quoted 8 lines
Quoted 8 lines
builtin/worktree.c +4
-0+if (remote) + branch = remote; + } } +strbuf_release (&ref); }if (ac ==2 && !opts.new_branch && !opts.detach) {
Quoted 9 lines · Thomas Gummerer
Quoted 8 lines · Duy Nguyen
Quoted 6 lines · Thomas Gummerer
builtin/worktree.c +4
-1-if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf (stderr,_ (", checking out existing branch '%s'" ), + refname); +else if (opts->new_branch)fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch);
Quoted 4 lines
Quoted 1 line
I wonder if "creating branch" and "checkout out branch" are enough.
I thought printing the branch name might be a good idea just to show more clearly what the dwim did.
No no printing branch name is definitely a good idea, especially when I think of one thing and type another. I shortened my example phrases too much. It should have been "creating branch %s" and "checkout out branch %s"
Signature · 1 line
Duy
Quoted 12 lines · Duy Nguyen
Quoted 11 lines · Thomas Gummerer
Quoted 9 lines · Duy Nguyen
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 328
Quoted 6 lines · Thomas Gummerer
builtin/worktree.c +4
-1-if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf (stderr,_ (", checking out existing branch '%s'" ), + refname); +else if (opts->new_branch)fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch);I wonder if "creating branch" and "checkout out branch" are enough.
Quoted 7 lines
Quoted 2 lines
I thought printing the branch name might be a good idea just to show more clearly what the dwim did.
No no printing branch name is definitely a good idea, especially when I think of one thing and type another. I shortened my example phrases too much. It should have been "creating branch %s" and "checkout out branch %s"
Ah sorry I misunderstood. Yeah I think that makes sense then :)
Quoted 2 lines
Signature · 1 line
Duy
v3 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererPrevious rounds are at <[email protected]> and <[email protected]>. Thanks Duy, Eric and Junio for the comments on the previous round.
Junio: I know it is the rc period right now, but the topic is also already queued in 'pu', so I wasn't sure if you prefer me submitting this now, or after 2.17 is released. I'm happy to resend this once 2.17 is out if that makes it easier for you.
This round improves the end user facing messages, and factors out a dwim_branch function as suggested by Duy.
Interdiff below:
builtin/worktree.c +32 -26
@@ -304,8 +304,6 @@ static int add_worktree(const char *path, const char *refname,strbuf_addf (&sb,"%s/commondir" , sb_repo.buf);write_file (sb.buf,"../.." ); -fprintf (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 ;
@@ -322,12 +320,12 @@ static int add_worktree(const char *path, const char *refname, goto done;if (opts->checkout_existing_branch) -fprintf (stderr,_ (", checking out existing branch '%s'" ), +fprintf (stderr,_ ("checking out branch '%s'" ), refname);else if (opts->new_branch) -fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch); -fprintf (stderr,_ (", setting HEAD to %s" ), +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));strbuf_reset (&sb);
@@ -373,6 +371,32 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static const char *dwim_branch (const char *path,struct add_opts *opts) +{ +int n; +const char *s =worktree_basename (path, &n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + *branch = branchname; + opts->checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + opts->new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts->new_branch, &oid); +return remote; + } +return NULL ; +} +static int add (int ac,const char **av,const char *prefix) {struct add_opts opts;
@@ -425,27 +449,9 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !opts.new_branch && !opts.detach) { -int n; -const char *s =worktree_basename (path, &n); -const char *branchname =xstrndup (s, n); -struct strbuf ref =STRBUF_INIT ; - -if (!strbuf_check_branch_ref (&ref, branchname) && -ref_exists (ref.buf)) { - branch = branchname; - opts.checkout_existing_branch =1 ; -UNLEAK (branch); - }else { - opts.new_branch = branchname; -if (guess_remote) { -struct object_id oid; -const char *remote = -unique_tracking_name (opts.new_branch, &oid); -if (remote) - branch = remote; - } - } -strbuf_release (&ref); +const char *dwim_branchname =dwim_branch (path, &opts); +if (dwim_branchname) + branch = dwim_branchname; }if (ac ==2 && !opts.new_branch && !opts.detach) {
Thomas Gummerer (4):
worktree: improve message when creating a new worktree
worktree: be clearer when "add" dwim-ery kicks in
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 +++-- builtin/worktree.c | 60 +++++++++++++++++++++++++--------- t/t2025-worktree-add.sh | 15 +++++++-- 3 files changed, 64 insertions(+), 20 deletions(-)
Signature · 1 line
2.17.0.rc0.231.g781580f06
PATCH v3 1/4 worktree: improve message when creating a new worktree
Currently 'git worktree add' produces output like the following, when '--no-checkout' is not given:
Preparing foo (identifier foo)
HEAD is now at 26da330922 <title>
where the first line is written to stderr, and the second line coming from 'git reset --hard' is written to stdout, even though both lines are supposed to tell the user what has happened. In addition to someone not familiar with 'git worktree', this might seem as if the current HEAD was modified, not the HEAD in the new working tree.
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
The identifier is also not particularly relevant for the user at the moment, as it's only used internally to distinguish between different worktrees that have the same $(basename <path>).
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call quiet, and printing the message directly from the builtin command instead.
--- builtin/worktree.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
builtin/worktree.c +10 -3
@@ -303,8 +303,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 ;
@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +fprintf (stderr,_ ("worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)
Signature · 1 line
2.17.0.rc0.231.g781580f06
PATCH v3 2/4 worktree: be clearer when "add" dwim-ery kicks in
In reply to Thomas GummererCurrently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in the next commit, which introduces a new kind of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
--- builtin/worktree.c | 3 +++ 1 file changed, 3 insertions(+)
builtin/worktree.c +3 -0
@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +if (opts->new_branch) +fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
Signature · 1 line
2.17.0.rc0.231.g781580f06
PATCH v3 3/4 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
builtin/worktree.c +17 -10
@@ -367,6 +367,20 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static const char *dwim_branch (const char *path,struct add_opts *opts) +{ +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); +return remote; + } +return NULL ; +} +static int add (int ac,const char **av,const char *prefix) {struct add_opts opts;
@@ -419,16 +433,9 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !opts.new_branch && !opts.detach) { -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; - } +const char *dwim_branchname =dwim_branch (path, &opts); +if (dwim_branchname) + branch = dwim_branchname; }if (ac ==2 && !opts.new_branch && !opts.detach) {
Signature · 1 line
2.17.0.rc0.231.g781580f06
PATCH v3 4/4 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 22 +++++++++++++++++++--- t/t2025-worktree-add.sh | 15 ++++++++++++--- 3 files changed, 38 insertions(+), 8 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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. list::
builtin/worktree.c +19 -3
@@ -29,6 +29,7 @@ struct add_opts {int keep_locked;const char *new_branch;int force_new_branch; +int checkout_existing_branch; };static int show_only;
@@ -318,8 +319,11 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; -if (opts->new_branch) -fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +if (opts->checkout_existing_branch) +fprintf (stderr,_ ("checking out branch '%s'" ), + refname); +else if (opts->new_branch) +fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch);fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
@@ -371,7 +375,19 @@ static const char *dwim_branch(const char *path, struct add_opts *opts) {int n;const char *s =worktree_basename (path, &n); - opts->new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + *branch = branchname; + opts->checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + opts->new_branch = branchname;if (guess_remote) {struct object_id oid; const char *remote =
t/t2025-worktree-add.sh +12 -3
@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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" no auto-vivify with --detach and <branch> omitted' '
Signature · 1 line
2.17.0.rc0.231.g781580f06
v4 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererPlease ignore v3 of this series, I just noticed that I botched the last commit :( sorry about the noise.
Previous rounds (other than v3) are at <[email protected]> and <[email protected]>. Thanks Duy, Eric and Junio for the comments on the previous round.
This round improves the end user facing messages, and factors out a dwim_branch function as suggested by Duy.
Interdiff between this and v2 below:
builtin/worktree.c +31 -26
@@ -304,8 +304,6 @@ static int add_worktree(const char *path, const char *refname,strbuf_addf (&sb,"%s/commondir" , sb_repo.buf);write_file (sb.buf,"../.." ); -fprintf (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 ;
@@ -322,12 +320,12 @@ static int add_worktree(const char *path, const char *refname, goto done;if (opts->checkout_existing_branch) -fprintf (stderr,_ (", checking out existing branch '%s'" ), +fprintf (stderr,_ ("checking out branch '%s'" ), refname);else if (opts->new_branch) -fprintf (stderr,_ (", creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch); -fprintf (stderr,_ (", setting HEAD to %s" ), +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));strbuf_reset (&sb);
@@ -373,6 +371,31 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static const char *dwim_branch (const char *path,struct add_opts *opts) +{ +int n; +const char *s =worktree_basename (path, &n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + opts->checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + opts->new_branch = branchname; +if (guess_remote) { +struct object_id oid; +const char *remote = +unique_tracking_name (opts->new_branch, &oid); +return remote; + } +return NULL ; +} +static int add (int ac,const char **av,const char *prefix) {struct add_opts opts;
@@ -425,27 +448,9 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !opts.new_branch && !opts.detach) { -int n; -const char *s =worktree_basename (path, &n); -const char *branchname =xstrndup (s, n); -struct strbuf ref =STRBUF_INIT ; - -if (!strbuf_check_branch_ref (&ref, branchname) && -ref_exists (ref.buf)) { - branch = branchname; - opts.checkout_existing_branch =1 ; -UNLEAK (branch); - }else { - opts.new_branch = branchname; -if (guess_remote) { -struct object_id oid; -const char *remote = -unique_tracking_name (opts.new_branch, &oid); -if (remote) - branch = remote; - } - } -strbuf_release (&ref); +const char *dwim_branchname =dwim_branch (path, &opts); +if (dwim_branchname) + branch = dwim_branchname; }if (ac ==2 && !opts.new_branch && !opts.detach) {
Thomas Gummerer (4):
worktree: improve message when creating a new worktree
worktree: be clearer when "add" dwim-ery kicks in
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 ++++-- builtin/worktree.c | 58 ++++++++++++++++++++++++++-------- t/t2025-worktree-add.sh | 15 +++++++-- 3 files changed, 64 insertions(+), 18 deletions(-)
Signature · 1 line
2.17.0.rc0.231.g781580f06
PATCH v4 1/4 worktree: improve message when creating a new worktree
Currently 'git worktree add' produces output like the following, when '--no-checkout' is not given:
Preparing foo (identifier foo)
HEAD is now at 26da330922 <title>
where the first line is written to stderr, and the second line coming from 'git reset --hard' is written to stdout, even though both lines are supposed to tell the user what has happened. In addition to someone not familiar with 'git worktree', this might seem as if the current HEAD was modified, not the HEAD in the new working tree.
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
The identifier is also not particularly relevant for the user at the moment, as it's only used internally to distinguish between different worktrees that have the same $(basename <path>).
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call quiet, and printing the message directly from the builtin command instead.
--- builtin/worktree.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
builtin/worktree.c +10 -3
@@ -303,8 +303,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 ;
@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +fprintf (stderr,_ ("worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)
Signature · 1 line
2.17.0.rc0.231.g781580f06
Re: [PATCH v4 1/4] worktree: improve message when creating a new worktree
Quoted 46 lines · Thomas Gummerer
Currently 'git worktree add' produces output like the following, when '--no-checkout' is not given:
Preparing foo (identifier foo)
HEAD is now at26da330922<title>where the first line is written to stderr, and the second line coming from 'git reset --hard' is written to stdout, even though both lines are supposed to tell the user what has happened. In addition to someone not familiar with 'git worktree', this might seem as if the current HEAD was modified, not the HEAD in the new working tree.
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
The identifier is also not particularly relevant for the user at the moment, as it's only used internally to distinguish between different worktrees that have the same $(basename <path>).
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call quiet, and printing the message directly from the builtin command instead.
Signed-off-by Thomas Gummerer <[email protected]>--- builtin/worktree.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)builtin/worktree.c +0
-2@@ -303,8 +303,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 ;
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 321
Quoted 4 lines
builtin/worktree.c +1
-0@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; + fprintf(stderr, _("worktree HEAD is now at %s"),
We use the term "working tree" for UI and documents. "worktree" is only used in code comments and stuff.
Quoted 19 lines
builtin/worktree.c +9
-2+ find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV)); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret) --2.17.0.rc0.231.g781580f06
Signature · 1 line
Duy
Re: [PATCH v4 1/4] worktree: improve message when creating a new worktree
Quoted 8 lines · Duy Nguyen
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 321
Quoted 5 lines
builtin/worktree.c +1
-0@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; + fprintf(stderr, _("worktree HEAD is now at %s"),We use the term "working tree" for UI and documents. "worktree" is only used in code comments and stuff.
Hmph, that is a bit different from what I recall. "working tree" is the phrase we have used and we still use to refer to those things that are checked out (as opposed to the in-repo data). We say "worktree" when we want to stress the fact that we are talking about a single instance among possibly multiple instances of the "working tree" that are associated to a single repository.
Technically speaking, the traditional "working tree" is everything in the directory immediately above ".git/" plus ".git/index"; a single "worktree" consists of a bit more, as we have to count per worktree states like .git/rebase-apply/ and .git/refs/bisect/ as part of it.
And from that point of view, HEAD is one of those per worktree states, so...
Re: [PATCH v4 1/4] worktree: improve message when creating a new worktree
In reply to Thomas Gummererbuiltin/worktree.c static int add_worktree(const char *path, const char *refname, · line 306
Quoted 13 lines · Thomas Gummerer
[...]
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call quiet, and printing the message directly from the builtin command instead.Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c +0
-1@@ -303,8 +303,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);
A minor regression with this change is that error messages from git-update-ref or git-symbolic-ref -- which could be emitted after this point but before the new "worktree HEAD is now at..." message -- are now somewhat orphaned. I'm not sure that it matters, though.
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 319
Quoted 6 lines
builtin/worktree.c +2
-0argv_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 ;@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname, +fprintf (stderr,_ ("worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
I wonder if this should say "new worktree HEAD is now at..." to clearly indicate that it is talking about HEAD in the _new_ worktree, not HEAD in the current worktree.
Quoted 5 lines
builtin/worktree.c +5
-0+strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr);
Re: [PATCH v4 1/4] worktree: improve message when creating a new worktree
Quoted 13 lines · Eric Sunshine
Quoted 12 lines · Thomas Gummerer
[...]
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call quiet, and printing the message directly from the builtin command instead.Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c +0
-0@@ -303,8 +303,6 @@ static int add_worktree(const char *path, const char *refname,strbuf_addf (&sb,"%s/commondir" , sb_repo.buf);write_file (sb.buf,"../.." );
Quoted 6 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 306
Quoted 1 line
builtin/worktree.c +0
-1-fprintf_ln (stderr,_ ("Preparing %s (identifier %s)" ), path, name);A minor regression with this change is that error messages from git-update-ref or git-symbolic-ref -- which could be emitted after this point but before the new "worktree HEAD is now at..." message -- are now somewhat orphaned. I'm not sure that it matters, though.
If those commands fail, we would now not print the "worktree HEAD is now at..." message, but go directly to "done", and clean up the working tree.
So while we no longer emit the "Preparing worktree" header, the user should still be aware that they are creating a new worktree, and that the error happened while creating a new worktree. From a (admittedly very quick) look the error messages would all make sense in this context, without an additional message.
Printing the "worktree HEAD is now at ..." message before that wouldn't make much sense, as we may not even have a new working tree at the end. We could add the message back, but that would also put us back at three lines of output. I think I prefer the more concise version here in the normal case, and I think we can live with the slight regression. But if others have a strong preference for the current way I'm happy to add that message back.
Quoted 10 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 319
Quoted 6 lines
builtin/worktree.c +2
-0argv_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 ;@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname, +fprintf (stderr,_ ("worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));I wonder if this should say "new worktree HEAD is now at..." to clearly indicate that it is talking about HEAD in the _new_ worktree, not HEAD in the current worktree.
Yeah I aggree that's nicer. Will change.
Quoted 5 lines
Quoted 5 lines
builtin/worktree.c +5
-0+strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr);
PATCH v4 2/4 worktree: be clearer when "add" dwim-ery kicks in
In reply to Thomas GummererCurrently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in the next commit, which introduces a new kind of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
--- builtin/worktree.c | 3 +++ 1 file changed, 3 insertions(+)
builtin/worktree.c +3 -0
@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +if (opts->new_branch) +fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
Signature · 1 line
2.17.0.rc0.231.g781580f06
Re: [PATCH v4 2/4] worktree: be clearer when "add" dwim-ery kicks in
Quoted 8 lines · Thomas Gummerer
Currently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
Good idea.
Quoted 6 lines
This will also be useful in the next commit, which introduces a new kind of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
Signed-off-by Thomas Gummerer <[email protected]>
Re: [PATCH v4 2/4] worktree: be clearer when "add" dwim-ery kicks in
In reply to Thomas GummererQuoted 13 lines · Thomas Gummerer
Currently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 322
Quoted 5 lines
builtin/worktree.c +3
-0@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname, +if (opts->new_branch) +fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
The "creating" message is missing a newline, which results in rather ugly output:
creating new branch 'foo'worktree HEAD is now at ...
Re: [PATCH v4 2/4] worktree: be clearer when "add" dwim-ery kicks in
Quoted 23 lines · Eric Sunshine
Quoted 13 lines · Thomas Gummerer
Currently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 322
Quoted 5 lines
builtin/worktree.c +3
-0@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname, +if (opts->new_branch) +fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));The "creating" message is missing a newline, which results in rather ugly output:
Quoted 1 line
creating new branch 'foo'worktree HEAD is now at ...
Also, I believe that the agreement[1] was that this message should say merely "creating branch", not "creating _new_ branch". And, indeed, patch 4/4 stealthily drops "new" from the message, but it really ought to be introduced with correct text in this patch, not fixed by 4/4.
Re: [PATCH v4 2/4] worktree: be clearer when "add" dwim-ery kicks in
Quoted 24 lines · Eric Sunshine
Quoted 23 lines · Eric Sunshine
Quoted 13 lines · Thomas Gummerer
Currently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 322
Quoted 5 lines
builtin/worktree.c +3
-0@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname, +if (opts->new_branch) +fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));The "creating" message is missing a newline, which results in rather ugly output:
Quoted 6 lines
Quoted 1 line
creating new branch 'foo'worktree HEAD is now at ...
Also, I believe that the agreement[1] was that this message should say merely "creating branch", not "creating _new_ branch". And, indeed, patch 4/4 stealthily drops "new" from the message, but it really ought to be introduced with correct text in this patch, not fixed by 4/4.
Ah yeah thanks for catching those, will fix.
Quoted 1 line
PATCH v4 3/4 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
builtin/worktree.c +17 -10
@@ -367,6 +367,20 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static const char *dwim_branch (const char *path,struct add_opts *opts) +{ +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); +return remote; + } +return NULL ; +} +static int add (int ac,const char **av,const char *prefix) {struct add_opts opts;
@@ -419,16 +433,9 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !opts.new_branch && !opts.detach) { -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; - } +const char *dwim_branchname =dwim_branch (path, &opts); +if (dwim_branchname) + branch = dwim_branchname; }if (ac ==2 && !opts.new_branch && !opts.detach) {
Signature · 1 line
2.17.0.rc0.231.g781580f06
PATCH v4 4/4 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 21 ++++++++++++++++++--- t/t2025-worktree-add.sh | 15 ++++++++++++--- 3 files changed, 37 insertions(+), 8 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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. list::
builtin/worktree.c +18 -3
@@ -29,6 +29,7 @@ struct add_opts {int keep_locked;const char *new_branch;int force_new_branch; +int checkout_existing_branch; };static int show_only;
@@ -318,8 +319,11 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; -if (opts->new_branch) -fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +if (opts->checkout_existing_branch) +fprintf (stderr,_ ("checking out branch '%s'" ), + refname); +else if (opts->new_branch) +fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch);fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
@@ -371,7 +375,18 @@ static const char *dwim_branch(const char *path, struct add_opts *opts) {int n;const char *s =worktree_basename (path, &n); - opts->new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + opts->checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + opts->new_branch = branchname;if (guess_remote) {struct object_id oid; const char *remote =
t/t2025-worktree-add.sh +12 -3
@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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" no auto-vivify with --detach and <branch> omitted' '
Signature · 1 line
2.17.0.rc0.231.g781580f06
Quoted 13 lines · Thomas Gummerer
[...]
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.
[...]
We will still 'die()' if the branch is checked out in another worktree, unless the --force flag is passed.Signed-off-by Thomas Gummerer <[email protected]>---
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 68
Quoted 10 lines
Documentation/git-worktree.txt +7
-2@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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.
Should this mention --force?
... refuse to create the worktree (unless --force is used).
builtin/worktree.c struct add_opts { · line 32
Quoted 6 lines
builtin/worktree.c +1
-0@@ -29,6 +29,7 @@ struct add_opts {int keep_locked;const char *new_branch;int force_new_branch; +int checkout_existing_branch;
As 'force_new_branch' and 'checkout_existing_branch' are mutually exclusive, I wonder if they should be combined into an enum to make it easier to reason about.
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 321
Quoted 6 lines
builtin/worktree.c +3
-2@@ -318,8 +319,11 @@ static int add_worktree(const char *path, const char *refname, -if (opts->new_branch) -fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +if (opts->checkout_existing_branch) +fprintf (stderr,_ ("checking out branch '%s'" ), + refname);
As with "creating branch" in 2/4, "checking out branch..." here is missing a newline.
Quoted 11 lines
builtin/worktree.c +2
-0+else if (opts->new_branch) +fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch);fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));t/t2025-worktree-add.sh +1
-1@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 &&
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 208
Quoted 10 lines
t/t2025-worktree-add.sh +6
-2git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +'
This test is no longer checking auto-vivification ("bringing a new branch to life automatically"), but rather branch name inference, so the title is now misleading. Perhaps retitle it to '"add" checks out existing branch of dwim'd name' or something.
(The name "precious" is also now misleading since it's no longer checking that a precious branch does not get clobbered, however, changing the name would make the diff unnecessarily noisy, so it's probably okay as is.)
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 217
Quoted 5 lines
t/t2025-worktree-add.sh +4
-0+test_expect_success '"add" auto-vivify fails with checked out branch' ' + git checkout -b test-branch && + test_must_fail git worktree add test-branch && + test_path_is_missing test-branch '
Should we also have a corresponding test that this "fail" can be overridden by --force? (I realize that --force is tested elsewhere, but only with an explicitly-provided branch name, whereas this dwims the branch name.)
Quoted 27 lines · Eric Sunshine
Quoted 13 lines · Thomas Gummerer
[...]
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.
[...]
We will still 'die()' if the branch is checked out in another worktree, unless the --force flag is passed.Signed-off-by Thomas Gummerer <[email protected]>---
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 68
Quoted 10 lines
Documentation/git-worktree.txt +7
-2@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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.Should this mention --force?
Quoted 1 line
... refuse to create the worktree (unless --force is used).
Yeah, will add.
Quoted 10 lines
builtin/worktree.c struct add_opts { · line 32
Quoted 6 lines
builtin/worktree.c +1
-0@@ -29,6 +29,7 @@ struct add_opts {int keep_locked;const char *new_branch;int force_new_branch; +int checkout_existing_branch;As '
force_new_branch' and 'checkout_existing_branch' are mutually exclusive, I wonder if they should be combined into an enum to make it easier to reason about.
I gave this a try, but I'm not sure the end result is nicer. The problem is that 'new_branch' and 'force_new_branch' both are mutually exclusive with 'checkout_existing_branch', but 'force_new_branch' is a "superset" of 'new_branch'.
I can't seem to think of a nice way to encode that, especially not without duplicating information we're already carrying in 'new_branch'. Looking at the code however I see that 'force_new_branch' is already only duplicating information that we already have in a variable in the same function. I think just removing that duplication and keeping the 'checkout_existing_branch' variable in the 'add_opts' would make most sense.
Quoted 9 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 321
Quoted 6 lines
builtin/worktree.c +3
-2@@ -318,8 +319,11 @@ static int add_worktree(const char *path, const char *refname, -if (opts->new_branch) -fprintf (stderr,_ ("creating new branch '%s'" ), opts->new_branch); +if (opts->checkout_existing_branch) +fprintf (stderr,_ ("checking out branch '%s'" ), + refname);As with "creating branch" in 2/4, "checking out branch..." here is missing a newline.
Thanks will add.
Quoted 27 lines
Quoted 11 lines
builtin/worktree.c +2
-0+else if (opts->new_branch) +fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch);fprintf (stderr,_ ("worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));t/t2025-worktree-add.sh +1
-1@@ -198,13 +198,22 @@ test_expect_success '"add" with <branch> omitted' ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" auto-vivify checks out existing branch' ' test_commit c1 && test_commit c2 &&t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 208
Quoted 10 lines
t/t2025-worktree-add.sh +6
-2git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +'This test is no longer checking auto-vivification ("bringing a new branch to life automatically"), but rather branch name inference, so the title is now misleading. Perhaps retitle it to '"add" checks out existing branch of dwim'd name' or something.
Quoted 4 lines
(The name "precious" is also now misleading since it's no longer checking that a precious branch does not get clobbered, however, changing the name would make the diff unnecessarily noisy, so it's probably okay as is.)
Good point. I can add an additional patch with that rename, so the changes here stay more obvious, but the end result would still end up less confusing.
Quoted 10 lines
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 217
Quoted 5 lines
t/t2025-worktree-add.sh +4
-0+test_expect_success '"add" auto-vivify fails with checked out branch' ' + git checkout -b test-branch && + test_must_fail git worktree add test-branch && + test_path_is_missing test-branch 'Should we also have a corresponding test that this "fail" can be overridden by --force? (I realize that --force is tested elsewhere, but only with an explicitly-provided branch name, whereas this dwims the branch name.)
Yes, will add a test. Thanks for your review!
v5 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererThanks Eric for the review of the previous round and Duy and Junio for additional comments.
Previous rounds are at <[email protected]>, <[email protected]>, <[email protected]> and <[email protected]>.
This round should address all of Eric's comments from the previous round.
As explained in more detail in a reply to the review comment directly, I did not add an enum to 'struct add_opts', for 'force_new_branch' and 'checkout_existing_branch', but instead removed 'force_new_branch' from the struct as it's not required.
The rest of the updates are mainly in the user facing messages, documentation and one added test.
Interdiff below:
Documentation/git-worktree.txt +1 -1
@@ -67,7 +67,7 @@ doesn't exist, a new branch based on HEAD is automatically created as if `-b <branch>` was given. If `<branch>` exists in the repository, 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. +worktree (unless `--force` is used). list::
builtin/worktree.c +6 -8
@@ -28,7 +28,6 @@ struct add_opts {int checkout;int keep_locked;const char *new_branch; -int force_new_branch;int checkout_existing_branch; };
@@ -320,12 +319,12 @@ static int add_worktree(const char *path, const char *refname, goto done;if (opts->checkout_existing_branch) -fprintf (stderr,_ ("checking out branch '%s'" ), - refname); +fprintf_ln (stderr,_ ("checking out branch '%s'" ), + refname);else if (opts->new_branch) -fprintf (stderr,_ ("creating branch '%s'" ), opts->new_branch); +fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch); -fprintf (stderr,_ ("worktree HEAD is now at %s" ), +fprintf (stderr,_ ("new worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));strbuf_reset (&sb);
@@ -434,8 +433,7 @@ 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;
@@ -472,7 +470,7 @@ static int add(int ac, const char **av, const char *prefix)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, branch);
t/t2025-worktree-add.sh +10 -6
@@ -198,15 +198,15 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify checks out existing branch' ' +test_expect_success '"add" checks out existing branch of dwimd name' ' test_commit c1 && test_commit c2 && - git branch precious HEAD~1 && - git worktree add precious && - test_cmp_rev HEAD~1 precious && + git branch dwim HEAD~1 && + git worktree add dwim && + test_cmp_rev HEAD~1 dwim && ( - cd precious && - test_cmp_rev precious HEAD + cd dwim && + test_cmp_rev dwim HEAD ) '
@@ -216,6 +216,10 @@ test_expect_success '"add" auto-vivify fails with checked out branch' ' test_path_is_missing test-branch ' +test_expect_success '"add --force" with existing dwimd name doesnt die' ' + git worktree add --force test-branch +' + test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' ' git worktree add --detach mish/mash && test_must_fail git rev-parse mash -- &&
Thomas Gummerer (6):
worktree: improve message when creating a new worktree
worktree: be clearer when "add" dwim-ery kicks in
worktree: remove force_new_branch from struct add_opts
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
t2025: rename now outdated branch name
Documentation/git-worktree.txt | 9 ++++-- builtin/worktree.c | 64 +++++++++++++++++++++++++++++++----------- t/t2025-worktree-add.sh | 23 +++++++++++---- 3 files changed, 72 insertions(+), 24 deletions(-)
Signature · 1 line
2.16.1.77.g8685934aa2
PATCH v5 1/6 worktree: improve message when creating a new worktree
Currently 'git worktree add' produces output like the following, when '--no-checkout' is not given:
Preparing foo (identifier foo)
HEAD is now at 26da330922 <title>
where the first line is written to stderr, and the second line coming from 'git reset --hard' is written to stdout, even though both lines are supposed to tell the user what has happened. In addition to someone not familiar with 'git worktree', this might seem as if the current HEAD was modified, not the HEAD in the new working tree.
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
The identifier is also not particularly relevant for the user at the moment, as it's only used internally to distinguish between different worktrees that have the same $(basename <path>).
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call quiet, and printing the message directly from the builtin command instead.
--- builtin/worktree.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
builtin/worktree.c +10 -3
@@ -303,8 +303,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 ;
@@ -320,10 +318,19 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +fprintf (stderr,_ ("new worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); +if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args); -argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_pushl (&cp.args,"reset" ,"--hard" ,"--quiet" ,NULL ); cp.env = child_env.argv; ret =run_command (&cp);if (ret)
Signature · 1 line
2.16.1.77.g8685934aa2
PATCH v5 2/6 worktree: be clearer when "add" dwim-ery kicks in
In reply to Thomas GummererCurrently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in the next commit, which introduces a new kind of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
--- builtin/worktree.c | 3 +++ 1 file changed, 3 insertions(+)
builtin/worktree.c +3 -0
@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +if (opts->new_branch) +fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch); +fprintf (stderr,_ ("new worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
Signature · 1 line
2.16.1.77.g8685934aa2
Re: [PATCH v5 2/6] worktree: be clearer when "add" dwim-ery kicks in
Quoted 5 lines · Thomas Gummerer
Currently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Failed to notice this last time around: s/from/for/
Perhaps Junio can tweak it when queuing.
Quoted 4 lines
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in the next commit, which introduces a new kind
It's no longer the _next_ commit which does this. Perhaps say instead "a subsequent commit". (Again, perhaps Junio can tweak it since it's not worth a re-roll.)
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 319
Quoted 10 lines
of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c +2
-0@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname, +if (opts->new_branch) +fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch);
I didn't think of this before, but what happens with the original dwim-ery you added which checks out a remote branch matching the worktree name if no such local branch exists? I was wondering if we'd want to see a distinct and more informative message in that case, such as "creating branch '%s' from remote '%s'" or something. However, I see that we're already getting a message:
Branch 'foo' set up to track remote branch 'foo' from 'origin'. creating branch 'foo'
new worktree HEAD is now at d3adb33f boople
which is a bit weird since tracking appears to be set up before the branch itself is created. I thought that this was another UI regression of the sort I mentioned in [1], however, the messages were out of order even before the current patch series:
Branch 'foo' set up to track remote branch 'foo' from 'origin'. Preparing foo (identifier foo)
Checking out files: 100% (9999/9999), done.
HEAD is now at d3adb33f boople
This highlights another regression introduced by this series. Patch 1/6 suppresses the "Checking out files:" message, which is a bit unfortunate for decent sized worktrees. I'm not sure I'm entirely happy about that loss. Thoughts?
[1]: https://public-inbox.org/git/CAPig+cT8i9L9kbhx=b0sG4_QYNdoEDPW-1xypM9xzBqPmqR__Q@mail.gmail.com/
Quoted 2 lines
builtin/worktree.c +0
-0fprintf (stderr,_ ("new worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
Re: [PATCH v5 2/6] worktree: be clearer when "add" dwim-ery kicks in
Quoted 11 lines · Eric Sunshine
Quoted 5 lines · Thomas Gummerer
Currently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one from them.
Failed to notice this last time around: s/from/for/
Perhaps Junio can tweak it when queuing.
Quoted 8 lines
Quoted 4 lines
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in the next commit, which introduces a new kind
It's no longer the _next_ commit which does this. Perhaps say instead "a subsequent commit". (Again, perhaps Junio can tweak it since it's not worth a re-roll.)
Right, will fix.
Quoted 18 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 319
Quoted 10 lines
of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c +2
-0@@ -318,6 +318,9 @@ static int add_worktree(const char *path, const char *refname, +if (opts->new_branch) +fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch);I didn't think of this before, but what happens with the original dwim-ery you added which checks out a remote branch matching the worktree name if no such local branch exists? I was wondering if we'd want to see a distinct and more informative message in that case, such as "creating branch '%s' from remote '%s'" or something. However, I see that we're already getting a message:
Quoted 8 lines
Branch 'foo' set up to track remote branch 'foo' from 'origin'. creating branch 'foo'
new worktree HEAD is now atd3adb33fbooplewhich is a bit weird since tracking appears to be set up before the branch itself is created. I thought that this was another UI regression of the sort I mentioned in [1], however, the messages were out of order even before the current patch series:
Yeah I agree this is a bit odd. I did think about this, and the main reason why I didn't change this by passing the '--quiet' flag to 'git branch' which we call in the 'add()' function is to keep the information about the remote tracking.
Looking at where this information actually comes from, there's multiple different ways we'd have to distinguish to print this information properly.
Now that I put some more thought into this, I think there are a couple of ways to solve this, the easiest and cleanest of which would probably be to run 'git branch' through 'pipe_command', save the output and print it after the "creating branch 'foo'" message.
Quoted 9 lines
Branch 'foo' set up to track remote branch 'foo' from 'origin'. Preparing foo (identifier foo)
Checking out files: 100% (9999/9999), done.
HEAD is now atd3adb33fboopleThis highlights another regression introduced by this series. Patch 1/6 suppresses the "Checking out files:" message, which is a bit unfortunate for decent sized worktrees. I'm not sure I'm entirely happy about that loss. Thoughts?
Tbh I never really noticed the "Checking out files" output, because the repositories I used day to day are usually of a smaller size, where checking out the files takes less than a second, so I would never even notice this output.
But while I don't care about it, others may, so I think it would be better to preserve this output. Maybe the best way to do that would be to not use 'run_command' to run 'git reset --hard', but to instead replace that with the internal functions.
I haven't actually looked at how hard this would be, but I'm guessing this may be the best option to avoid this UI regression. I'll play with this some more and see what I can come up with, and send a re-roll hopefully in a few days.
Thanks for your review!
Quoted 4 lines
[1]: https://public-inbox.org/git/CAPig+cT8i9L9kbhx=b0sG4_QYNdoEDPW-1xypM9xzBqPmqR__Q@mail.gmail.com/
Quoted 2 lines
builtin/worktree.c +0
-0fprintf (stderr,_ ("new worktree HEAD is now at %s" ),find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
PATCH v5 3/6 worktree: remove force_new_branch from struct add_opts
In reply to Thomas GummererThe 'force_new_branch' flag in 'struct add_opts' is only used inside the add function, where we already have the same information stored in the 'new_branch_force' variable. Avoid that unnecessary duplication.
--- builtin/worktree.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
builtin/worktree.c +2 -4
@@ -28,7 +28,6 @@ struct add_opts {int checkout;int keep_locked;const char *new_branch; -int force_new_branch; };static int show_only;
@@ -405,8 +404,7 @@ 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;
@@ -450,7 +448,7 @@ static int add(int ac, const char **av, const char *prefix)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, branch);
Signature · 1 line
2.16.1.77.g8685934aa2
Re: [PATCH v5 3/6] worktree: remove force_new_branch from struct add_opts
Quoted 3 lines · Thomas Gummerer
The '
force_new_branch' flag in 'structadd_opts' is only used inside the add function, where we already have the same information stored in the 'new_branch_force' variable. Avoid that unnecessary duplication.
When I was reviewing your original dwim-ery series (inferring 'foo' from 'origin/foo'), I noticed that 'struct add_opts' had accumulated a number of unnecessary members over time, including this one. My plan was to submit a patch removing all those pointless members after your dwim-ery series settled, however, I never got around to it.
This patch might be a good opportunity for doing that cleanup wholesale, removing all unneeded members rather than just the one. (If so, you'd probably want to move to this patch to the front of the series as cleanup before the meatier changes.) Anyhow, it's just a thought; feel free to respond with "it's outside the scope of this series" if you're not interested in doing it.
Quoted 1 line
Signed-off-by Thomas Gummerer <[email protected]>
Re: [PATCH v5 3/6] worktree: remove force_new_branch from struct add_opts
Quoted 10 lines · Eric Sunshine
Quoted 3 lines · Thomas Gummerer
The '
force_new_branch' flag in 'structadd_opts' is only used inside the add function, where we already have the same information stored in the 'new_branch_force' variable. Avoid that unnecessary duplication.When I was reviewing your original dwim-ery series (inferring 'foo' from 'origin/foo'), I noticed that 'struct
add_opts' had accumulated a number of unnecessary members over time, including this one. My plan was to submit a patch removing all those pointless members after your dwim-ery series settled, however, I never got around to it.
Ah right, I didn't look at them in detail, so I failed to notice that. While I'm already in the area I may as well do that, thanks for the suggestion!
Quoted 8 lines
This patch might be a good opportunity for doing that cleanup wholesale, removing all unneeded members rather than just the one. (If so, you'd probably want to move to this patch to the front of the series as cleanup before the meatier changes.) Anyhow, it's just a thought; feel free to respond with "it's outside the scope of this series" if you're not interested in doing it.
Quoted 1 line
Signed-off-by Thomas Gummerer <[email protected]>
PATCH v5 4/6 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
builtin/worktree.c +17 -10
@@ -366,6 +366,20 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static const char *dwim_branch (const char *path,struct add_opts *opts) +{ +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); +return remote; + } +return NULL ; +} +static int add (int ac,const char **av,const char *prefix) {struct add_opts opts;
@@ -417,16 +431,9 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !opts.new_branch && !opts.detach) { -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; - } +const char *dwim_branchname =dwim_branch (path, &opts); +if (dwim_branchname) + branch = dwim_branchname; }if (ac ==2 && !opts.new_branch && !opts.detach) {
Signature · 1 line
2.16.1.77.g8685934aa2
Re: [PATCH v5 4/6] worktree: factor out dwim_branch function
Quoted 13 lines · Thomas Gummerer
Factor out a
dwim_branchfunction, 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.[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c +0
-3@@ -417,16 +431,9 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !opts.new_branch && !opts.detach) { -int n; -const char *s =worktree_basename (path, &n); - opts.new_branch =xstrndup (s, n);
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 434
Quoted 10 lines
builtin/worktree.c +3
-7-if (guess_remote) { -struct object_id oid; -const char *remote = -unique_tracking_name (opts.new_branch, &oid); -if (remote) - branch = remote; - } +const char *dwim_branchname =dwim_branch (path, &opts); +if (dwim_branchname) + branch = dwim_branchname;
I don't care strongly but the name 'dwim_branchname' is awfully long in a context where it's assigned the result of a call to dwim_branch(). In this tiny code block, a short and sweet name 's' would be easy to grok; there'd be no confusion.
Anyhow, it's subjective and not at all worth a re-roll.
PATCH v5 5/6 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 19 +++++++++++++++++-- t/t2025-worktree-add.sh | 19 ++++++++++++++++--- 3 files changed, 40 insertions(+), 7 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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::
builtin/worktree.c +17 -2
@@ -28,6 +28,7 @@ struct add_opts {int checkout;int keep_locked;const char *new_branch; +int checkout_existing_branch; };static int show_only;
@@ -317,7 +318,10 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; -if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf_ln (stderr,_ ("checking out branch '%s'" ), + refname); +else if (opts->new_branch)fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch); fprintf(stderr, _("new worktree HEAD is now at %s"),
@@ -370,7 +374,18 @@ static const char *dwim_branch(const char *path, struct add_opts *opts) {int n;const char *s =worktree_basename (path, &n); - opts->new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + opts->checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + opts->new_branch = branchname;if (guess_remote) {struct object_id oid; const char *remote =
t/t2025-worktree-add.sh +16 -3
@@ -198,13 +198,26 @@ test_expect_success '"add" with <branch> omitted' ' test_cmp_rev HEAD bat ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" checks out existing branch of dwimd name' ' test_commit c1 && test_commit c2 && git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +' + +test_expect_success '"add" auto-vivify 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 worktree add --force test-branch ' test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
Signature · 1 line
2.16.1.77.g8685934aa2
Quoted 14 lines · Thomas Gummerer
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.
[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 320
Quoted 4 lines
builtin/worktree.c +3
-1@@ -317,7 +318,10 @@ static int add_worktree(const char *path, const char *refname, -if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf_ln (stderr,_ ("checking out branch '%s'" ), + refname);
This fprintf_ln() can easily fit on one line; no need to wrap 'refname' to the next one.
Not worth a re-roll, though.
Quoted 8 lines
builtin/worktree.c +1
-0+else if (opts->new_branch)fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch);t/t2025-worktree-add.sh +1
-1@@ -198,13 +198,26 @@ test_expect_success '"add" with <branch> omitted' ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" checks out existing branch of dwimd name' ' test_commit c1 && test_commit c2 &&
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 208
Quoted 10 lines
t/t2025-worktree-add.sh +6
-2git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +'
Looking at this more closely, once it stops being a "don't clobber precious branch" test, it's doing more than it needs to do, thus is confusing for future readers. The setup -- creating two commits and making "precious" point one commit back -- was very intentional and allowed the test to verify not only that the worktree wasn't created but that "precious" didn't change to reference a different commit. However, _none_ of this matters once it becomes a dwim'ing test; the unnecessary code is confusing since it doesn't make sense in the context of dwim'ing. I _think_ the entire test can collapse to:
git branch existing &&
git worktree add existing &&
(
cd existing &&
test_cmp_rev existing HEAD
)In other words, this patch should drop the "precious" test altogether and just introduce a new dwim'ing test (and drop patch 6/6).
I do think that with the potential confusion to future readers, this does deserve a re-roll.
Quoted 9 lines
t/t2025-worktree-add.sh +8
-0+test_expect_success '"add" auto-vivify 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 worktree add --force test-branch '
Quoted 61 lines · Eric Sunshine
Quoted 14 lines · Thomas Gummerer
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.
[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 320
Quoted 4 lines
builtin/worktree.c +3
-1@@ -317,7 +318,10 @@ static int add_worktree(const char *path, const char *refname, -if (opts->new_branch) +if (opts->checkout_existing_branch) +fprintf_ln (stderr,_ ("checking out branch '%s'" ), + refname);This
fprintf_ln()can easily fit on one line; no need to wrap 'refname' to the next one.Not worth a re-roll, though.
Quoted 8 lines
builtin/worktree.c +1
-0+else if (opts->new_branch)fprintf_ln (stderr,_ ("creating branch '%s'" ), opts->new_branch);t/t2025-worktree-add.sh +1
-1@@ -198,13 +198,26 @@ test_expect_success '"add" with <branch> omitted' ' -test_expect_success '"add" auto-vivify does not clobber existing branch' ' +test_expect_success '"add" checks out existing branch of dwimd name' ' test_commit c1 && test_commit c2 &&t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 208
Quoted 10 lines
t/t2025-worktree-add.sh +6
-2git branch precious HEAD~1 && - test_must_fail git worktree add precious && + git worktree add precious && test_cmp_rev HEAD~1 precious && - test_path_is_missing precious + ( + cd precious && + test_cmp_rev precious HEAD + ) +'Looking at this more closely, once it stops being a "don't clobber precious branch" test, it's doing more than it needs to do, thus is confusing for future readers. The setup -- creating two commits and making "precious" point one commit back -- was very intentional and allowed the test to verify not only that the worktree wasn't created but that "precious" didn't change to reference a different commit. However, _none_ of this matters once it becomes a dwim'ing test; the unnecessary code is confusing since it doesn't make sense in the context of dwim'ing. I _think_ the entire test can collapse to:
git branch existing && git worktree add existing && ( cd existing && test_cmp_rev existing HEAD )
Quoted 5 lines
In other words, this patch should drop the "precious" test altogether and just introduce a new dwim'ing test (and drop patch 6/6).
I do think that with the potential confusion to future readers, this does deserve a re-roll.
Hmm I do think that it's important to have the branch we create the new working tree from different from the branch of the main working tree, to make sure we don't accidentally overwrite an existing branch, and to show that the new branch is checked out.
Maybe I'm overly paranoid though?
Quoted 9 lines
Quoted 9 lines
t/t2025-worktree-add.sh +8
-0+test_expect_success '"add" auto-vivify 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 worktree add --force test-branch '
PATCH v5 6/6 t2025: rename now outdated branch name
In reply to Thomas GummererBefore the previous commit, the branch named precious was used to check that 'git worktree' wouldn't clobber the branch. While 'git worktree' still shouldn't (and doesn't) modify the branch, that's no longer the main thing the test is checking.
Rename the branch to avoid making future readers wonder why this particular branch is more "precious" than others.
--- t/t2025-worktree-add.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
t/t2025-worktree-add.sh +5 -5
@@ -201,12 +201,12 @@ test_expect_success '"add" with <branch> omitted' ' test_expect_success '"add" checks out existing branch of dwimd name' ' test_commit c1 && test_commit c2 && - git branch precious HEAD~1 && - git worktree add precious && - test_cmp_rev HEAD~1 precious && + git branch dwim HEAD~1 && + git worktree add dwim && + test_cmp_rev HEAD~1 dwim && ( - cd precious && - test_cmp_rev precious HEAD + cd dwim && + test_cmp_rev dwim HEAD ) '
Signature · 1 line
2.16.1.77.g8685934aa2
Quoted 3 lines · Thomas Gummerer
Thanks Eric for the review of the previous round and Duy and Junio for additional comments.
This round should address all of Eric's comments from the previous round.
Thanks, it appears to cover my review comments from the previous round. I do have some additional comments on this round (which I could have raised with the previous round if I had thought of them at the time).
Quoted 4 lines
As explained in more detail in a reply to the review comment directly, I did not add an enum to 'struct
add_opts', for 'force_new_branch' and 'checkout_existing_branch', but instead removed 'force_new_branch' from the struct as it's not required.
Makes sense. In fact, I had thoughts along these lines during your previous dwim-ery series. See my comments on patch 3/6.
Quoted 3 lines
The rest of the updates are mainly in the user facing messages, documentation and one added test.
Interdiff below:
The interdiff looks sane. Unfortunately, due to UI regressions, I'm having second thoughts about whether this series is going in the right direction. See my comments on patch 2/6.
Quoted 18 lines · Eric Sunshine
Quoted 3 lines · Thomas Gummerer
Thanks Eric for the review of the previous round and Duy and Junio for additional comments.
This round should address all of Eric's comments from the previous round.Thanks, it appears to cover my review comments from the previous round. I do have some additional comments on this round (which I could have raised with the previous round if I had thought of them at the time).
Quoted 4 lines
As explained in more detail in a reply to the review comment directly, I did not add an enum to 'struct
add_opts', for 'force_new_branch' and 'checkout_existing_branch', but instead removed 'force_new_branch' from the struct as it's not required.Makes sense. In fact, I had thoughts along these lines during your previous dwim-ery series. See my comments on patch 3/6.
Quoted 7 lines
Quoted 3 lines
The rest of the updates are mainly in the user facing messages, documentation and one added test.
Interdiff below:The interdiff looks sane. Unfortunately, due to UI regressions, I'm having second thoughts about whether this series is going in the right direction. See my comments on patch 2/6.
Thanks for your reviews of this series! As I mentioned in the reply there I'm going to see whether or not I can fix those regressions (hopefully I can :)), and send a re-roll.
v6 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererThanks Eric for the review of the last round.
Previous rounds are at <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]> and [email protected].
This round should fix all the UI issues Eric found in the last round.
The changes I made in a bit more detail:
- in addition to removing the 'force_new_branch' flag from 'struct add_opts', also remove the 'new_branch' member, which is local to the 'add()' function. The other four members are needed in the
'worktree_add()' function, so I left them there. This patch is now
the first patch in the series.
- added a new commit introducing a new hidden --show-new-head-line flag in 'git reset'. This is used to suppress the "HEAD is now at ..." line that 'git reset --hard' usually prints, so we can replace it with our own "New worktree HEAD is now at ..." line instead, while keeping the progress indicator for larger repositories.
I guess this may be the most controversial bit at this point, as we'd be adding an option for internal use only. Not sure how we feel about that. But short of going back to the old output format, I don't see a good option to make this work otherwise. I tried re-using internal functions for this, but until we have 'struct repository' everywhere, that's going to be quite hard.
- Print the "Creating branch ..." and "Checking out branch ..." messages earlier in the process. This is mainly to avoid the out of order "Branch '...' now set up to track ..." message.
- Various commit message and style cleanups
Note that these fixes are quite differently executed than I had imagined them in the reply to the review comments, as things didn't work as I had imagined. The UI problems should be fixed now nonetheless :)
Some examples of the new UI behaviour here for reference:
- guess-remote mode
$ git worktree add --guess-remote ../next
Creating branch 'next'
Branch 'next' set up to track remote branch 'next' from 'origin'.
New worktree HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test
Creating branch 'test'
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- new dwim (check out existing branch)
$ git worktree add ../test
Checking out branch 'test'
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- no new branch created
$ git worktree add ../test2 origin/master
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- large repository (with original dwim)
$ g worktree add ../test
Creating branch 'test'
Checking out files: 100% (62915/62915), done.
New worktree HEAD is now at c2a9838452a4 Merge tag 'for-4.16/dm-fixes-4' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dmCompare this to the old UI (new dwim omitted, as there's no old version of that):
- guess-remote mode
$ git worktree add --guess-remote ../next
Branch 'next' set up to track remote branch 'next' from 'origin'.
Preparing ../next (identifier next)
HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test
Preparing ../test (identifier test)
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- no new branch created
$ git worktree add ../test2 origin/master
Preparing ../test2 (identifier test2)
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- large repository
$ git worktree add ../test
Preparing ../test (identifier test)
Checking out files: 100% (62915/62915), done.
HEAD is now at c2a9838452a4 Merge tag 'for-4.16/dm-fixes-4' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dmThe one thing we are loosing is a context line before "Checking out files:", if no new branch is created. Personally I feel like that's acceptable, as the user just used the 'git worktree add' command, so it should be intuitive where those files are being checked out.
We could also print "Preparing worktree <path>" as a line in the beginning (without mentioning the identifier, so we can print it in the 'add()' function), but I don't feel like that's worth spending the extra screen estate. I don't feel strongly about that though, so if someone has a moderately strong preference for that line being there, I'm happy to add it.
Interdiff below:
builtin/reset.c +4 -1
@@ -288,6 +288,7 @@ static int git_reset_config(const char *var, const char *value, void *cb)int cmd_reset (int argc,const char **argv,const char *prefix) {int reset_type =NONE , update_ref_status =0 , quiet =0 ; +int show_new_head_line =1 ;int patch_mode =0 , unborn;const char *rev;struct object_id oid;
@@ -310,6 +311,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)OPT_BOOL ('p' ,"patch" , &patch_mode,N_ ("select hunks interactively" )),OPT_BOOL ('N' ,"intent-to-add" , &intent_to_add,N_ ("record only the fact that removed paths will be added later" )), +OPT_HIDDEN_BOOL (0 ,"show-new-head-line" , &show_new_head_line,N_ ("show information on the new HEAD" )), OPT_END() };
@@ -403,7 +405,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)* switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status =reset_refs (rev, &oid); -if (reset_type ==HARD && !update_ref_status && !quiet) +if (reset_type ==HARD && !update_ref_status && !quiet && + show_new_head_line)print_new_head_line (lookup_commit_reference (&oid)); }if (!pathspec.nr)
builtin/worktree.c +36 -35
163 lines not on this page · raw message
t/t2025-worktree-add.sh +1 -3
@@ -199,8 +199,6 @@ test_expect_success '"add" with <branch> omitted' ' ' test_expect_success '"add" checks out existing branch of dwimd name' ' - test_commit c1 && - test_commit c2 && git branch dwim HEAD~1 && git worktree add dwim && test_cmp_rev HEAD~1 dwim &&
@@ -210,7 +208,7 @@ test_expect_success '"add" checks out existing branch of dwimd name' ' ) ' -test_expect_success '"add" auto-vivify fails with checked out branch' ' +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
t/t7102-reset.sh +5 -0
@@ -568,4 +568,9 @@ test_expect_success 'reset --mixed sets up work tree' ' test_cmp expect actual ' +test_expect_success 'reset --no-show-new-head-line suppresses "HEAD is now at" output' ' + git reset --hard --no-show-new-head-line HEAD >actual && + ! grep "HEAD is now at" <actual +' + test_done
Thomas Gummerer (6):
worktree: remove extra members from struct add_opts
reset: introduce show-new-head-line option
worktree: improve message when creating a new worktree
worktree: be clearer when "add" dwim-ery kicks in
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 ++++- builtin/reset.c | 5 ++- builtin/worktree.c | 85 ++++++++++++++++++++++++++++-------------- t/t2025-worktree-add.sh | 25 +++++++++---- t/t7102-reset.sh | 5 +++ 5 files changed, 92 insertions(+), 37 deletions(-)
Signature · 1 line
2.16.1.78.g383dce0c66
PATCH v6 1/6 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.
--- builtin/worktree.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-)
builtin/worktree.c +15 -17
@@ -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 <branch> 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,32 @@ 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);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 +428,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" )); }
Signature · 1 line
2.16.1.78.ga2082135d8
PATCH v6 2/6 reset: introduce show-new-head-line option
In reply to Thomas GummererIntroduce a new --show-new-head-line command line option, that determines whether the "HEAD is now at ..." message is printed or not. It is enabled by default to preserve the current behaviour.
It will be used in a subsequent commit to disable printing the "HEAD is now at ..." line in 'git worktree add', so it can be replaced with a custom one, that explains the behaviour better.
We cannot just pass the --quite flag from 'git worktree add', as that would also hide progress output when checking out large working trees, which is undesirable.
As this option is only for internal use, which we probably don't want to advertise to our users, at least until there's a need for it, make it a hidden flag.
--- builtin/reset.c | 5 ++++- t/t7102-reset.sh | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-)
builtin/reset.c +4 -1
@@ -288,6 +288,7 @@ static int git_reset_config(const char *var, const char *value, void *cb)int cmd_reset (int argc,const char **argv,const char *prefix) {int reset_type =NONE , update_ref_status =0 , quiet =0 ; +int show_new_head_line =1 ;int patch_mode =0 , unborn;const char *rev;struct object_id oid;
@@ -310,6 +311,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)OPT_BOOL ('p' ,"patch" , &patch_mode,N_ ("select hunks interactively" )),OPT_BOOL ('N' ,"intent-to-add" , &intent_to_add,N_ ("record only the fact that removed paths will be added later" )), +OPT_HIDDEN_BOOL (0 ,"show-new-head-line" , &show_new_head_line,N_ ("show information on the new HEAD" )), OPT_END() };
@@ -403,7 +405,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)* switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status =reset_refs (rev, &oid); -if (reset_type ==HARD && !update_ref_status && !quiet) +if (reset_type ==HARD && !update_ref_status && !quiet && + show_new_head_line)print_new_head_line (lookup_commit_reference (&oid)); }if (!pathspec.nr)
t/t7102-reset.sh +5 -0
@@ -568,4 +568,9 @@ test_expect_success 'reset --mixed sets up work tree' ' test_cmp expect actual ' +test_expect_success 'reset --no-show-new-head-line suppresses "HEAD is now at" output' ' + git reset --hard --no-show-new-head-line HEAD >actual && + ! grep "HEAD is now at" <actual +' + test_done
Signature · 1 line
2.16.1.78.ga2082135d8
Re: [PATCH v6 2/6] reset: introduce show-new-head-line option
Quoted 8 lines · Thomas Gummerer
Introduce a new --show-new-head-line command line option, that determines whether the "HEAD is now at ..." message is printed or not. It is enabled by default to preserve the current behaviour.
It will be used in a subsequent commit to disable printing the "HEAD is now at ..." line in 'git worktree add', so it can be replaced with a custom one, that explains the behaviour better.
Quoted 7 lines
We cannot just pass the --quite flag from 'git worktree add', as that would also hide progress output when checking out large working trees, which is undesirable.
As this option is only for internal use, which we probably don't want to advertise to our users, at least until there's a need for it, make it a hidden flag.
As a temporary hack, adding something like this may be OK, but in the longer run, I think we should (at least) consider refactoring "reset --hard" codepath to a freestanding and silent helper function so that both cmd_reset() and add_worktree() can call it and report the outcome in their own phrasing.
And I support the decision not to advertise this as a new feature or an option by implementing it as hidden-bool.
This is completely offtopic tangent, but I wonder how hidden-bool or hidden options[] element in general interacts with the recent addition of helping command line completion. Are we already doing the right thing?
Re: [PATCH v6 2/6] reset: introduce show-new-head-line option
Quoted 18 lines · Junio C Hamano
Quoted 8 lines · Thomas Gummerer
Introduce a new --show-new-head-line command line option, that determines whether the "HEAD is now at ..." message is printed or not. It is enabled by default to preserve the current behaviour.
It will be used in a subsequent commit to disable printing the "HEAD is now at ..." line in 'git worktree add', so it can be replaced with a custom one, that explains the behaviour better.
Quoted 7 lines
We cannot just pass the --quite flag from 'git worktree add', as that would also hide progress output when checking out large working trees, which is undesirable.
As this option is only for internal use, which we probably don't want to advertise to our users, at least until there's a need for it, make it a hidden flag.
Quoted 5 lines
As a temporary hack, adding something like this may be OK, but in the longer run, I think we should (at least) consider refactoring "reset --hard" codepath to a freestanding and silent helper function so that both
cmd_reset()andadd_worktree()can call it and report the outcome in their own phrasing.
I agree that refactoring this would have been nicer. The biggest obstacle there is that the 'git reset' needs to run with a different "GIT_DIR" and "GIT_WORK_TREE", which is not easy to accomplish in the current code.
It does seem like something that would be much easier once we have 'struct repository' being passed through everywhere. I'd rather wait a bit more for the 'struct repository' series to land, which will hopefully make the refactoring easier (although I didn't have time to follow the series closely).
Quoted 7 lines
And I support the decision not to advertise this as a new feature or an option by implementing it as hidden-bool.
This is completely offtopic tangent, but I wonder how hidden-bool or hidden options[] element in general interacts with the recent addition of helping command line completion. Are we already doing the right thing?
Re: [PATCH v6 2/6] reset: introduce show-new-head-line option
In reply to Junio C HamanoQuoted 4 lines · Junio C Hamano
This is completely offtopic tangent, but I wonder how hidden-bool or hidden options[] element in general interacts with the recent addition of helping command line completion. Are we already doing the right thing?
I had a quick look at this, and it looks like we're doing the right thing here. The following snipped from the 'show_gitcomp' function in parse-options.c:
Quoted 7 lines
for (; opts->type != OPTION_END; opts++) {
const char *suffix = "";
if (!opts->long_name)
continue;
if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
continue;So if the PARSE_OPT_HIDDEN flag is given, we skip printing the option, which seems like the right thing to do.
Re: [PATCH v6 2/6] reset: introduce show-new-head-line option
In reply to Thomas Gummerert/t7102-reset.sh test_expect_success 'reset --mixed sets up work tree' ' · line 574
Quoted 4 lines · Thomas Gummerer
t/t7102-reset.sh +4
-0+test_expect_success 'reset --no-show-new-head-line suppresses "HEAD is now at" output' ' + git reset --hard --no-show-new-head-line HEAD >actual && + ! grep "HEAD is now at" <actual +'
As builtin/reset.c::print_new_head_line() does this:
printf(_("HEAD is now at %s"), hex);this needs to use "test_i18ngrep !" instead, no?
Re: [PATCH v6 2/6] reset: introduce show-new-head-line option
Quoted 9 lines · Junio C Hamano
t/t7102-reset.sh test_expect_success 'reset --mixed sets up work tree' ' · line 574
Quoted 4 lines · Thomas Gummerer
t/t7102-reset.sh +4
-0+test_expect_success 'reset --no-show-new-head-line suppresses "HEAD is now at" output' ' + git reset --hard --no-show-new-head-line HEAD >actual && + ! grep "HEAD is now at" <actual +'As builtin/reset.c::print_new_head_line() does this:
Quoted 3 lines
printf(_("HEAD is now at %s"), hex);this needs to use "
test_i18ngrep!" instead, no?
Ah yes you're right. Thanks for catching this.
PATCH v6 3/6 worktree: improve message when creating a new worktree
In reply to Thomas GummererCurrently 'git worktree add' produces output like the following, when '--no-checkout' is not given:
Preparing foo (identifier foo)
HEAD is now at 26da330922 <title>
where the first line is written to stderr, and the second line coming from 'git reset --hard' is written to stdout, even though both lines are supposed to tell the user what has happened. In addition to someone not familiar with 'git worktree', this might seem as if the current HEAD was modified, not the HEAD in the new working tree.
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
The identifier is also not particularly relevant for the user at the moment, as it's only used internally to distinguish between different worktrees that have the same $(basename <path>).
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call not print the "HEAD is now at ..." line using the newly introduced flag from the previous commit, and printing the message directly from the builtin command instead.
--- builtin/worktree.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
builtin/worktree.c +10 -2
@@ -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 ;
@@ -322,12 +320,22 @@ static int add_worktree(const char *path, const char *refname, cp.argv =NULL ;argv_array_clear (&cp.args);argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_push (&cp.args,"--no-show-new-head-line" ); cp.env = child_env.argv; ret =run_command (&cp);if (ret) goto done; } +fprintf (stderr,_ ("New worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); + +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); + is_junk =0 ;FREE_AND_NULL (junk_work_tree);FREE_AND_NULL (junk_git_dir);
Signature · 1 line
2.16.1.78.ga2082135d8
Re: [PATCH v6 3/6] worktree: improve message when creating a new worktree
Quoted 20 lines · Thomas Gummerer
[...]
Fix these inconsistencies, and no longer show the identifier by making the 'git reset --hard' call not print the "HEAD is now at ..." line using the newly introduced flag from the previous commit, and printing the message directly from the builtin command instead.Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c +3
-0@@ -322,12 +320,22 @@ static int add_worktree(const char *path, const char *refname,argv_array_pushl (&cp.args,"reset" ,"--hard" ,NULL ); +argv_array_push (&cp.args,"--no-show-new-head-line" ); cp.env = child_env.argv; ret =run_command (&cp);if (ret) goto done; } +fprintf (stderr,_ ("New worktree HEAD is now at %s" ), +find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV ));
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 339
Quoted 10 lines
builtin/worktree.c +7
-0+ +strbuf_reset (&sb); +pp_commit_easy (CMIT_FMT_ONELINE , commit, &sb); +if (sb.len >0 ) +fprintf (stderr," %s" , sb.buf); +fputc ('\n' , stderr); + is_junk =0 ;FREE_AND_NULL (junk_work_tree);FREE_AND_NULL (junk_git_dir);
Generally speaking, code such as this probably ought to be inserted outside of the is_junk={1,0} context in order to keep that critical section as small as possible. However, as mentioned in my response to the v6 cover letter[1], I think this chunk of new code can just go away entirely if git-reset is made to print the customized message on git-worktree's behalf.
[1]: https://public-inbox.org/git/CAPig+cQ8VzDycUMo-QOexNDBgQGEGj2BPmPa-Y0vhGCt_brbhg@mail.gmail.com/
PATCH v6 4/6 worktree: be clearer when "add" dwim-ery kicks in
In reply to Thomas GummererCurrently there is no indication in the "git worktree add" output that a new branch was created. This would be especially useful information in the case where the dwim of "git worktree add <path>" kicks in, as the user didn't explicitly ask for a new branch, but we create one for them.
Print some additional output showing that a branch was created and the branch name to help the user.
This will also be useful in a subsequent commit, which introduces a new kind of dwim-ery of checking out the branch if it exists instead of refusing to create a new worktree in that case, and where it's nice to tell the user which kind of dwim-ery kicked in.
--- builtin/worktree.c | 2 ++ 1 file changed, 2 insertions(+)
builtin/worktree.c +2 -0
@@ -444,6 +444,8 @@ static int add(int ac, const char **av, const char *prefix)if (new_branch) {struct child_process cp =CHILD_PROCESS_INIT ; + +fprintf_ln (stderr,_ ("Creating branch '%s'" ), new_branch); cp.git_cmd =1 ;argv_array_push (&cp.args,"branch" );if (new_branch_force)
Signature · 1 line
2.16.1.78.ga2082135d8
PATCH v6 5/6 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
builtin/worktree.c +17 -10
@@ -363,6 +363,20 @@ static int add_worktree(const char *path, const char *refname,return ret; } +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); +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;
@@ -415,16 +429,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); -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) {
Signature · 1 line
2.16.1.78.ga2082135d8
PATCH v6 6/6 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 22 +++++++++++++++++++--- t/t2025-worktree-add.sh | 25 ++++++++++++++++++------- 3 files changed, 44 insertions(+), 12 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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, a worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository, +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::
builtin/worktree.c +19 -3
@@ -363,11 +363,23 @@ static int add_worktree(const char *path, const char *refname,return ret; } -static const char *dwim_branch (const char *path,const char **new_branch) +static const char *dwim_branch (const char *path,const char **new_branch, +int *checkout_existing_branch) {int n;const char *s =worktree_basename (path, &n); - *new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + *checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + *new_branch = branchname;if (guess_remote) {struct object_id oid; const char *remote =
@@ -385,6 +397,7 @@ static int add(int ac, const char **av, const char *prefix)const char *branch;const char *new_branch =NULL ;const char *opt_track =NULL ; +int checkout_existing_branch =0 ;struct option options[] = {OPT__FORCE (&opts.force,N_ ("checkout <branch> even if already checked out in other worktree" )), OPT_STRING('b', NULL, &new_branch, N_("branch"),
@@ -429,7 +442,8 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !new_branch && !opts.detach) { -const char *s =dwim_branch (path, &new_branch); +const char *s =dwim_branch (path, &new_branch, + &checkout_existing_branch);if (s) branch = s; }
@@ -464,6 +478,8 @@ static int add(int ac, const char **av, const char *prefix)if (run_command (&cp))return -1 ; branch = new_branch; + }else if (checkout_existing_branch) { +fprintf_ln (stderr,_ ("Checking out branch '%s'" ), branch); }else if (opt_track) {die (_ ("--[no-]track can only be used if a new branch is created" )); }
t/t2025-worktree-add.sh +18 -7
@@ -198,13 +198,24 @@ 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 dwim HEAD + ) +' + +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 worktree add --force test-branch ' test_expect_success '"add" no auto-vivify with --detach and <branch> omitted' '
Signature · 1 line
2.16.1.78.ga2082135d8
PATCH v6 1/1 fixup! worktree: teach "add" to check out existing branches
---
So while playing with it a bit more I found one case where the new UI is not ideal and a bit confusing. Namely when the new check out dwim kicks in, but there is already a file/directory at the path we're giving to 'git worktree add'.
In that case something like the following would be printed:
$ g worktree add ../next
Checking out branch 'next'
fatal: '../next' already existsInstead I think we'd just want the error without the "Checking out branch" message, which is what this fixup here does.
One thing that gets a bit strange is that the "Checking out branch" message and the "Creating branch" messages are printed from different places. But without doing quite some refactoring I don't think there's a good way to do that, and I think having the UI do the right thing is more important.
One thing I also noticed is that if a branch is created by 'git worktree add', but we fail, we never clean up that branch again, which I'm not sure is ideal. As a pre-existing problem I'd like to keep fixing that out of the scope of this series though (at least after this series the user would get some output showing that this happened, even when the branch is not set up to track a remote), so I'd like to keep fixing that out of the scope of this series.
Junio: could you please squash this in in 6/6 while queuing? I'd prefer not to re-send the whole series just for fixing this up, but obviously can if that makes your life easier :)
Thanks!
builtin/worktree.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
builtin/worktree.c +4 -4
@@ -27,6 +27,7 @@ struct add_opts {int detach;int checkout;int keep_locked; +int checkout_existing_branch; };static int show_only;
@@ -316,6 +317,8 @@ static int add_worktree(const char *path, const char *refname,if (ret) goto done; +if (opts->checkout_existing_branch) +fprintf_ln (stderr,_ ("Checking out branch '%s'" ), refname);if (opts->checkout) { cp.argv =NULL ;argv_array_clear (&cp.args);
@@ -397,7 +400,6 @@ static int add(int ac, const char **av, const char *prefix)const char *branch;const char *new_branch =NULL ;const char *opt_track =NULL ; -int checkout_existing_branch =0 ;struct option options[] = {OPT__FORCE (&opts.force,N_ ("checkout <branch> even if already checked out in other worktree" )), OPT_STRING('b', NULL, &new_branch, N_("branch"),
@@ -443,7 +445,7 @@ static int add(int ac, const char **av, const char *prefix)if (ac <2 && !new_branch && !opts.detach) {const char *s =dwim_branch (path, &new_branch, - &checkout_existing_branch); + &opts.checkout_existing_branch);if (s) branch = s; }
@@ -478,8 +480,6 @@ static int add(int ac, const char **av, const char *prefix)if (run_command (&cp))return -1 ; branch = new_branch; - }else if (checkout_existing_branch) { -fprintf_ln (stderr,_ ("Checking out branch '%s'" ), branch); }else if (opt_track) {die (_ ("--[no-]track can only be used if a new branch is created" )); }
Signature · 1 line
2.16.1.78.g71f731ae26.dirty
Re: [PATCH v6 6.5/6] fixup! worktree: teach "add" to check out existing branches
Quoted 5 lines · Thomas Gummerer
So while playing with it a bit more I found one case where the new UI is not ideal and a bit confusing. Namely when the new check out dwim kicks in, but there is already a file/directory at the path we're giving to 'git worktree add'.
Quoted 8 lines
In that case something like the following would be printed:
$ g worktree add ../next Checking out branch 'next' fatal: '../next' already existsInstead I think we'd just want the error without the "Checking out branch" message, which is what this fixup here does.
Doesn't the same UI "problem" exist when it creates a new branch?
$ git worktree add ../dwim
Creating branch 'dwim'
fatal: '../dwim' already existsAs you mention below, we don't (yet) clean up the newly-created branch upon failure, so we can't suppress the "Creating branch" message as you suppress the "Checking out branch" message above (since the user needs to know that the new branch exists).
This is making me wonder if "Checking out branch" is perhaps the wrong terminology. What if it said something like this instead:
$ git worktree add ../next
Preparing worktree (branch 'next' <= 'origin/next')
fatal: '../next' already exists $ git worktree add ../gobble
Preparing worktree (new branch 'gobble')
fatal: '../gobble' already existsThis way, we don't need the special case added by this "fixup!" patch. (I'm not wedded to the "Preparing" message but just used it as an example; better suggestions welcome.)
Quoted 5 lines
One thing that gets a bit strange is that the "Checking out branch" message and the "Creating branch" messages are printed from different places. But without doing quite some refactoring I don't think there's a good way to do that, and I think having the UI do the right thing is more important.
The implementation is getting rather ugly, though, especially with these messages being printed by different bits of code like this. worktree.c:add_worktree() was supposed to be the low-level worker; it wasn't intended for it to take on UI duties like this "fixup!" makes it do. UI should be handled by worktree.c:add().
Taking the above "Preparing..." idea into consideration, then it should be possible to sidestep this implementation ugliness, I would think.
Quoted 7 lines
One thing I also noticed is that if a branch is created by 'git worktree add', but we fail, we never clean up that branch again, which I'm not sure is ideal. As a pre-existing problem I'd like to keep fixing that out of the scope of this series though (at least after this series the user would get some output showing that this happened, even when the branch is not set up to track a remote), so I'd like to keep fixing that out of the scope of this series.
Nice idea, but outside the scope of this series, as you mention.
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 319
Quoted 9 lines
builtin/worktree.c +3
-0@@ -27,6 +27,7 @@ struct add_opts {int keep_locked; +int checkout_existing_branch; };@@ -316,6 +317,8 @@ static int add_worktree(const char *path, const char *refname, +if (opts->checkout_existing_branch) +fprintf_ln (stderr,_ ("Checking out branch '%s'" ), refname);if (opts->checkout) {
I'd have expected to see the "if (opts->checkout_existing_branch) fprintf_ln(...)" inside the following "if (opts->checkout)" conditional, though, as noted above, I'm not entirely happy about worktree.c:add_worktree() taking on UI duties.
Quoted 2 lines
builtin/worktree.c +0
-0cp.argv =NULL ;argv_array_clear (&cp.args);
Re: [PATCH v6 6.5/6] fixup! worktree: teach "add" to check out existing branches
Quoted 33 lines · Eric Sunshine
Quoted 5 lines · Thomas Gummerer
So while playing with it a bit more I found one case where the new UI is not ideal and a bit confusing. Namely when the new check out dwim kicks in, but there is already a file/directory at the path we're giving to 'git worktree add'.
Quoted 8 lines
In that case something like the following would be printed:
$ g worktree add ../next Checking out branch 'next' fatal: '../next' already existsInstead I think we'd just want the error without the "Checking out branch" message, which is what this fixup here does.
Doesn't the same UI "problem" exist when it creates a new branch?
$ git worktree add ../dwim Creating branch 'dwim' fatal: '../dwim' already existsAs you mention below, we don't (yet) clean up the newly-created branch upon failure, so we can't suppress the "Creating branch" message as you suppress the "Checking out branch" message above (since the user needs to know that the new branch exists).
This is making me wonder if "Checking out branch" is perhaps the wrong terminology. What if it said something like this instead:
$ git worktree add ../next Preparing worktree (branch 'next' <= 'origin/next') fatal: '../next' already exists
Quoted 7 lines
$ git worktree add ../gobble Preparing worktree (new branch 'gobble') fatal: '../gobble' already existsThis way, we don't need the special case added by this "fixup!" patch. (I'm not wedded to the "Preparing" message but just used it as an example; better suggestions welcome.)
Yeah, I think this looks like another improvement of what I currently have. I'm not sure about the "(branch 'next' <= 'origin/next')" message though, as it doesn't cover all the ways the branch could be set up for tracking the remote branch, e.g. tracking by rebasing, when 'branch.autosetuprebase' is set up.
But how about just "Preparing worktree (new branch 'next')", and then keeping the message from 'git branch' about setting up the remote tracking branch?
Quoted 26 lines
Quoted 5 lines
One thing that gets a bit strange is that the "Checking out branch" message and the "Creating branch" messages are printed from different places. But without doing quite some refactoring I don't think there's a good way to do that, and I think having the UI do the right thing is more important.
The implementation is getting rather ugly, though, especially with these messages being printed by different bits of code like this. worktree.c:add_worktree() was supposed to be the low-level worker; it wasn't intended for it to take on UI duties like this "fixup!" makes it do. UI should be handled by worktree.c:add().
Taking the abonve "Preparing..." idea into consideration, then it should be possible to sidestep this implementation ugliness, I would think.
Quoted 7 lines
One thing I also noticed is that if a branch is created by 'git worktree add', but we fail, we never clean up that branch again, which I'm not sure is ideal. As a pre-existing problem I'd like to keep fixing that out of the scope of this series though (at least after this series the user would get some output showing that this happened, even when the branch is not set up to track a remote), so I'd like to keep fixing that out of the scope of this series.
Nice idea, but outside the scope of this series, as you mention.
Quoted 14 lines
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 319
Quoted 9 lines
builtin/worktree.c +3
-0@@ -27,6 +27,7 @@ struct add_opts {int keep_locked; +int checkout_existing_branch; };@@ -316,6 +317,8 @@ static int add_worktree(const char *path, const char *refname, +if (opts->checkout_existing_branch) +fprintf_ln (stderr,_ ("Checking out branch '%s'" ), refname);if (opts->checkout) {I'd have expected to see the "if (
opts->checkout_existing_branch)fprintf_ln(...)" inside the following "if (opts->checkout)" conditional, though, as noted above, I'm not entirely happy about worktree.c:add_worktree() taking on UI duties.
Fair enough. I didn't quite like the code either. I think what you have above would be much nicer, and I'll implement that in the re-roll.
Quoted 2 lines
Quoted 2 lines
builtin/worktree.c +0
-0cp.argv =NULL ;argv_array_clear (&cp.args);
Re: [PATCH v6 6.5/6] fixup! worktree: teach "add" to check out existing branches
Quoted 18 lines · Thomas Gummerer
Quoted 10 lines · Eric Sunshine
This is making me wonder if "Checking out branch" is perhaps the wrong terminology. What if it said something like this instead:
$ git worktree add ../next Preparing worktree (branch 'next' <= 'origin/next') fatal: '../next' already existsThis way, we don't need the special case added by this "fixup!" patch. (I'm not wedded to the "Preparing" message but just used it as an example; better suggestions welcome.)
Yeah, I think this looks like another improvement of what I currently have. I'm not sure about the "(branch 'next' <= 'origin/next')" message though, as it doesn't cover all the ways the branch could be set up for tracking the remote branch, e.g. tracking by rebasing, when 'branch.autosetuprebase' is set up.
Quoted 3 lines
But how about just "Preparing worktree (new branch 'next')", and then keeping the message from 'git branch' about setting up the remote tracking branch?
Fair enough. Extra annotation (such as "'foo' <= 'bar'" or whatever) can always be added later if someone wants it.
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 63
Quoted 13 lines · Thomas Gummerer
[...]
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.[...]Signed-off-by Thomas Gummerer <[email protected]>---
Documentation/git-worktree.txt +2
-0@@ -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 worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created.
I had a hard time digesting this. I _think_ it wants to say:
If <commit-ish> is omitted and neither -b nor -B nor --detach is used, then, as a convenience, the new worktree is associated with a branch (call it <branch>) named after $(basename <path>).
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 67
Quoted 3 lines
Documentation/git-worktree.txt +2
-0If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository,
Maybe: s/exists in the repository/does exist/
Or: s/.../is a local branch/
Though, the latter may be getting too pedantic.
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 204
Quoted 12 lines
Documentation/git-worktree.txt +3
-0+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).t/t2025-worktree-add.sh +7
-0@@ -198,13 +198,24 @@ test_expect_success '"add" with <branch> omitted' ' +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 dwim HEAD
Nit: Argument order of the two test_cmp_rev() invocations differs.
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 219
Quoted 12 lines
t/t2025-worktree-add.sh +11
-0+ ) +' + +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 worktree add --force test-branch '
Maybe make this last test slightly more robust by having it "git checkout test-branch" before "git worktree add ..." to protect against someone inserting a new test (which checks out some other branch) between these two. Probably not worth a re-roll.
Quoted 17 lines · Eric Sunshine
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 63
Quoted 13 lines · Thomas Gummerer
[...]
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.[...]Signed-off-by Thomas Gummerer <[email protected]>---
Documentation/git-worktree.txt +2
-0@@ -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 worktree with a branch named after +`$(basename <path>)` (call it `<branch>`) is created.I had a hard time digesting this. I _think_ it wants to say:
Quoted 4 lines
If
<commit-ish>is omitted and neither-bnor-Bnor--detachis used, then, as a convenience, the new worktree is associated with a branch (call it<branch>) named after$(basename <path>).
Yeah, this is what it wants to say, and what you have sounds much nicer, will change.
Quoted 33 lines
Documentation/git-worktree.txt $ git worktree add --track -b <branch> <path> <remote>/<branch> · line 67
Quoted 3 lines
Documentation/git-worktree.txt +2
-0If `<branch>` +doesn't exist, a new branch based on HEAD is automatically created as +if `-b <branch>` was given. If `<branch>` exists in the repository,Maybe: s/exists in the repository/does exist/
Or: s/.../is a local branch/Though, the latter may be getting too pedantic.
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 204
Quoted 12 lines
Documentation/git-worktree.txt +3
-0+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).t/t2025-worktree-add.sh +7
-0@@ -198,13 +198,24 @@ test_expect_success '"add" with <branch> omitted' ' +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 dwim HEADNit: Argument order of the two
test_cmp_rev()invocations differs.Quoted 9 lines
t/t2025-worktree-add.sh +9
-0+ ) +' + +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 +' +
Quoted 8 lines
t/t2025-worktree-add.sh test_expect_success '"add" with <branch> omitted' ' · line 219
Quoted 3 lines
t/t2025-worktree-add.sh +2
-0+test_expect_success '"add --force" with existing dwimd name doesnt die' ' + git worktree add --force test-branch 'Maybe make this last test slightly more robust by having it "git checkout test-branch" before "git worktree add ..." to protect against someone inserting a new test (which checks out some other branch) between these two. Probably not worth a re-roll.
As I'll have to re-roll anyway for the other suggestions, I'll change this as well.
Thanks for your review!
Quoted 8 lines · Thomas Gummerer
This round should fix all the UI issues Eric found in the last round. The changes I made in a bit more detail:
- added a new commit introducing a new hidden --show-new-head-line flag in 'git reset'. This is used to suppress the "HEAD is now at ..." line that 'git reset --hard' usually prints, so we can replace it with our own "New worktree HEAD is now at ..." line instead, while keeping the progress indicator for larger repositories.
As with Junio, I'm fine with this hidden option (for now), however, I think you can take this a step further. Rather than having a (hidden) git-reset option which suppresses "HEAD is now at...", instead have a (hidden) option which augments the message. For example, --new-head-desc="New worktree" would make it output "New worktree HEAD is now at...". Changes to builtin/reset.c to support this would hardly be larger than the changes you already made.
The major benefit is that patch 3/6 no longer has to duplicate the code from builtin/reset.c:print_new_head_line() just to print its own "New worktree HEAD is now at..." message. (As for the argument that "git worktree add" must duplicate that code because it wants the message on stderr, whereas git-reset prints it to stdout, I don't see why git-worktree puts those messages to stderr in the first place. As far as I can tell, it would be equally valid to print them to stdout.)
Quoted 17 lines
Some examples of the new UI behaviour here for reference:
- guess-remote mode
$ git worktree add --guess-remote ../next Creating branch 'next' Branch 'next' set up to track remote branch 'next' from 'origin'. New worktree HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test Creating branch 'test' New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- new dwim (check out existing branch)
Quoted 8 lines
$ git worktree add ../test Checking out branch 'test' New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- no new branch created
$ git worktree add ../test2 origin/master New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'
I like the "creating" or "checking out" messages we now get for all the DWIM cases. I wonder if it would make sense to print "Checkout out blah..." for this case too. It's certainly not necessary since the user specified <commit-ish> explicitly, but it would make the UI even more consistent, and address your subsequent comment about missing context above the "Checking out files: ...%" line for this case. Thoughts?
Quoted 2 lines
Compare this to the old UI (new dwim omitted, as there's no old version of that):
Thanks for contrasting the new with the old. The new output is nicer and more helpful.
Quoted 4 lines
The one thing we are loosing is a context line before "Checking out files:", if no new branch is created. Personally I feel like that's acceptable, as the user just used the 'git worktree add' command, so it should be intuitive where those files are being checked out.
Quoted 18 lines · Eric Sunshine
Quoted 8 lines · Thomas Gummerer
This round should fix all the UI issues Eric found in the last round. The changes I made in a bit more detail:
- added a new commit introducing a new hidden --show-new-head-line flag in 'git reset'. This is used to suppress the "HEAD is now at ..." line that 'git reset --hard' usually prints, so we can replace it with our own "New worktree HEAD is now at ..." line instead, while keeping the progress indicator for larger repositories.
As with Junio, I'm fine with this hidden option (for now), however, I think you can take this a step further. Rather than having a (hidden) git-reset option which suppresses "HEAD is now at...", instead have a (hidden) option which augments the message. For example, --new-head-desc="New worktree" would make it output "New worktree HEAD is now at...". Changes to
builtin/reset.cto support this would hardly be larger than the changes you already made.
Quoted 7 lines
The major benefit is that patch 3/6 no longer has to duplicate the code from builtin/reset.c:print_new_head_line() just to print its own "New worktree HEAD is now at..." message. (As for the argument that "git worktree add" must duplicate that code because it wants the message on stderr, whereas git-reset prints it to stdout, I don't see why git-worktree puts those messages to stderr in the first place. As far as I can tell, it would be equally valid to print them to stdout.)
I didn't think of that, but I think that's nicer indeed. Will change. This will also be nicer when we're in a position to remove the hidden option and do all of this with internal functions. Then we can just re-use the new function that also takes a prefix at that point (after moving the function to 'libgit.a' of course.
Quoted 26 lines
Quoted 17 lines
Some examples of the new UI behaviour here for reference:
- guess-remote mode
$ git worktree add --guess-remote ../next Creating branch 'next' Branch 'next' set up to track remote branch 'next' from 'origin'. New worktree HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test Creating branch 'test' New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- new dwim (check out existing branch)
Quoted 8 lines
$ git worktree add ../test Checking out branch 'test' New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- no new branch created
$ git worktree add ../test2 origin/master New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'
Quoted 7 lines
I like the "creating" or "checking out" messages we now get for all the DWIM cases. I wonder if it would make sense to print "Checkout out blah..." for this case too. It's certainly not necessary since the user specified <commit-ish> explicitly, but it would make the UI even more consistent, and address your subsequent comment about missing context above the "Checking out files: ...%" line for this case. Thoughts?
Let me think through some of the cases here, of 'git worktre add <path> <commit-ish>' with various flags and what the UI would be with that added:
- no flags:
$ git worktree add ../test origin/master
Checking out 'origin/master'
Checking out files: ...%
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- -b branch:
$ git worktree add -b test ../test origin/master
Creating branch 'test'
Checking out 'origin/master'
Checking out files: ...%
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'Would we want to omit the "Checking out ..." here? I'm leaning towards yes, but dunno?
- --no-checkout
$ git worktree add --no-checkout test ../test origin/master
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- Original dwim with --detach flag
$ git worktree add --detach ../test
Checking out 'c2a499e6c'
Checking out files: ...%
New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'Looking at this, I'm not sure what's best here. I'm not sure I'm a fan of the duplicate "Checking out " message (I assume that's what you meant above, or did you mean just "Checkout ..."?)
I als don't think it gives too much context compared to just "Checking out files: ...%". I think it gives a bit more context when that message is not displayed at all, as it shows whether files are checked out or not, but if we do that, when we create a new branch, the amount of output we'd display is getting a bit long, to the point where I suspect users would just not read it anymore.
So I personally don't feel like this is worth it, even though it may give some context in some cases. But I'm also far from an expert in UI design, so if you (or others) feel this would be nicer I'm happy to implement it :)
Quoted 10 lines
Quoted 2 lines
Compare this to the old UI (new dwim omitted, as there's no old version of that):
Thanks for contrasting the new with the old. The new output is nicer and more helpful.
Quoted 4 lines
The one thing we are loosing is a context line before "Checking out files:", if no new branch is created. Personally I feel like that's acceptable, as the user just used the 'git worktree add' command, so it should be intuitive where those files are being checked out.
Quoted 13 lines · Thomas Gummerer
Quoted 1 line · Eric Sunshine
On Sat, Mar 31, 2018 at 11:17 AM, Thomas Gummerer <[email protected]> wrote:
Let me think through some of the cases here, of 'git worktre add <path> <commit-ish>' with various flags and what the UI would be with that added:
- no flags:
$ git worktree add ../test origin/master Checking out 'origin/master' Checking out files: ...% New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'
Quoted 5 lines
- -b branch:
$ git worktree add -b test ../test origin/master Creating branch 'test' Checking out 'origin/master'
Did you mean "Checking out 'test'"?
Quoted 5 lines
Checking out files: ...%
New worktree HEAD is now atc2a499e6cMerge branch 'jh/partial-clone'Would we want to omit the "Checking out ..." here? I'm leaning towards yes, but dunno?
To which "Checking out" message do you refer, the one showing the branch name or the one showing the checkout progress?
I'd probably agree that showing both "Creating" and "Checkout out" is overkill. However, see my response[1] to your "fixup!" patch in which I explore the idea that unifying "Checking out 'branch' and "Creating branch" messages may be a good idea and get us out of some UI jams which seem to be cropping up.
[1]: https://public-inbox.org/git/[email protected]/T/#m5d38b0c6427609e8c36aa6af83d518791c1e1581
Quoted 10 lines
- Original dwim with --detach flag
$ git worktree add --detach ../test Checking out 'c2a499e6c' Checking out files: ...% New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'Looking at this, I'm not sure what's best here. I'm not sure I'm a fan of the duplicate "Checking out " message (I assume that's what you meant above, or did you mean just "Checkout ..."?)
Taking [1] into account, this might become something like:
$ git worktree add --detach ../test
Preparing worktree (detached HEAD c2a499e6c)
Checking out files: ...%
New worktree HEAD is now at c2a499e6c GobbledygookQuoted 9 lines
I als don't think it gives too much context compared to just "Checking out files: ...%". I think it gives a bit more context when that message is not displayed at all, as it shows whether files are checked out or not, but if we do that, when we create a new branch, the amount of output we'd display is getting a bit long, to the point where I suspect users would just not read it anymore.
So I personally don't feel like this is worth it, even though it may give some context in some cases.
Fair enough observation. The idea suggested in [1] may keep output to a reasonable amount.
Quoted 50 lines · Eric Sunshine
Quoted 13 lines · Thomas Gummerer
Quoted 1 line · Eric Sunshine
On Sat, Mar 31, 2018 at 11:17 AM, Thomas Gummerer <[email protected]> wrote:
Let me think through some of the cases here, of 'git worktre add <path> <commit-ish>' with various flags and what the UI would be with that added:
- no flags:
$ git worktree add ../test origin/master Checking out 'origin/master' Checking out files: ...% New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'Quoted 5 lines
- -b branch:
$ git worktree add -b test ../test origin/master Creating branch 'test' Checking out 'origin/master'Did you mean "Checking out 'test'"?
Quoted 5 lines
Checking out files: ...%
New worktree HEAD is now atc2a499e6cMerge branch 'jh/partial-clone'Would we want to omit the "Checking out ..." here? I'm leaning towards yes, but dunno?
To which "Checking out" message do you refer, the one showing the branch name or the one showing the checkout progress?
I'd probably agree that showing both "Creating" and "Checkout out" is overkill. However, see my response[1] to your "fixup!" patch in which I explore the idea that unifying "Checking out 'branch' and "Creating branch" messages may be a good idea and get us out of some UI jams which seem to be cropping up.
[1]: https://public-inbox.org/git/[email protected]/T/#m5d38b0c6427609e8c36aa6af83d518791c1e1581
Quoted 10 lines
- Original dwim with --detach flag
$ git worktree add --detach ../test Checking out 'c2a499e6c' Checking out files: ...% New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'Looking at this, I'm not sure what's best here. I'm not sure I'm a fan of the duplicate "Checking out " message (I assume that's what you meant above, or did you mean just "Checkout ..."?)
Quoted 6 lines
Taking [1] into account, this might become something like:
$ git worktree add --detach ../test Preparing worktree (detached HEAD c2a499e6c) Checking out files: ...% New worktree HEAD is now at c2a499e6c Gobbledygook
The more I look at this solution, the more I like it. I'll try to implement this to see if there's anything I'm not thinking of right now, but I think I'll take the suggestion and send a re-roll with it implemented.
Quoted 12 lines
Quoted 9 lines
I als don't think it gives too much context compared to just "Checking out files: ...%". I think it gives a bit more context when that message is not displayed at all, as it shows whether files are checked out or not, but if we do that, when we create a new branch, the amount of output we'd display is getting a bit long, to the point where I suspect users would just not read it anymore.
So I personally don't feel like this is worth it, even though it may give some context in some cases.
Fair enough observation. The idea suggested in [1] may keep output to a reasonable amount.
Quoted 10 lines · Eric Sunshine
Quoted 8 lines · Thomas Gummerer
This round should fix all the UI issues Eric found in the last round. The changes I made in a bit more detail:
- added a new commit introducing a new hidden --show-new-head-line flag in 'git reset'. This is used to suppress the "HEAD is now at ..." line that 'git reset --hard' usually prints, so we can replace it with our own "New worktree HEAD is now at ..." line instead, while keeping the progress indicator for larger repositories.
Quoted 7 lines
As with Junio, I'm fine with this hidden option (for now), however, I think you can take this a step further. Rather than having a (hidden) git-reset option which suppresses "HEAD is now at...", instead have a (hidden) option which augments the message. For example, --new-head-desc="New worktree" would make it output "New worktree HEAD is now at...". Changes to
builtin/reset.cto support this would hardly be larger than the changes you already made.
Something else I just noticed that may make this a worse solution is that this breaks the sentence in two pieces for translators. I guess we could somehow get the "New worktree" part of the option translated, but that still means that if some language would require to move parts of the sentence around that would be less than ideal for translation.
Duy pointed this out to me in an earlier patch series, and I think we should probably not make life harder (or impossible) for translators if we can avoid it.
Would factoring out what we have in 'print_new_head_line()' into some common code, maybe in 'pretty.c', and still doing the printing from here be a reasonable tradeoff?
I think this could potentially even be re-used in other places, although again I'd like to keep that for a followup series to avoid scope creep in this one.
Quoted 52 lines
The major benefit is that patch 3/6 no longer has to duplicate the code from builtin/reset.c:print_new_head_line() just to print its own "New worktree HEAD is now at..." message. (As for the argument that "git worktree add" must duplicate that code because it wants the message on stderr, whereas git-reset prints it to stdout, I don't see why git-worktree puts those messages to stderr in the first place. As far as I can tell, it would be equally valid to print them to stdout.)
Quoted 17 lines
Some examples of the new UI behaviour here for reference:
- guess-remote mode
$ git worktree add --guess-remote ../next Creating branch 'next' Branch 'next' set up to track remote branch 'next' from 'origin'. New worktree HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test Creating branch 'test' New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- new dwim (check out existing branch)
Quoted 8 lines
$ git worktree add ../test Checking out branch 'test' New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- no new branch created
$ git worktree add ../test2 origin/master New worktree HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'I like the "creating" or "checking out" messages we now get for all the DWIM cases. I wonder if it would make sense to print "Checkout out blah..." for this case too. It's certainly not necessary since the user specified <commit-ish> explicitly, but it would make the UI even more consistent, and address your subsequent comment about missing context above the "Checking out files: ...%" line for this case. Thoughts?
Quoted 2 lines
Compare this to the old UI (new dwim omitted, as there's no old version of that):
Thanks for contrasting the new with the old. The new output is nicer and more helpful.
Quoted 4 lines
The one thing we are loosing is a context line before "Checking out files:", if no new branch is created. Personally I feel like that's acceptable, as the user just used the 'git worktree add' command, so it should be intuitive where those files are being checked out.
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Eric Sunshine
As with Junio, I'm fine with this hidden option (for now), however, I think you can take this a step further. Rather than having a (hidden) git-reset option which suppresses "HEAD is now at...", instead have a (hidden) option which augments the message. For example, --new-head-desc="New worktree" would make it output "New worktree HEAD is now at...". Changes to
builtin/reset.cto support this would hardly be larger than the changes you already made.
Quoted 5 lines
Something else I just noticed that may make this a worse solution is that this breaks the sentence in two pieces for translators. I guess we could somehow get the "New worktree" part of the option translated, but that still means that if some language would require to move parts of the sentence around that would be less than ideal for translation.
Good point.
One solution would be to have the new hidden option replace the string
entirely: --new-head-msg="New worktree HEAD is now at %s", which would allow translators to deal with the entire sentence. Even clearer would be to drop "now", as in "New worktree HEAD is at %s". (Default in reset.c would still be "HEAD is now at %s", of course.)
Another solution would be not to augment the "HEAD is now at..." message at all. I realize that that augmentation was one of the original motivations for this patch series, but with the upcoming restoration of the "Preparing worktree" message:
Preparing worktree (_branch disposition_)
HEAD is now at ...
it seems clear enough (at least to me) from the context introduced by the "Preparing..." message that "HEAD is now at..." refers to HEAD in the worktree. (But that's just my opinion.)
Quoted 3 lines
Would factoring out what we have in '
print_new_head_line()' into some common code, maybe in 'pretty.c', and still doing the printing from here be a reasonable tradeoff?
Isn't that getting uglier again? Not only would you have to publish that function, but you'd still need the hidden git-reset --show-new-head-line option.
Also, you'd end up calling that function from within low-level worker worktree.c:add_worktree(), thus making it take on UI duties, which would be nice to avoid if possible. (Unfortunately, the alternate idea of having worktree.c:add() handle this UI task doesn't quite work since add_worktree() doesn't return sufficient information for add() to know whether or not it should print "HEAD is now at..."; however, add_worktree() could be augmented to return more information.)
So, I don't presently have a good answer. I'm partial to the idea of not augmenting "HEAD is now at...", partly because context makes it relatively clear that it applies to the new worktree and partly because it's simpler (less implementation work for you) to leave it as is. If that choice isn't desirable, then next best would be for --new-head-msg= to replace the entire "HEAD is now at..." string rather than augmenting it.
Quoted 24 lines · Eric Sunshine
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Eric Sunshine
As with Junio, I'm fine with this hidden option (for now), however, I think you can take this a step further. Rather than having a (hidden) git-reset option which suppresses "HEAD is now at...", instead have a (hidden) option which augments the message. For example, --new-head-desc="New worktree" would make it output "New worktree HEAD is now at...". Changes to
builtin/reset.cto support this would hardly be larger than the changes you already made.Quoted 5 lines
Something else I just noticed that may make this a worse solution is that this breaks the sentence in two pieces for translators. I guess we could somehow get the "New worktree" part of the option translated, but that still means that if some language would require to move parts of the sentence around that would be less than ideal for translation.
Good point.
One solution would be to have the new hidden option replace the string
entirely: --new-head-msg="New worktree HEAD is now at %s", which would allow translators to deal with the entire sentence. Even clearer would be to drop "now", as in "New worktree HEAD is at %s". (Default inreset.cwould still be "HEAD is now at %s", of course.)
Quoted 4 lines
Another solution would be not to augment the "HEAD is now at..." message at all. I realize that that augmentation was one of the original motivations for this patch series, but with the upcoming restoration of the "Preparing worktree" message:
My original motivation of the series was to just make the new dwim work :) Because that's adding some magic, the secondary motivation became improving the UI, to help users see which dwim was used. I felt like this was going to be one of those improvements, especially after we get rid of the "Preparing ..." line.
I do however like your suggestion of the "Preparing worktree (_branch disposition_)", as that doesn't add more lines to the output, while still giving a good indication of what exactly is happening. At that point just showing "HEAD is now at ..." is fine by me, and doesn't require adding the hidden flag to 'git reset'. So I'm happy just dropping the change in the message here, which will simplify things.
Thanks for the suggestion!
Quoted 30 lines
Preparing worktree (
_branchdisposition_)
HEAD is now at ...it seems clear enough (at least to me) from the context introduced by the "Preparing..." message that "HEAD is now at..." refers to HEAD in the worktree. (But that's just my opinion.)
Quoted 3 lines
Would factoring out what we have in '
print_new_head_line()' into some common code, maybe in 'pretty.c', and still doing the printing from here be a reasonable tradeoff?Isn't that getting uglier again? Not only would you have to publish that function, but you'd still need the hidden git-reset --show-new-head-line option.
Also, you'd end up calling that function from within low-level worker worktree.c:add_worktree(), thus making it take on UI duties, which would be nice to avoid if possible. (Unfortunately, the alternate idea of having worktree.c:add() handle this UI task doesn't quite work since
add_worktree()doesn't return sufficient information foradd()to know whether or not it should print "HEAD is now at..."; however,add_worktree()could be augmented to return more information.)So, I don't presently have a good answer. I'm partial to the idea of not augmenting "HEAD is now at...", partly because context makes it relatively clear that it applies to the new worktree and partly because it's simpler (less implementation work for you) to leave it as is. If that choice isn't desirable, then next best would be for --new-head-msg= to replace the entire "HEAD is now at..." string rather than augmenting it.
Quoted 12 lines · Thomas Gummerer
Quoted 4 lines · Eric Sunshine
Another solution would be not to augment the "HEAD is now at..." message at all. I realize that that augmentation was one of the original motivations for this patch series, but with the upcoming restoration of the "Preparing worktree" message:
My original motivation of the series was to just make the new dwim work :) Because that's adding some magic, the secondary motivation became improving the UI, to help users see which dwim was used. I felt like this was going to be one of those improvements, especially after we get rid of the "Preparing ..." line.
Quoted 6 lines
I do however like your suggestion of the "Preparing worktree (
_branchdisposition_)", as that doesn't add more lines to the output, while still giving a good indication of what exactly is happening. At that point just showing "HEAD is now at ..." is fine by me, and doesn't require adding the hidden flag to 'git reset'. So I'm happy just dropping the change in the message here, which will simplify things.
Nice. This sounds like a good plan for moving forward.
Quoted 36 lines · Thomas Gummerer
Quoted 24 lines · Eric Sunshine
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Eric Sunshine
As with Junio, I'm fine with this hidden option (for now), however, I think you can take this a step further. Rather than having a (hidden) git-reset option which suppresses "HEAD is now at...", instead have a (hidden) option which augments the message. For example, --new-head-desc="New worktree" would make it output "New worktree HEAD is now at...". Changes to
builtin/reset.cto support this would hardly be larger than the changes you already made.Quoted 5 lines
Something else I just noticed that may make this a worse solution is that this breaks the sentence in two pieces for translators. I guess we could somehow get the "New worktree" part of the option translated, but that still means that if some language would require to move parts of the sentence around that would be less than ideal for translation.
Good point.
One solution would be to have the new hidden option replace the string
entirely: --new-head-msg="New worktree HEAD is now at %s", which would allow translators to deal with the entire sentence. Even clearer would be to drop "now", as in "New worktree HEAD is at %s". (Default inreset.cwould still be "HEAD is now at %s", of course.)Quoted 4 lines
Another solution would be not to augment the "HEAD is now at..." message at all. I realize that that augmentation was one of the original motivations for this patch series, but with the upcoming restoration of the "Preparing worktree" message:
My original motivation of the series was to just make the new dwim work :) Because that's adding some magic, the secondary motivation became improving the UI, to help users see which dwim was used. I felt like this was going to be one of those improvements, especially after we get rid of the "Preparing ..." line.
Quoted 6 lines
I do however like your suggestion of the "Preparing worktree (
_branchdisposition_)", as that doesn't add more lines to the output, while still giving a good indication of what exactly is happening. At that point just showing "HEAD is now at ..." is fine by me, and doesn't require adding the hidden flag to 'git reset'. So I'm happy just dropping the change in the message here, which will simplify things.
And just as I'm re-reading my commit messages, I guess there was one more motivation for printing the "HEAD is now at ..." line ourselves:
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
I think I can live with that for now, I personally don't use '--no-checkout', and we could fix this at some point in the future if we desire to do so. I think we can consider that out of scope for this patch series, as its main goal is to introduce the new dwim.
Quoted 1 line
Thanks for the suggestion!
Quoted 6 lines · Thomas Gummerer
And just as I'm re-reading my commit messages, I guess there was one more motivation for printing the "HEAD is now at ..." line ourselves:
If the '--no-checkout' flag is given, the output of 'git worktree add' is just:
Quoted 8 lines
Preparing foo (identifier foo)
even though the HEAD is set to a commit, which is just not checked out.
I think I can live with that for now, I personally don't use '--no-checkout', and we could fix this at some point in the future if we desire to do so. I think we can consider that out of scope for this patch series, as its main goal is to introduce the new dwim.
Sounds reasonable, especially since the proposed "Preparing worktree (_branch description_)" provides similarly useful feedback.
v7 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererThanks Eric for the review and all of the suggestions in the last round.
Previous rounds are at <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]> and <[email protected]>.
The main change once again in this series is the user interface. It feels like we went in a complete circle here now, as this iteration is bringing the "Preparing ..." line back (although in a slightly different form than the original), and is moving away from printing it's own "HEAD is now at..." line again. This also means we don't need the new hidden option to 'git reset' anymore, which is nice.
I do like the new UI more than what we had in the last round (which I already liked more than the original UI) :)
Other than those changes, it also includes Eric's suggestion for a better wording in the documentation, fixes the argument order to test_cmd_rev in a test, and makes a test more robust.
To demonstrate the UI updates, let's compare the new UI and the old UI again. This is the same commands as used for the demonstration in the last iteration, so please have a look at <[email protected]> for an example of what it looked like after the last round.
New UI:
- guess-remote mode
$ git worktree add --guess-remote ../next
Preparing worktree (new branch 'next')
Branch 'next' set up to track remote branch 'next' from 'origin'.
HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test
Preparing worktree (new branch 'test')
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- new dwim (check out existing branch)
$ git worktree add ../test
Preparing worktree (checking out 'test')
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- detached HEAD
$ git worktree add ../test2 origin/master
Preparing worktree (detached HEAD c2a499e6c)
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- resetting existing branch
$ git worktree add -B next ../test3 origin/master
Preparing worktree (resetting branch 'next' (was at caa68db14))
Branch 'next' set up to track remote branch 'master' from 'origin'.
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'This output is new in this round and wasn't previously discussed. While working on the "Preparing ..." line I noticed this, and thought it would be a good idea. I feel like this fits in the scope of the series quite well, as it's improving the UI, but I'm happy to split it out if that's preferred.
It may also be worth displaying the title of the commit here, but at that point the line would get a bit long, so dunno.
- large repository (with original dwim)
$ git worktree add ../test
Preparing worktree (new branch 'test')
Checking out files: 100% (62915/62915), done.
HEAD is now at c2a9838452a4 Merge tag 'for-4.16/dm-fixes-4' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm- error when directory already exists
$ git worktree add ../test
Preparing worktree (checking out 'test')
fatal: '../test' already existsCompare this to the old UI (new dwim omitted, as there's no old version of that):
- guess-remote mode
$ git worktree add --guess-remote ../next
Branch 'next' set up to track remote branch 'next' from 'origin'.
Preparing ../next (identifier next)
HEAD is now at caa68db14 Merge branch 'sb/packfiles-in-repository' into next- original dwim (create a branch based on the current HEAD)
$ git worktree add ../test
Preparing ../test (identifier test)
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- no new branch created
$ git worktree add ../test2 origin/master
Preparing ../test2 (identifier test2)
HEAD is now at c2a499e6c Merge branch 'jh/partial-clone'- large repository
$ git worktree add ../test
Preparing ../test (identifier test)
Checking out files: 100% (62915/62915), done.
HEAD is now at c2a9838452a4 Merge tag 'for-4.16/dm-fixes-4' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm- error when directory already exists
$ git worktree add ../test
fatal: '../test' already existsInterdiff below:
Documentation/git-worktree.txt +6 -6
@@ -61,13 +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 worktree with a branch named after -`$(basename <path>)` (call it `<branch>`) is created. If `<branch>` +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>` exists in the repository, -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). +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::
builtin/reset.c +1 -4
@@ -288,7 +288,6 @@ static int git_reset_config(const char *var, const char *value, void *cb)int cmd_reset (int argc,const char **argv,const char *prefix) {int reset_type =NONE , update_ref_status =0 , quiet =0 ; -int show_new_head_line =1 ;int patch_mode =0 , unborn;const char *rev;struct object_id oid;
@@ -311,7 +310,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)OPT_BOOL ('p' ,"patch" , &patch_mode,N_ ("select hunks interactively" )),OPT_BOOL ('N' ,"intent-to-add" , &intent_to_add,N_ ("record only the fact that removed paths will be added later" )), -OPT_HIDDEN_BOOL (0 ,"show-new-head-line" , &show_new_head_line,N_ ("show information on the new HEAD" )), OPT_END() };
@@ -405,8 +403,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)* switched to, saving the previous head in ORIG_HEAD before. */ update_ref_status =reset_refs (rev, &oid); -if (reset_type ==HARD && !update_ref_status && !quiet && - show_new_head_line) +if (reset_type ==HARD && !update_ref_status && !quiet)print_new_head_line (lookup_commit_reference (&oid)); }if (!pathspec.nr)
builtin/worktree.c +33 -16
97 lines not on this page · raw message
t/t2025-worktree-add.sh +2 -1
@@ -204,7 +204,7 @@ test_expect_success '"add" checks out existing branch of dwimd name' ' test_cmp_rev HEAD~1 dwim && ( cd dwim && - test_cmp_rev dwim HEAD + test_cmp_rev HEAD dwim ) '
@@ -215,6 +215,7 @@ test_expect_success '"add <path>" dwim fails with checked out branch' ' ' test_expect_success '"add --force" with existing dwimd name doesnt die' ' + git checkout test-branch && git worktree add --force test-branch '
t/t7102-reset.sh +0 -5
@@ -568,9 +568,4 @@ test_expect_success 'reset --mixed sets up work tree' ' test_cmp expect actual ' -test_expect_success 'reset --no-show-new-head-line suppresses "HEAD is now at" output' ' - git reset --hard --no-show-new-head-line HEAD >actual && - ! grep "HEAD is now at" <actual -' - test_done
Thomas Gummerer (4):
worktree: remove extra members from struct add_opts
worktree: improve message when creating a new worktree
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 +++- builtin/worktree.c | 101 ++++++++++++++++++++++++++++++----------- t/t2025-worktree-add.sh | 26 ++++++++--- 3 files changed, 100 insertions(+), 36 deletions(-)
Signature · 1 line
2.16.1.74.g6cd9b6cbe3
PATCH v7 1/4 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.
--- builtin/worktree.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-)
builtin/worktree.c +15 -17
@@ -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 <branch> 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,32 @@ 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);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 +428,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" )); }
Signature · 1 line
2.17.0.252.gfe0a9eaf31
PATCH v7 2/4 worktree: improve message when creating a new worktree
In reply to Thomas GummererCurrently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at 26da330922 <title>
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 are 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 this ends up in three 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 'next' (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> <branch>', 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>
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.
--- builtin/worktree.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-)
builtin/worktree.c +27 -2
@@ -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,31 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static void print_preparing_worktree_line (const char *branch, +const char *new_branch, +const char *new_branch_force) +{ +if (new_branch_force) { +struct commit *commit =lookup_commit_reference_by_name (new_branch_force); +if (!commit) +printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force); +else +printf_ln (_ ("Preparing worktree (resetting branch '%s' (was at %s))" ), + new_branch_force, +find_unique_abbrev (commit->object.oid.hash, +DEFAULT_ABBREV )); + }else if (new_branch) { +printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_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 )); + } +} +static int add (int ac,const char **av,const char *prefix) {struct add_opts opts;
@@ -434,6 +457,8 @@ static int add(int ac, const char **av, const char *prefix) } } +print_preparing_worktree_line (branch, new_branch, new_branch_force); +if (new_branch) {struct child_process cp =CHILD_PROCESS_INIT ; cp.git_cmd =1 ;
Signature · 1 line
2.17.0.252.gfe0a9eaf31
Re: [PATCH v7 2/4] worktree: improve message when creating a new worktree
Quoted 9 lines · Thomas Gummerer
Currently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at26da330922<title>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.
OK. Maybe there once was a place or two that took the identifier as an input to name a specific worktree to work on (perhaps "prune"?), but if we no longer do that (which makes sense, as we should be able to uniquely identify a worktree by the path to it), then it makes perfect sense to prevent it from appearing in the UI.
Quoted 3 lines
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 are perform some magic under the hood, which should be
s/are perform/perform/, I think (no need to reroll, only to fix this).
Quoted 6 lines
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.
I think it is a bug for reset to give this kind of informational message to the standard output stream. A rule of thumb I use is "is this something that a user who wants quiet operation would wish to squelch with --quiet option?" and if the answer is yes, it should go to the standard error output, so info and progress should go to the standard output in general.
But I am OK to unify to the standard output with this patch. We may want to come back and correct _both_ this new message and what reset says, but that is outside the scope of this topic.
Thanks.
Re: [PATCH v7 2/4] worktree: improve message when creating a new worktree
Quoted 18 lines · Junio C Hamano
Quoted 9 lines · Thomas Gummerer
Currently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at26da330922<title>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.
OK. Maybe there once was a place or two that took the identifier as an input to name a specific worktree to work on (perhaps "prune"?), but if we no longer do that (which makes sense, as we should be able to uniquely identify a worktree by the path to it), then it makes perfect sense to prevent it from appearing in the UI.
Quoted 5 lines
Quoted 3 lines
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 are perform some magic under the hood, which should be
s/are perform/perform/, I think (no need to reroll, only to fix this).
Now that I'm re-rolling anyway, I'll fix this as well, thanks.
Quoted 7 lines
Quoted 6 lines
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.
Quoted 6 lines
I think it is a bug for reset to give this kind of informational message to the standard output stream. A rule of thumb I use is "is this something that a user who wants quiet operation would wish to squelch with --quiet option?" and if the answer is yes, it should go to the standard error output, so info and progress should go to the standard output in general.
I assume you meant "standard error output" just above?
Quoted 3 lines
But I am OK to unify to the standard output with this patch. We may want to come back and correct _both_ this new message and what reset says, but that is outside the scope of this topic.
Cool, I'd rather not add more moving pieces to this series, as it's already at v7 (v8 soon), but I'm happy to have a look at this and fix it on top once the dust settled on this series.
Quoted 1 line
Thanks.
Re: [PATCH v7 2/4] worktree: improve message when creating a new worktree
In reply to Thomas GummererQuoted 19 lines · Thomas Gummerer
Currently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at26da330922<title>
[...]
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 are 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 this ends up in three different cases:
- 'git worktree add -b ...' or 'git worktree add <path>' [...]
Quoted 8 lines
- '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 'next' (was at
caa68db14)) HEAD is now at26da330922<title>
The (...) embedded inside another (...) is ugly and hard to read. Better perhaps:
Preparing worktree (resetting branch 'next'; was at caa68db14)
Not necessarily worth a re-roll. It would be nice to see this series land; perhaps this can be tweaked later.
Quoted 11 lines
or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title>- 'git worktree add --detach' or 'git worktree add <path> <branch>', 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 at26da330922<title>
This is inaccurate, isn't it? Certainly, specifying something like "origin/floop" for <branch> ends up detached:
% git worktree add w1 origin/floop
...
% git worktree list
/proj fe0a9eaf31 [master]
/proj/w1 b46fe60e1d (detached HEAD)
but specifying an existing local branch (say "wip") does not end up detached:
% git worktree add w2 wip
...
% git worktree list
/proj fe0a9eaf31 [master]
/proj/w1 b46fe60e1d (detached HEAD)
/proj/w2 820ed2a513 [wip]
Quoted 9 lines
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 <[email protected]>Signed-off-by Thomas Gummerer <[email protected]>
Re: [PATCH v7 2/4] worktree: improve message when creating a new worktree
Quoted 29 lines · Eric Sunshine
Quoted 19 lines · Thomas Gummerer
Currently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at26da330922<title>
[...]
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 are 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 this ends up in three different cases:
- 'git worktree add -b ...' or 'git worktree add <path>' [...]
Quoted 8 lines
- '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 'next' (was at
caa68db14)) HEAD is now at26da330922<title>
Quoted 7 lines
The (...) embedded inside another (...) is ugly and hard to read. Better perhaps:
Preparing worktree (resetting branch 'next'; was at
caa68db14)Not necessarily worth a re-roll. It would be nice to see this series land; perhaps this can be tweaked later.
I'll tweak it while fixing the other bit.
Quoted 9 lines
Quoted 9 lines
or: Preparing worktree (new branch '<branch>') HEAD is now at 26da330922 <title>- 'git worktree add --detach' or 'git worktree add <path> <branch>', both of which create a new worktree with a detached HEAD, for which we will print the following output:
Quoted 5 lines
Quoted 2 lines
Preparing worktree (detached HEAD
26da330922) HEAD is now at26da330922<title>This is inaccurate, isn't it? Certainly, specifying something like "origin/floop" for <branch> ends up detached:
Ah indeed, this was the case I missed. I thought I managed to go through all of them, but this one slipped through the cracks. Thanks for catching this, will fix in a re-roll.
Quoted 24 lines
% git worktree add w1 origin/floop
...
% git worktree list
/projfe0a9eaf31[master]
/proj/w1b46fe60e1d(detached HEAD)but specifying an existing local branch (say "wip") does not end up detached:
% git worktree add w2 wip
...
% git worktree list
/projfe0a9eaf31[master]
/proj/w1b46fe60e1d(detached HEAD)
/proj/w2820ed2a513[wip]Quoted 9 lines
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 <[email protected]>Signed-off-by Thomas Gummerer <[email protected]>
PATCH v7 3/4 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
builtin/worktree.c +17 -10
@@ -378,6 +378,20 @@ static void print_preparing_worktree_line(const char *branch, } } +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); +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;
@@ -430,16 +444,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); -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) {
Signature · 1 line
2.17.0.252.gfe0a9eaf31
PATCH v7 4/4 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 30 ++++++++++++++++++++++++------ t/t2025-worktree-add.sh | 26 +++++++++++++++++++------- 3 files changed, 50 insertions(+), 15 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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::
builtin/worktree.c +24 -6
@@ -355,9 +355,12 @@ static int add_worktree(const char *path, const char *refname,static void print_preparing_worktree_line (const char *branch,const char *new_branch, -const char *new_branch_force) +const char *new_branch_force, +int checkout_existing_branch) { -if (new_branch_force) { +if (checkout_existing_branch) { +printf_ln (_ ("Preparing worktree (checking out '%s')" ), branch); + }else if (new_branch_force) {struct commit *commit =lookup_commit_reference_by_name (new_branch_force);if (!commit)printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force);
@@ -378,11 +381,23 @@ static void print_preparing_worktree_line(const char *branch, } } -static const char *dwim_branch (const char *path,const char **new_branch) +static const char *dwim_branch (const char *path,const char **new_branch, +int *checkout_existing_branch) {int n;const char *s =worktree_basename (path, &n); - *new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + *checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + *new_branch = branchname;if (guess_remote) {struct object_id oid; const char *remote =
@@ -397,6 +412,7 @@ static int add(int ac, const char **av, const char *prefix)struct add_opts opts;const char *new_branch_force =NULL ;char *path; +int checkout_existing_branch =0 ;const char *branch;const char *new_branch =NULL ;const char *opt_track =NULL ;
@@ -444,7 +460,8 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !new_branch && !opts.detach) { -const char *s =dwim_branch (path, &new_branch); +const char *s =dwim_branch (path, &new_branch, + &checkout_existing_branch);if (s) branch = s; }
@@ -464,7 +481,8 @@ static int add(int ac, const char **av, const char *prefix) } } -print_preparing_worktree_line (branch, new_branch, new_branch_force); +print_preparing_worktree_line (branch, new_branch, new_branch_force, + checkout_existing_branch);if (new_branch) {struct child_process cp =CHILD_PROCESS_INIT ;
t/t2025-worktree-add.sh +19 -7
@@ -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' '
Signature · 1 line
2.17.0.252.gfe0a9eaf31
Quoted 6 lines · Thomas Gummerer
The main change once again in this series is the user interface. It feels like we went in a complete circle here now, as this iteration is bringing the "Preparing ..." line back (although in a slightly different form than the original), and is moving away from printing it's own "HEAD is now at..." line again. This also means we don't need the new hidden option to 'git reset' anymore, which is nice.
I'm glad to see the proposed hidden git-reset option go away, and am likewise happy to see that worktree no longer wants to print "HEAD is now at" itself. I'm much more pleased with the direction this series is now taking than in earlier rounds. It's also much simpler, which is a nice plus.
Quoted 7 lines
I do like the new UI more than what we had in the last round (which I already liked more than the original UI) :)
To demonstrate the UI updates, let's compare the new UI and the old UI again. This is the same commands as used for the demonstration in the last iteration, so please have a look at <[email protected]> for an example of what it looked like after the last round.
Thanks for presenting examples of the new UI under various conditions. Like you, I find the new "Preparing..." message superior to and much more useful than the original.
Aside from the problem pointed out in my review of 2/4 in which it incorrectly shows "detached HEAD" for "git worktree add wt existing-local-branch", I think this series is just about ready, and hope to see it land with the next re-roll.
Thanks for working on it.
v8 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererThanks Eric and Junio for the review the suggestions in the last round.
Previous rounds are at <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]> and <[email protected]>.
This round updates the output for "resetting branch ..." to not have braces embedded inside of another pair of braces, and is not correctly printing "checking out '<branch>'" when 'git worktree add <path> <local-branch>' is used. Both these changes are in patch 2/4, the other patches are the same as in the previous round.
Interdiff below:
builtin/worktree.c +18 -9
@@ -353,7 +353,8 @@ static int add_worktree(const char *path, const char *refname,return ret; } -static void print_preparing_worktree_line (const char *branch, +static void print_preparing_worktree_line (int detach, +const char *branch,const char *new_branch,const char *new_branch_force,int checkout_existing_branch)
@@ -365,19 +366,27 @@ static void print_preparing_worktree_line(const char *branch,if (!commit)printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force);else -printf_ln (_ ("Preparing worktree (resetting branch '%s' (was at %s))" ), +printf_ln (_ ("Preparing worktree (resetting branch '%s'; was at %s)" ), new_branch_force,find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); }else if (new_branch) {printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_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 )); +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); } }
@@ -481,7 +490,7 @@ static int add(int ac, const char **av, const char *prefix) } } -print_preparing_worktree_line (branch, new_branch, new_branch_force, +print_preparing_worktree_line (opts.detach, branch, new_branch, new_branch_force, checkout_existing_branch);if (new_branch) {
Thomas Gummerer (4):
worktree: remove extra members from struct add_opts
worktree: improve message when creating a new worktree
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 +++- builtin/worktree.c | 111 +++++++++++++++++++++++++++++++---------- t/t2025-worktree-add.sh | 26 +++++++--- 3 files changed, 110 insertions(+), 36 deletions(-)
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
PATCH v8 1/4 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.
--- builtin/worktree.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-)
builtin/worktree.c +15 -17
@@ -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 <branch> 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,32 @@ 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);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 +428,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" )); }
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
Re: [PATCH v8 1/4] worktree: remove extra members from struct add_opts
Quoted 16 lines · Thomas Gummerer
There are two members of 'struct
add_opts', which are only used inside the 'add()' function, but being part of 'structadd_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 <[email protected]>---
builtin/worktree.c +0
-2@@ -27,8 +27,6 @@ struct add_opts {int keep_locked; -const char *new_branch; -int force_new_branch; };
builtin/worktree.c static int add(int ac, const char **av, const char *prefix) · line 397
Quoted 6 lines
builtin/worktree.c +2
-2@@ -395,33 +394,32 @@ static int add(int ac, const char **av, const char *prefix) -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);
Sorry for not noticing this earlier, but when 'new_branch' was part of 'opts', this leaked xstrndup'd string was covered by the UNLEAK(opts) at the end of the function. However, now that 'new_branch' is a standalone variable, it needs its own UNLEAK().
PATCH v8 2/4 worktree: improve message when creating a new worktree
In reply to Thomas GummererCurrently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at 26da330922 <title>
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 this ends up in three 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 'next'; 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 prints the following output:
Preparing worktree (checking out '<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.
--- builtin/worktree.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-)
builtin/worktree.c +36 -2
@@ -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 @@ static int add_worktree(const char *path, const char *refname,return ret; } +static void print_preparing_worktree_line (int detach, +const char *branch, +const char *new_branch, +const char *new_branch_force) +{ +if (new_branch_force) { +struct commit *commit =lookup_commit_reference_by_name (new_branch_force); +if (!commit) +printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force); +else +printf_ln (_ ("Preparing worktree (resetting branch '%s'; was at %s)" ), + new_branch_force, +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;
@@ -434,6 +466,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 ;
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
Re: [PATCH v8 2/4] worktree: improve message when creating a new worktree
Quoted 16 lines · Thomas Gummerer
Currently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at26da330922<title>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.
Quoted 5 lines
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 this ends up in three different cases:
You now enumerate four cases, not three. Perhaps: "There are several different cases:"
Quoted 8 lines
- '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 at26da330922<title>
Quoted 8 lines
- '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 'next'; was at
caa68db14) HEAD is now at26da330922<title>
For consistency with the other examples: s/next/<branch>/
Quoted 9 lines
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:
Quoted 5 lines
Preparing worktree (detached HEAD
26da330922) HEAD is now at26da330922<title>- 'git worktree add <path> <local-branch>', which checks out the branch prints the following output:
s/prints/and prints/
Quoted 2 lines
Preparing worktree (checking out '<branch>') HEAD is now at 47007d5 <title>
s/<branch>/<local-branch>/ would probably be clearer since you use <local-branch> in the example command.
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 359
Quoted 13 lines
---
builtin/worktree.c +7
-0@@ -355,6 +353,40 @@ static int add_worktree(const char *path, const char *refname, +static void print_preparing_worktree_line (int detach, +const char *branch, +const char *new_branch, +const char *new_branch_force) +{ +if (new_branch_force) { +struct commit *commit =lookup_commit_reference_by_name (new_branch_force);
Nit: By the time this function is called, 'new_branch' contains the same value as 'new_branch_force' if "-B" was used. 'new_branch' _is_ the name of the new branch, forced or not, and it's easier to reason about 'new_branch_force' as a mere boolean indicating whether "force" is in effect. This suggests that the final argument to this function should be a boolean (int) named either 'force' or 'force_new_branch', and that 'new_branch' should be used everywhere the code needs to reference the new branch name (forced or not).
Not worth a re-roll.
Quoted 13 lines
builtin/worktree.c +13
-0+if (!commit) +printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force); +else +printf_ln (_ ("Preparing worktree (resetting branch '%s'; was at %s)" ), + new_branch_force, +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))
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 385
Quoted 10 lines
builtin/worktree.c +10
-0+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 )); + }
It's too bad this final case duplicates so much code from add_worktree(). Perhaps some future refactoring patch (not part of this series) can consolidate the code.
Quoted 8 lines
builtin/worktree.c +5
-0+strbuf_release (&s); + } +}@@ -434,6 +466,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 ;
PATCH v8 3/4 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-)
builtin/worktree.c +17 -10
@@ -387,6 +387,20 @@ 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); +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,16 +453,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); -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) {
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
PATCH v8 4/4 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 30 ++++++++++++++++++++++++------ t/t2025-worktree-add.sh | 26 +++++++++++++++++++------- 3 files changed, 50 insertions(+), 15 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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::
builtin/worktree.c +24 -6
@@ -356,9 +356,12 @@ static int add_worktree(const char *path, const char *refname,static void print_preparing_worktree_line (int detach,const char *branch,const char *new_branch, -const char *new_branch_force) +const char *new_branch_force, +int checkout_existing_branch) { -if (new_branch_force) { +if (checkout_existing_branch) { +printf_ln (_ ("Preparing worktree (checking out '%s')" ), branch); + }else if (new_branch_force) {struct commit *commit =lookup_commit_reference_by_name (new_branch_force);if (!commit)printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force);
@@ -387,11 +390,23 @@ static void print_preparing_worktree_line(int detach, } } -static const char *dwim_branch (const char *path,const char **new_branch) +static const char *dwim_branch (const char *path,const char **new_branch, +int *checkout_existing_branch) {int n;const char *s =worktree_basename (path, &n); - *new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + *checkout_existing_branch =1 ; +strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + } + + *new_branch = branchname;if (guess_remote) {struct object_id oid; const char *remote =
@@ -406,6 +421,7 @@ static int add(int ac, const char **av, const char *prefix)struct add_opts opts;const char *new_branch_force =NULL ;char *path; +int checkout_existing_branch =0 ;const char *branch;const char *new_branch =NULL ;const char *opt_track =NULL ;
@@ -453,7 +469,8 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !new_branch && !opts.detach) { -const char *s =dwim_branch (path, &new_branch); +const char *s =dwim_branch (path, &new_branch, + &checkout_existing_branch);if (s) branch = s; }
@@ -473,7 +490,8 @@ static int add(int ac, const char **av, const char *prefix) } } -print_preparing_worktree_line (opts.detach, branch, new_branch, new_branch_force); +print_preparing_worktree_line (opts.detach, branch, new_branch, new_branch_force, + checkout_existing_branch);if (new_branch) {struct child_process cp =CHILD_PROCESS_INIT ;
t/t2025-worktree-add.sh +19 -7
@@ -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' '
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
Quoted 14 lines · Thomas Gummerer
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.
[...]Signed-off-by Thomas Gummerer <[email protected]>---
builtin/worktree.c static int add_worktree(const char *path, const char *refname, · line 363
Quoted 10 lines
builtin/worktree.c +4
-2@@ -356,9 +356,12 @@ static int add_worktree(const char *path, const char *refname,static void print_preparing_worktree_line (int detach,const char *branch,const char *new_branch, -const char *new_branch_force) +const char *new_branch_force, +int checkout_existing_branch) { -if (new_branch_force) { +if (checkout_existing_branch) { +printf_ln (_ ("Preparing worktree (checking out '%s')" ), branch);
Is the new 'checkout_existing_branch' argument and this new conditional really needed? Both 'new_branch' and 'new_branch_force' will be NULL in the case of an existing branch, so there should be no need for 'checkout_existing_branch' to state explicitly what is already indicated by their NULLness. With both of them NULL, the existing "(checking out '...')" message later in the function will kick in, so this new condition isn't needed.
Quoted 4 lines
builtin/worktree.c +3
-0+ }else if (new_branch_force) {@@ -387,11 +390,23 @@ static void print_preparing_worktree_line(int detach, +static const char *dwim_branch (const char *path,const char **new_branch, +int *checkout_existing_branch)
builtin/worktree.c static void print_preparing_worktree_line(int detach, · line 400
Quoted 10 lines
builtin/worktree.c +6
-1{int n;const char *s =worktree_basename (path, &n); - *new_branch =xstrndup (s, n); +const char *branchname =xstrndup (s, n); +struct strbuf ref =STRBUF_INIT ; + +if (!strbuf_check_branch_ref (&ref, branchname) && +ref_exists (ref.buf)) { + *checkout_existing_branch =1 ;
See above regarding apparent lack of need for this new variable.
builtin/worktree.c static void print_preparing_worktree_line(int detach, · line 404
Quoted 4 lines
builtin/worktree.c +4
-0+strbuf_release (&ref); +UNLEAK (branchname); +return branchname; + }
Taking the above observation into account, I applied, atop this patch, the following fixup! which removes the 'checkout_existing_branch' gunk and still get the same output:
--- >8 ---
builtin/worktree.c +5 -13
@@ -356,12 +356,9 @@ static int add_worktree(const char *path, const char *refname,static void print_preparing_worktree_line (int detach,const char *branch,const char *new_branch, -const char *new_branch_force, -int checkout_existing_branch) +const char *new_branch_force) { -if (checkout_existing_branch) { -printf_ln (_ ("Preparing worktree (checking out '%s')" ), branch); - }else if (new_branch_force) { +if (new_branch_force) {struct commit *commit =lookup_commit_reference_by_name (new_branch_force);if (!commit)printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch_force);
@@ -390,8 +387,7 @@ static void print_preparing_worktree_line(int detach, } } -static const char *dwim_branch (const char *path,const char **new_branch, -int *checkout_existing_branch) +static const char *dwim_branch (const char *path,const char **new_branch) {int n;const char *s =worktree_basename (path, &n);
@@ -400,7 +396,6 @@ static const char *dwim_branch(const char *path, const char **new_branch,if (!strbuf_check_branch_ref (&ref, branchname) &&ref_exists (ref.buf)) { - *checkout_existing_branch =1 ;strbuf_release (&ref);UNLEAK (branchname);return branchname;
@@ -421,7 +416,6 @@ static int add(int ac, const char **av, const char *prefix)struct add_opts opts;const char *new_branch_force =NULL ;char *path; -int checkout_existing_branch =0 ;const char *branch;const char *new_branch =NULL ;const char *opt_track =NULL ;
@@ -469,8 +463,7 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !new_branch && !opts.detach) { -const char *s =dwim_branch (path, &new_branch, - &checkout_existing_branch); +const char *s =dwim_branch (path, &new_branch);if (s) branch = s; }
@@ -490,8 +483,7 @@ static int add(int ac, const char **av, const char *prefix) } } -print_preparing_worktree_line (opts.detach, branch, new_branch, new_branch_force, - checkout_existing_branch); +print_preparing_worktree_line (opts.detach, branch, new_branch, new_branch_force);if (new_branch) {struct child_process cp =CHILD_PROCESS_INIT ;
--- >8 ---
v9 cover worktree: teach "add" to check out existing branches
In reply to Thomas GummererPrevious rounds are at <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]> and <[email protected]>.
Thanks Eric for the review and the suggestions on the previous round.
Changes since the previous round:
- UNLEAK new_branch after it was xstrndup'd
- update the commit message of 2/4 according to Eric's suggestions
- make force_new_branch a boolean flag in print_preparing_worktree_line, instead of using it as the branch name. Instead use new_branch as the new branch name everywhere in that function.
- get rid of the ckeckout_existing_branch flag
Interdiff below:
builtin/worktree.c +9 -17
@@ -356,18 +356,15 @@ static int add_worktree(const char *path, const char *refname,static void print_preparing_worktree_line (int detach,const char *branch,const char *new_branch, -const char *new_branch_force, -int checkout_existing_branch) +int force_new_branch) { -if (checkout_existing_branch) { -printf_ln (_ ("Preparing worktree (checking out '%s')" ), branch); - }else if (new_branch_force) { -struct commit *commit =lookup_commit_reference_by_name (new_branch_force); +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_force); +printf_ln (_ ("Preparing worktree (new branch '%s')" ), new_branch);else printf_ln (_ ("Preparing worktree (resetting branch '%s'; was at %s)" ), - new_branch_force, + new_branch,find_unique_abbrev (commit->object.oid.hash,DEFAULT_ABBREV )); }else if (new_branch) {
@@ -390,19 +387,17 @@ static void print_preparing_worktree_line(int detach, } } -static const char *dwim_branch (const char *path,const char **new_branch, -int *checkout_existing_branch) +static const char *dwim_branch (const char *path,const char **new_branch) {int n;const char *s =worktree_basename (path, &n);const char *branchname =xstrndup (s, n);struct strbuf ref =STRBUF_INIT ; +UNLEAK (branchname);if (!strbuf_check_branch_ref (&ref, branchname) &&ref_exists (ref.buf)) { - *checkout_existing_branch =1 ;strbuf_release (&ref); -UNLEAK (branchname);return branchname; }
@@ -421,7 +416,6 @@ static int add(int ac, const char **av, const char *prefix)struct add_opts opts;const char *new_branch_force =NULL ;char *path; -int checkout_existing_branch =0 ;const char *branch;const char *new_branch =NULL ;const char *opt_track =NULL ;
@@ -469,8 +463,7 @@ static int add(int ac, const char **av, const char *prefix) }if (ac <2 && !new_branch && !opts.detach) { -const char *s =dwim_branch (path, &new_branch, - &checkout_existing_branch); +const char *s =dwim_branch (path, &new_branch);if (s) branch = s; }
@@ -490,8 +483,7 @@ static int add(int ac, const char **av, const char *prefix) } } -print_preparing_worktree_line (opts.detach, branch, new_branch, new_branch_force, - checkout_existing_branch); +print_preparing_worktree_line (opts.detach, branch, new_branch, !!new_branch_force);if (new_branch) {struct child_process cp =CHILD_PROCESS_INIT ;
Thomas Gummerer (4):
worktree: remove extra members from struct add_opts
worktree: improve message when creating a new worktree
worktree: factor out dwim_branch function
worktree: teach "add" to check out existing branches
Documentation/git-worktree.txt | 9 +++- builtin/worktree.c | 103 ++++++++++++++++++++++++++++++----------- t/t2025-worktree-add.sh | 26 ++++++++--- 3 files changed, 102 insertions(+), 36 deletions(-)
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
PATCH v9 1/4 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.
--- builtin/worktree.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-)
builtin/worktree.c +16 -17
@@ -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 <branch> 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" )); }
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
PATCH v9 2/4 worktree: improve message when creating a new worktree
In reply to Thomas GummererCurrently 'git worktree add' produces output like the following:
Preparing ../foo (identifier foo)
HEAD is now at 26da330922 <title>
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.
--- builtin/worktree.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-)
builtin/worktree.c +36 -2
@@ -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 @@ static int add_worktree(const char *path, const char *refname,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 ;
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
PATCH v9 3/4 worktree: factor out dwim_branch function
In reply to Thomas GummererFactor 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.
--- builtin/worktree.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-)
builtin/worktree.c +18 -11
@@ -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) {
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
PATCH v9 4/4 worktree: teach "add" to check out existing branches
In reply to Thomas GummererCurrently '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.
--- Documentation/git-worktree.txt | 9 +++++++-- builtin/worktree.c | 13 +++++++++++-- t/t2025-worktree-add.sh | 26 +++++++++++++++++++------- 3 files changed, 37 insertions(+), 11 deletions(-)
Documentation/git-worktree.txt +7 -2
@@ -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::
builtin/worktree.c +11 -2
@@ -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 =
t/t2025-worktree-add.sh +19 -7
@@ -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' '
Signature · 1 line
2.16.1.74.g7afd1c25cc.dirty
Quoted 11 lines · Thomas Gummerer
Thanks Eric for the review and the suggestions on the previous round.
Changes since the previous round:
- UNLEAK
new_branchafter it was xstrndup'd
- update the commit message of 2/4 according to Eric's suggestions
- makeforce_new_brancha boolean flag inprint_preparing_worktree_line, instead of using it as the branch name. Instead usenew_branchas the new branch name everywhere in that function.
- get rid of theckeckout_existing_branchflag
Thanks. I did another full review of all the patches and played around with the new functionality again for good measure. Everything looked good, and I think the patches are now ready to graduate.
For what it's worth, the entire series is:
Quoted 13 lines · Eric Sunshine
Quoted 11 lines · Thomas Gummerer
Thanks Eric for the review and the suggestions on the previous round.
Changes since the previous round:
- UNLEAK
new_branchafter it was xstrndup'd
- update the commit message of 2/4 according to Eric's suggestions
- makeforce_new_brancha boolean flag inprint_preparing_worktree_line, instead of using it as the branch name. Instead usenew_branchas the new branch name everywhere in that function.
- get rid of theckeckout_existing_branchflag
Quoted 3 lines
Thanks. I did another full review of all the patches and played around with the new functionality again for good measure. Everything looked good, and I think the patches are now ready to graduate.
Thanks for all your review work on this series!
Quoted 3 lines
For what it's worth, the entire series is:
Reviewed-by Eric Sunshine <[email protected]>
Quoted 14 lines · Thomas Gummerer
Quoted 13 lines · Eric Sunshine
Quoted 11 lines · Thomas Gummerer
Thanks Eric for the review and the suggestions on the previous round.
Changes since the previous round:
- UNLEAK
new_branchafter it was xstrndup'd
- update the commit message of 2/4 according to Eric's suggestions
- makeforce_new_brancha boolean flag inprint_preparing_worktree_line, instead of using it as the branch name. Instead usenew_branchas the new branch name everywhere in that function.
- get rid of theckeckout_existing_branchflag
Quoted 5 lines
Quoted 3 lines
Thanks. I did another full review of all the patches and played around with the new functionality again for good measure. Everything looked good, and I think the patches are now ready to graduate.
Thanks for all your review work on this series!
Thanks, both.
Quoted 8 lines · Thomas Gummerer
Previous rounds are at <[email protected]> and <[email protected]>. Thanks Duy, Eric and Junio for the comments on the previous round.
Junio: I know it is the rc period right now, but the topic is also already queued in 'pu', so I wasn't sure if you prefer me submitting this now, or after 2.17 is released. I'm happy to resend this once 2.17 is out if that makes it easier for you.
Mail archives would not forget messages easily, so it is OK if you sent early.
You just need to set expectations low enough, though ;-) Topics that are not in 'next' before -rc1 is tagged will not be in the upcoming release unless there is an extraordinary reason, and reviews, replacing in 'pu', and advancing topics to 'next' will all get lower priority than keeping an eye on breakages in what is already in 'master' until the final.