@matheusbsbs / matheus-bagliano / commits / f24c30e0b6
Fork 0

wt-status: tolerate dangling marks

When a user checks out the upstream branch of HEAD, the upstream branch not being a local branch, and then runs "git status", like this: git clone $URL client cd client git checkout @{u} git status no status is printed, but instead an error message: fatal: HEAD does not point to a branch (This error message when running "git branch" persists even after checking out other things - it only stops after checking out a branch.) This is because "git status" reads the reflog when determining the "HEAD detached" message, and thus attempts to DWIM "@{u}", but that doesn't work because HEAD no longer points to a branch. Therefore, when calculating the status of a worktree, tolerate dangling marks. This is done by adding an additional parameter to dwim_ref() and repo_dwim_ref(). Signed-off-by: Jonathan Tan <jonathantanmy@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jonathan Tan committed Sep 1, 2020 at 15:28 UTC f24c30e0b6b13078d8fc7cd71b9989d28fd76610
19 files changed +60 -29
archive.c
+2 -2
@@ -397,10 +397,10 @@ static void parse_treeish_arg(const char **argv,
397 const char *colon = strchrnul(name, ':');
398 int refnamelen = colon - name;
399
400 - if (!dwim_ref(name, refnamelen, &oid, &ref))
400 + if (!dwim_ref(name, refnamelen, &oid, &ref, 0))
401 die(_("no such ref: %.*s"), refnamelen, name);
402 } else {
403 - dwim_ref(name, strlen(name), &oid, &ref);
403 + dwim_ref(name, strlen(name), &oid, &ref, 0);
404 }
405
406 if (get_oid(name, &oid))
branch.c
+1 -1
@@ -281,7 +281,7 @@ void create_branch(struct repository *r,
281 die(_("Not a valid object name: '%s'."), start_name);
282 }
283
284 - switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref)) {
284 + switch (dwim_ref(start_name, strlen(start_name), &oid, &real_ref, 0)) {
285 case 0:
286 /* Not branching from any existing branch */
287 if (explicit_tracking)
builtin/checkout.c
+2 -2
@@ -650,7 +650,7 @@ static void setup_branch_path(struct branch_info *branch)
650 * If this is a ref, resolve it; otherwise, look up the OID for our
651 * expression. Failure here is okay.
652 */
653 - if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname))
653 + if (!dwim_ref(branch->name, strlen(branch->name), &branch->oid, &branch->refname, 0))
654 repo_get_oid_committish(the_repository, branch->name, &branch->oid);
655
656 strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
@@ -1349,7 +1349,7 @@ static void die_expecting_a_branch(const struct branch_info *branch_info)
1349 struct object_id oid;
1350 char *to_free;
1351
1352 - if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free) == 1) {
1352 + if (dwim_ref(branch_info->name, strlen(branch_info->name), &oid, &to_free, 0) == 1) {
1353 const char *ref = to_free;
1354
1355 if (skip_prefix(ref, "refs/tags/", &ref))
builtin/fast-export.c
+1 -1
@@ -943,7 +943,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
943 if (e->flags & UNINTERESTING)
944 continue;
945
946 - if (dwim_ref(e->name, strlen(e->name), &oid, &full_name) != 1)
946 + if (dwim_ref(e->name, strlen(e->name), &oid, &full_name, 0) != 1)
947 continue;
948
949 if (refspecs.nr) {
builtin/log.c
+1 -1
@@ -1062,7 +1062,7 @@ static char *find_branch_name(struct rev_info *rev)
1062 return NULL;
1063 ref = rev->cmdline.rev[positive].name;
1064 tip_oid = &rev->cmdline.rev[positive].item->oid;
1065 - if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
1065 + if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref, 0) &&
1066 skip_prefix(full_ref, "refs/heads/", &v) &&
1067 oideq(tip_oid, &branch_oid))
1068 branch = xstrdup(v);
builtin/merge.c
+1 -1
@@ -501,7 +501,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
501 if (!remote_head)
502 die(_("'%s' does not point to a commit"), remote);
503
504 - if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref) > 0) {
504 + if (dwim_ref(remote, strlen(remote), &branch_head, &found_ref, 0) > 0) {
505 if (starts_with(found_ref, "refs/heads/")) {
506 strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
507 oid_to_hex(&branch_head), remote);
builtin/reset.c
+1 -1
@@ -423,7 +423,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
423 char *ref = NULL;
424 int err;
425
426 - dwim_ref(rev, strlen(rev), &dummy, &ref);
426 + dwim_ref(rev, strlen(rev), &dummy, &ref, 0);
427 if (ref && !starts_with(ref, "refs/"))
428 ref = NULL;
429
builtin/rev-parse.c
+1 -1
@@ -136,7 +136,7 @@ static void show_rev(int type, const struct object_id *oid, const char *name)
136 struct object_id discard;
137 char *full;
138
139 - switch (dwim_ref(name, strlen(name), &discard, &full)) {
139 + switch (dwim_ref(name, strlen(name), &discard, &full, 0)) {
140 case 0:
141 /*
142 * Not found -- not a ref. We could
builtin/show-branch.c
+1 -1
@@ -741,7 +741,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
741 die(Q_("only %d entry can be shown at one time.",
742 "only %d entries can be shown at one time.",
743 MAX_REVS), MAX_REVS);
744 - if (!dwim_ref(*av, strlen(*av), &oid, &ref))
744 + if (!dwim_ref(*av, strlen(*av), &oid, &ref, 0))
745 die(_("no such ref %s"), *av);
746
747 /* Has the base been specified? */
builtin/stash.c
+1 -1
@@ -185,7 +185,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
185 end_of_rev = strchrnul(revision, '@');
186 strbuf_add(&symbolic, revision, end_of_rev - revision);
187
188 - ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
188 + ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref, 0);
189 strbuf_release(&symbolic);
190 switch (ret) {
191 case 0: /* Not found, but valid ref */
bundle.c
+1 -1
@@ -377,7 +377,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
377
378 if (e->item->flags & UNINTERESTING)
379 continue;
380 - if (dwim_ref(e->name, strlen(e->name), &oid, &ref) != 1)
380 + if (dwim_ref(e->name, strlen(e->name), &oid, &ref, 0) != 1)
381 goto skip_write_ref;
382 if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
383 flag = 0;
cache.h
+7
@@ -1567,6 +1567,13 @@ struct interpret_branch_name_options {
1567 * allowed, even ones to refs outside of those namespaces.
1568 */
1569 unsigned allowed;
1570 +
1571 + /*
1572 + * If ^{upstream} or ^{push} (or equivalent) is requested, and the
1573 + * branch in question does not have such a reference, return -1 instead
1574 + * of die()-ing.
1575 + */
1576 + unsigned nonfatal_dangling_mark : 1;
1577 };
1578 int repo_interpret_branch_name(struct repository *r,
1579 const char *str, int len,
commit.c
+1 -1
@@ -921,7 +921,7 @@ struct commit *get_fork_point(const char *refname, struct commit *commit)
921 struct commit *ret = NULL;
922 char *full_refname;
923
924 - switch (dwim_ref(refname, strlen(refname), &oid, &full_refname)) {
924 + switch (dwim_ref(refname, strlen(refname), &oid, &full_refname, 0)) {
925 case 0:
926 die("No such ref: '%s'", refname);
927 case 1:
refs.c
+9 -5
@@ -598,10 +598,13 @@ const char *git_default_branch_name(void)
598 * to name a branch.
599 */
600 static char *substitute_branch_name(struct repository *r,
601 - const char **string, int *len)
601 + const char **string, int *len,
602 + int nonfatal_dangling_mark)
603 {
604 struct strbuf buf = STRBUF_INIT;
604 - struct interpret_branch_name_options options = { 0 } ;
605 + struct interpret_branch_name_options options = {
606 + .nonfatal_dangling_mark = nonfatal_dangling_mark
607 + };
608 int ret = repo_interpret_branch_name(r, *string, *len, &buf, &options);
609
610 if (ret == *len) {
@@ -615,9 +618,10 @@ static char *substitute_branch_name(struct repository *r,
618 }
619
620 int repo_dwim_ref(struct repository *r, const char *str, int len,
618 - struct object_id *oid, char **ref)
621 + struct object_id *oid, char **ref, int nonfatal_dangling_mark)
622 {
620 - char *last_branch = substitute_branch_name(r, &str, &len);
623 + char *last_branch = substitute_branch_name(r, &str, &len,
624 + nonfatal_dangling_mark);
625 int refs_found = expand_ref(r, str, len, oid, ref);
626 free(last_branch);
627 return refs_found;
@@ -661,7 +665,7 @@ int repo_dwim_log(struct repository *r, const char *str, int len,
665 struct object_id *oid, char **log)
666 {
667 struct ref_store *refs = get_main_ref_store(r);
664 - char *last_branch = substitute_branch_name(r, &str, &len);
668 + char *last_branch = substitute_branch_name(r, &str, &len, 0);
669 const char **p;
670 int logs_found = 0;
671 struct strbuf path = STRBUF_INIT;
refs.h
+5 -3
@@ -151,12 +151,14 @@ struct argv_array;
151 void expand_ref_prefix(struct argv_array *prefixes, const char *prefix);
152
153 int expand_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
154 -int repo_dwim_ref(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
154 +int repo_dwim_ref(struct repository *r, const char *str, int len,
155 + struct object_id *oid, char **ref, int nonfatal_dangling_mark);
156 int repo_dwim_log(struct repository *r, const char *str, int len, struct object_id *oid, char **ref);
157 static inline int dwim_ref(const char *str, int len, struct object_id *oid,
157 - char **ref)
158 + char **ref, int nonfatal_dangling_mark)
159 {
159 - return repo_dwim_ref(the_repository, str, len, oid, ref);
160 + return repo_dwim_ref(the_repository, str, len, oid, ref,
161 + nonfatal_dangling_mark);
162 }
163 int dwim_log(const char *str, int len, struct object_id *oid, char **ref);
164
remote.c
+1 -1
@@ -1558,7 +1558,7 @@ static void set_merge(struct branch *ret)
1558 strcmp(ret->remote_name, "."))
1559 continue;
1560 if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
1561 - &oid, &ref) == 1)
1561 + &oid, &ref, 0) == 1)
1562 ret->merge[i]->dst = ref;
1563 else
1564 ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
sha1-name.c
+11 -5
@@ -809,7 +809,7 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
809
810 if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) {
811 if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
812 - refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref);
812 + refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref, 0);
813 if (refs_found > 0) {
814 warning(warn_msg, len, str);
815 if (advice_object_name_warning)
@@ -860,11 +860,11 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
860
861 if (!len && reflog_len)
862 /* allow "@{...}" to mean the current branch reflog */
863 - refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref);
863 + refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref, 0);
864 else if (reflog_len)
865 refs_found = repo_dwim_log(r, str, len, oid, &real_ref);
866 else
867 - refs_found = repo_dwim_ref(r, str, len, oid, &real_ref);
867 + refs_found = repo_dwim_ref(r, str, len, oid, &real_ref, 0);
868
869 if (!refs_found)
870 return -1;
@@ -1496,8 +1496,14 @@ static int interpret_branch_mark(struct repository *r,
1496 branch = branch_get(NULL);
1497
1498 value = get_data(branch, &err);
1499 - if (!value)
1500 - die("%s", err.buf);
1499 + if (!value) {
1500 + if (options->nonfatal_dangling_mark) {
1501 + strbuf_release(&err);
1502 + return -1;
1503 + } else {
1504 + die("%s", err.buf);
1505 + }
1506 + }
1507
1508 if (!branch_interpret_allowed(value, options->allowed))
1509 return -1;
t/t7508-status.sh
+12
@@ -846,6 +846,18 @@ test_expect_success 'status refreshes the index' '
846 test_cmp expect output
847 '
848
849 +test_expect_success 'status shows detached HEAD properly after checking out non-local upstream branch' '
850 + test_when_finished rm -rf upstream downstream actual &&
851 +
852 + test_create_repo upstream &&
853 + test_commit -C upstream foo &&
854 +
855 + git clone upstream downstream &&
856 + git -C downstream checkout @{u} &&
857 + git -C downstream status >actual &&
858 + test_i18ngrep "HEAD detached at [0-9a-f]\\+" actual
859 +'
860 +
861 test_expect_success 'setup status submodule summary' '
862 test_create_repo sm && (
863 cd sm &&
wt-status.c
+1 -1
@@ -1574,7 +1574,7 @@ static void wt_status_get_detached_from(struct repository *r,
1574 return;
1575 }
1576
1577 - if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref) == 1 &&
1577 + if (dwim_ref(cb.buf.buf, cb.buf.len, &oid, &ref, 1) == 1 &&
1578 /* sha1 is a commit? match without further lookup */
1579 (oideq(&cb.noid, &oid) ||
1580 /* perhaps sha1 is a tag, try to dereference to a commit */