status: show more info than "currently not on any branch"
When a remote ref or a tag is checked out, HEAD is automatically detached. There is no user-friendly way to find out what ref is checked out in this case. This patch digs in reflog for this information and shows "HEAD detached from origin/master" or "HEAD detached at v1.8.0" instead of "currently not on any branch". When it cannot figure out the original ref, it shows an abbreviated SHA-1. "Currently not on any branch" would never display (unless reflog is pruned to near empty that the last checkout entry is lost). Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
Mar 13, 2013 at 18:42 UTC
b397ea4863a143e97cb9da76c6c18cc555537d64
4 files changed
+125
-26
t/t7406-submodule-update.sh
+4
-2
@@ -664,8 +664,10 @@ test_expect_success 'submodule add properly re-creates deeper level submodules'
664
665
test_expect_success 'submodule update properly revives a moved submodule' '
666
(cd super &&
667
+ H=$(git rev-parse --short HEAD) &&
668
git commit -am "pre move" &&
668
- git status >expect&&
669
+ H2=$(git rev-parse --short HEAD) &&
670
+ git status | sed "s/$H/XXX/" >expect &&
671
H=$(cd submodule2; git rev-parse HEAD) &&
672
git rm --cached submodule2 &&
673
rm -rf submodule2 &&
@@ -674,7 +676,7 @@ test_expect_success 'submodule update properly revives a moved submodule' '
676
git config -f .gitmodules submodule.submodule2.path "moved/sub module"
677
git commit -am "post move" &&
678
git submodule update &&
677
- git status >actual &&
679
+ git status | sed "s/$H2/XXX/" >actual &&
680
test_cmp expect actual
681
)
682
'
t/t7512-status-help.sh
+33
-19
@@ -76,7 +76,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'
76
ONTO=$(git rev-parse --short HEAD^^) &&
77
test_must_fail git rebase HEAD^ --onto HEAD^^ &&
78
cat >expected <<-EOF &&
79
- # Not currently on any branch.
79
+ # HEAD detached at $ONTO
80
# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
81
# (fix conflicts and then run "git rebase --continue")
82
# (use "git rebase --skip" to skip this patch)
@@ -103,7 +103,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '
103
echo three >main.txt &&
104
git add main.txt &&
105
cat >expected <<-EOF &&
106
- # Not currently on any branch.
106
+ # HEAD detached at $ONTO
107
# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
108
# (all conflicts fixed: run "git rebase --continue")
109
#
@@ -135,7 +135,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '
135
ONTO=$(git rev-parse --short rebase_i_conflicts) &&
136
test_must_fail git rebase -i rebase_i_conflicts &&
137
cat >expected <<-EOF &&
138
- # Not currently on any branch.
138
+ # HEAD detached at $ONTO
139
# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
140
# (fix conflicts and then run "git rebase --continue")
141
# (use "git rebase --skip" to skip this patch)
@@ -161,7 +161,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '
161
test_must_fail git rebase -i rebase_i_conflicts &&
162
git add main.txt &&
163
cat >expected <<-EOF &&
164
- # Not currently on any branch.
164
+ # HEAD detached at $ONTO
165
# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
166
# (all conflicts fixed: run "git rebase --continue")
167
#
@@ -187,9 +187,10 @@ test_expect_success 'status when rebasing -i in edit mode' '
187
export FAKE_LINES &&
188
test_when_finished "git rebase --abort" &&
189
ONTO=$(git rev-parse --short HEAD~2) &&
190
+ TGT=$(git rev-parse --short two_rebase_i) &&
191
git rebase -i HEAD~2 &&
192
cat >expected <<-EOF &&
192
- # Not currently on any branch.
193
+ # HEAD detached from $TGT
194
# You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
195
# (use "git commit --amend" to amend the current commit)
196
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -214,8 +215,9 @@ test_expect_success 'status when splitting a commit' '
215
ONTO=$(git rev-parse --short HEAD~3) &&
216
git rebase -i HEAD~3 &&
217
git reset HEAD^ &&
218
+ TGT=$(git rev-parse --short HEAD) &&
219
cat >expected <<-EOF &&
218
- # Not currently on any branch.
220
+ # HEAD detached at $TGT
221
# You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
222
# (Once your working directory is clean, run "git rebase --continue")
223
#
@@ -243,10 +245,11 @@ test_expect_success 'status after editing the last commit with --amend during a
245
export FAKE_LINES &&
246
test_when_finished "git rebase --abort" &&
247
ONTO=$(git rev-parse --short HEAD~3) &&
248
+ TGT=$(git rev-parse --short three_amend) &&
249
git rebase -i HEAD~3 &&
250
git commit --amend -m "foo" &&
251
cat >expected <<-EOF &&
249
- # Not currently on any branch.
252
+ # HEAD detached from $TGT
253
# You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
254
# (use "git commit --amend" to amend the current commit)
255
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -276,7 +279,7 @@ test_expect_success 'status: (continue first edit) second edit' '
279
git rebase -i HEAD~3 &&
280
git rebase --continue &&
281
cat >expected <<-EOF &&
279
- # Not currently on any branch.
282
+ # HEAD detached from $ONTO
283
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
284
# (use "git commit --amend" to amend the current commit)
285
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -298,7 +301,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '
301
git rebase --continue &&
302
git reset HEAD^ &&
303
cat >expected <<-EOF &&
301
- # Not currently on any branch.
304
+ # HEAD detached from $ONTO
305
# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
306
# (Once your working directory is clean, run "git rebase --continue")
307
#
@@ -325,7 +328,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '
328
git rebase --continue &&
329
git commit --amend -m "foo" &&
330
cat >expected <<-EOF &&
328
- # Not currently on any branch.
331
+ # HEAD detached from $ONTO
332
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
333
# (use "git commit --amend" to amend the current commit)
334
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -347,7 +350,7 @@ test_expect_success 'status: (amend first edit) second edit' '
350
git commit --amend -m "a" &&
351
git rebase --continue &&
352
cat >expected <<-EOF &&
350
- # Not currently on any branch.
353
+ # HEAD detached from $ONTO
354
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
355
# (use "git commit --amend" to amend the current commit)
356
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -370,7 +373,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '
373
git rebase --continue &&
374
git reset HEAD^ &&
375
cat >expected <<-EOF &&
373
- # Not currently on any branch.
376
+ # HEAD detached from $ONTO
377
# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
378
# (Once your working directory is clean, run "git rebase --continue")
379
#
@@ -398,7 +401,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '
401
git rebase --continue &&
402
git commit --amend -m "d" &&
403
cat >expected <<-EOF &&
401
- # Not currently on any branch.
404
+ # HEAD detached from $ONTO
405
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
406
# (use "git commit --amend" to amend the current commit)
407
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -422,7 +425,7 @@ test_expect_success 'status: (split first edit) second edit' '
425
git commit -m "e" &&
426
git rebase --continue &&
427
cat >expected <<-EOF &&
425
- # Not currently on any branch.
428
+ # HEAD detached from $ONTO
429
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
430
# (use "git commit --amend" to amend the current commit)
431
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -447,7 +450,7 @@ test_expect_success 'status: (split first edit) second edit and split' '
450
git rebase --continue &&
451
git reset HEAD^ &&
452
cat >expected <<-EOF &&
450
- # Not currently on any branch.
453
+ # HEAD detached from $ONTO
454
# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
455
# (Once your working directory is clean, run "git rebase --continue")
456
#
@@ -477,7 +480,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '
480
git rebase --continue &&
481
git commit --amend -m "h" &&
482
cat >expected <<-EOF &&
480
- # Not currently on any branch.
483
+ # HEAD detached from $ONTO
484
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
485
# (use "git commit --amend" to amend the current commit)
486
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -572,8 +575,9 @@ test_expect_success 'status when bisecting' '
575
git bisect start &&
576
git bisect bad &&
577
git bisect good one_bisect &&
575
- cat >expected <<-\EOF &&
576
- # Not currently on any branch.
578
+ TGT=$(git rev-parse --short two_bisect) &&
579
+ cat >expected <<-EOF &&
580
+ # HEAD detached at $TGT
581
# You are currently bisecting branch '\''bisect'\''.
582
# (use "git bisect reset" to get back to the original branch)
583
#
@@ -596,7 +600,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
600
ONTO=$(git rev-parse --short HEAD^^) &&
601
test_must_fail git rebase HEAD^ --onto HEAD^^ &&
602
cat >expected <<-EOF &&
599
- # Not currently on any branch.
603
+ # HEAD detached at $ONTO
604
# You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
605
#
606
# Unmerged paths:
@@ -662,5 +666,15 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '
666
test_i18ncmp expected actual
667
'
668
669
+test_expect_success 'status showing detached from a tag' '
670
+ test_commit atag tagging &&
671
+ git checkout atag &&
672
+ cat >expected <<-\EOF
673
+ # HEAD detached at atag
674
+ nothing to commit (use -u to show untracked files)
675
+ EOF
676
+ git status --untracked-files=no >actual &&
677
+ test_i18ncmp expected actual
678
+'
679
680
test_done
wt-status.c
+85
-4
@@ -999,7 +999,73 @@ got_nothing:
999
return NULL;
1000
}
1001
1002
-void wt_status_get_state(struct wt_status_state *state)
1002
+struct grab_1st_switch_cbdata {
1003
+ int found;
1004
+ struct strbuf buf;
1005
+ unsigned char nsha1[20];
1006
+};
1007
+
1008
+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
1009
+ const char *email, unsigned long timestamp, int tz,
1010
+ const char *message, void *cb_data)
1011
+{
1012
+ struct grab_1st_switch_cbdata *cb = cb_data;
1013
+ const char *target = NULL, *end;
1014
+
1015
+ if (prefixcmp(message, "checkout: moving from "))
1016
+ return 0;
1017
+ message += strlen("checkout: moving from ");
1018
+ target = strstr(message, " to ");
1019
+ if (!target)
1020
+ return 0;
1021
+ target += strlen(" to ");
1022
+ strbuf_reset(&cb->buf);
1023
+ hashcpy(cb->nsha1, nsha1);
1024
+ for (end = target; *end && *end != '\n'; end++)
1025
+ ;
1026
+ strbuf_add(&cb->buf, target, end - target);
1027
+ cb->found = 1;
1028
+ return 1;
1029
+}
1030
+
1031
+static void wt_status_get_detached_from(struct wt_status_state *state)
1032
+{
1033
+ struct grab_1st_switch_cbdata cb;
1034
+ struct commit *commit;
1035
+ unsigned char sha1[20];
1036
+ char *ref = NULL;
1037
+
1038
+ strbuf_init(&cb.buf, 0);
1039
+ if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
1040
+ strbuf_release(&cb.buf);
1041
+ return;
1042
+ }
1043
+
1044
+ if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
1045
+ /* sha1 is a commit? match without further lookup */
1046
+ (!hashcmp(cb.nsha1, sha1) ||
1047
+ /* perhaps sha1 is a tag, try to dereference to a commit */
1048
+ ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
1049
+ !hashcmp(cb.nsha1, commit->object.sha1)))) {
1050
+ int ofs;
1051
+ if (!prefixcmp(ref, "refs/tags/"))
1052
+ ofs = strlen("refs/tags/");
1053
+ else if (!prefixcmp(ref, "refs/remotes/"))
1054
+ ofs = strlen("refs/remotes/");
1055
+ else
1056
+ ofs = 0;
1057
+ state->detached_from = xstrdup(ref + ofs);
1058
+ } else
1059
+ state->detached_from =
1060
+ xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
1061
+ hashcpy(state->detached_sha1, cb.nsha1);
1062
+
1063
+ free(ref);
1064
+ strbuf_release(&cb.buf);
1065
+}
1066
+
1067
+void wt_status_get_state(struct wt_status_state *state,
1068
+ int get_detached_from)
1069
{
1070
struct stat st;
1071
@@ -1029,6 +1095,9 @@ void wt_status_get_state(struct wt_status_state *state)
1095
state->bisect_in_progress = 1;
1096
state->branch = read_and_strip_branch("BISECT_START");
1097
}
1098
+
1099
+ if (get_detached_from)
1100
+ wt_status_get_detached_from(state);
1101
}
1102
1103
static void wt_status_print_state(struct wt_status *s,
@@ -1054,7 +1123,8 @@ void wt_status_print(struct wt_status *s)
1123
struct wt_status_state state;
1124
1125
memset(&state, 0, sizeof(state));
1057
- wt_status_get_state(&state);
1126
+ wt_status_get_state(&state,
1127
+ s->branch && !strcmp(s->branch, "HEAD"));
1128
1129
if (s->branch) {
1130
const char *on_what = _("On branch ");
@@ -1062,9 +1132,19 @@ void wt_status_print(struct wt_status *s)
1132
if (!prefixcmp(branch_name, "refs/heads/"))
1133
branch_name += 11;
1134
else if (!strcmp(branch_name, "HEAD")) {
1065
- branch_name = "";
1135
branch_status_color = color(WT_STATUS_NOBRANCH, s);
1067
- on_what = _("Not currently on any branch.");
1136
+ if (state.detached_from) {
1137
+ unsigned char sha1[20];
1138
+ branch_name = state.detached_from;
1139
+ if (!get_sha1("HEAD", sha1) &&
1140
+ !hashcmp(sha1, state.detached_sha1))
1141
+ on_what = _("HEAD detached at ");
1142
+ else
1143
+ on_what = _("HEAD detached from ");
1144
+ } else {
1145
+ branch_name = "";
1146
+ on_what = _("Not currently on any branch.");
1147
+ }
1148
}
1149
status_printf(s, color(WT_STATUS_HEADER, s), "");
1150
status_printf_more(s, branch_status_color, "%s", on_what);
@@ -1076,6 +1156,7 @@ void wt_status_print(struct wt_status *s)
1156
wt_status_print_state(s, &state);
1157
free(state.branch);
1158
free(state.onto);
1159
+ free(state.detached_from);
1160
1161
if (s->is_initial) {
1162
status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
wt-status.h
+3
-1
@@ -81,12 +81,14 @@ struct wt_status_state {
81
int bisect_in_progress;
82
char *branch;
83
char *onto;
84
+ char *detached_from;
85
+ unsigned char detached_sha1[20];
86
};
87
88
void wt_status_prepare(struct wt_status *s);
89
void wt_status_print(struct wt_status *s);
90
void wt_status_collect(struct wt_status *s);
89
-void wt_status_get_state(struct wt_status_state *state);
91
+void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
92
93
void wt_shortstatus_print(struct wt_status *s);
94
void wt_porcelain_print(struct wt_status *s);