Re: "git branch -f" corrupt other worktree
- Date: Fri, 3 May 2019 00:16:13 +0700
- From: Duy Nguyen <pclouds@xxxxxxxxx>
- Subject: Re: "git branch -f" corrupt other worktree
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< --