On Thu, May 02, 2019 at 08:05:57PM +0700, Duy Nguyen wrote: > The difficulty will be coming up with some sane UI that can > handle that and not leave too many traps behind. I can't see that UI. Well, I'm still thinking about it. And perhaps a good UI is not that far away. The following hacky patch does three things: 1. it resolves "foo" to "refs/worktree/foo" if neither refs/heads/foo or refs/tags/foo exists. 2. it pretty prints refs/worktree/foo as worktree/foo 3. "git branch" has a new option --per-worktree that creates refs/worktree/foo instead of refs/heads/foo. The idea here is we manage/decide per-worktree or shared refs at branch/tag creation time. After that we use it like a normal branch/tag. Point #1 helps the "normal" part. Point #2 could even pretty print it to "foo" to make it even more normal, but I feel that the distinction between per-worktree and shared should still be more visible, hence "worktree/foo". --per-worktree is pretty much a placeholder name. If we could find a good name (and --worktre is already taken), then it could be added in more places where a branch may be created. It's far from complete. For example, refs/worktree/foo is not considered a branch by any command, Only those inside refs/heads are at the moment. When I added refs/worktree/ namespace I didn't think that far ahead, what kind of refs should be in there. But maybe we can still have branches in refs/worktree/heads/ and tags in refs/worktree/tags/. Or perhaps all refs/worktree/ should only contain branches, tags are always shared (it's shared with remotes now even, at least in common case)... With more details worked out, perhaps we can make it work. -- 8< -- diff --git a/branch.c b/branch.c index 28b81a7e02..e5d738efa7 100644 --- a/branch.c +++ b/branch.c @@ -242,6 +242,8 @@ N_("\n" "will track its remote counterpart, you may want to use\n" "\"git push -u\" to set the upstream config as you push."); +int create_branch_per_worktree; + void create_branch(struct repository *r, const char *name, const char *start_name, int force, int clobber_head_ok, int reflog, @@ -308,6 +310,9 @@ void create_branch(struct repository *r, if (reflog) log_all_ref_updates = LOG_REFS_NORMAL; + if (create_branch_per_worktree) + strbuf_splice(&ref, 0, 11, "refs/worktree/", 14); + if (!dont_change_ref) { struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; diff --git a/builtin/branch.c b/builtin/branch.c index d4359b33ac..8839c6f33f 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -605,6 +605,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int icase = 0; static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; struct ref_format format = REF_FORMAT_INIT; + int per_worktree = 0; struct option options[] = { OPT_GROUP(N_("Generic options")), @@ -613,6 +614,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("suppress informational messages")), OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"), BRANCH_TRACK_EXPLICIT), + OPT_BOOL(0, "per-worktree", &per_worktree, N_("create per-worktree branch")), OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"), BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN), OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), @@ -829,12 +831,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) git_config_set_multivar(buf.buf, NULL, NULL, 1); strbuf_release(&buf); } else if (argc > 0 && argc <= 2) { + extern int create_branch_per_worktree; + if (filter.kind != FILTER_REFS_BRANCHES) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); if (track == BRANCH_TRACK_OVERRIDE) die(_("the '--set-upstream' option is no longer supported. Please use '--track' or '--set-upstream-to' instead.")); + create_branch_per_worktree = per_worktree; create_branch(the_repository, argv[0], (argc == 2) ? argv[1] : head, force, 0, reflog, quiet, track); diff --git a/refs.c b/refs.c index 142888a40a..8d01a80e07 100644 --- a/refs.c +++ b/refs.c @@ -479,7 +479,9 @@ const char *prettify_refname(const char *name) { if (skip_prefix(name, "refs/heads/", &name) || skip_prefix(name, "refs/tags/", &name) || - skip_prefix(name, "refs/remotes/", &name)) + skip_prefix(name, "refs/remotes/", &name) || + (starts_with(name, "refs/worktree/") && + skip_prefix(name, "refs/", &name))) ; /* nothing */ return name; } @@ -491,6 +493,7 @@ static const char *ref_rev_parse_rules[] = { "refs/heads/%.*s", "refs/remotes/%.*s", "refs/remotes/%.*s/HEAD", + "refs/worktree/%.*s", NULL }; -- 8< --