Skip to content

Commit 176ea74

Browse files
pcloudsgitster
authored andcommitted
wt-status.c: handle worktree renames
Before 425a28e (diff-lib: allow ita entries treated as "not yet exist in index" - 2016-10-24) there are never "new files" in the index, which essentially disables rename detection because we only detect renames when a new file appears in a diff pair. After that commit, an i-t-a entry can appear as a new file in "git diff-files". But the diff callback function in wt-status.c does not handle this case and produces incorrect status output. PS. The reader may notice that this patch adds a new xstrdup() but not a free(). Yes we leak memory (the same for head_path). But wt_status so far has been short lived, this leak should not matter in practice. Noticed-by: Alex Vandiver <[email protected]> Helped-by: Igor Djordjevic <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 5134ccd commit 176ea74

File tree

3 files changed

+92
-13
lines changed

3 files changed

+92
-13
lines changed

Documentation/git-status.txt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,15 @@ the status.relativePaths config option below.
125125
Short Format
126126
~~~~~~~~~~~~
127127

128-
In the short-format, the status of each path is shown as
128+
In the short-format, the status of each path is shown as one of these
129+
forms
129130

130-
XY PATH1 -> PATH2
131+
XY PATH
132+
XY ORIG_PATH -> PATH
131133

132-
where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
133-
shown only when `PATH1` corresponds to a different path in the
134-
index/worktree (i.e. the file is renamed). The `XY` is a two-letter
135-
status code.
134+
where `ORIG_PATH` is where the renamed/copied contents came
135+
from. `ORIG_PATH` is only shown when the entry is renamed or
136+
copied. The `XY` is a two-letter status code.
136137

137138
The fields (including the `->`) are separated from each other by a
138139
single space. If a filename contains whitespace or other nonprintable
@@ -168,6 +169,8 @@ in which case `XY` are `!!`.
168169
[MARC] index and work tree matches
169170
[ MARC] M work tree changed since index
170171
[ MARC] D deleted in work tree
172+
[ D] R renamed in work tree
173+
[ D] C copied in work tree
171174
-------------------------------------------------
172175
D D unmerged, both deleted
173176
A U unmerged, added by us
@@ -285,13 +288,13 @@ Renamed or copied entries have the following format:
285288
of similarity between the source and target of the
286289
move or copy). For example "R100" or "C75".
287290
<path> The pathname. In a renamed/copied entry, this
288-
is the path in the index and in the working tree.
291+
is the target path.
289292
<sep> When the `-z` option is used, the 2 pathnames are separated
290293
with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
291294
byte separates them.
292-
<origPath> The pathname in the commit at HEAD. This is only
293-
present in a renamed/copied entry, and tells
294-
where the renamed/copied contents came from.
295+
<origPath> The pathname in the commit at HEAD or in the index.
296+
This is only present in a renamed/copied entry, and
297+
tells where the renamed/copied contents came from.
295298
--------------------------------------------------------
296299

297300
Unmerged entries have the following format; the first character is

t/t2203-add-intent.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,65 @@ test_expect_success 'commit: ita entries ignored in empty commit check' '
162162
)
163163
'
164164

165+
test_expect_success 'rename detection finds the right names' '
166+
git init rename-detection &&
167+
(
168+
cd rename-detection &&
169+
echo contents >first &&
170+
git add first &&
171+
git commit -m first &&
172+
mv first third &&
173+
git add -N third &&
174+
175+
git status | grep -v "^?" >actual.1 &&
176+
test_i18ngrep "renamed: *first -> third" actual.1 &&
177+
178+
git status --porcelain | grep -v "^?" >actual.2 &&
179+
cat >expected.2 <<-\EOF &&
180+
R first -> third
181+
EOF
182+
test_cmp expected.2 actual.2 &&
183+
184+
hash=12f00e90b6ef79117ce6e650416b8cf517099b78 &&
185+
git status --porcelain=v2 | grep -v "^?" >actual.3 &&
186+
cat >expected.3 <<-EOF &&
187+
2 .R N... 100644 100644 100644 $hash $hash R100 third first
188+
EOF
189+
test_cmp expected.3 actual.3
190+
)
191+
'
192+
193+
test_expect_success 'double rename detection in status' '
194+
git init rename-detection-2 &&
195+
(
196+
cd rename-detection-2 &&
197+
echo contents >first &&
198+
git add first &&
199+
git commit -m first &&
200+
git mv first second &&
201+
mv second third &&
202+
git add -N third &&
203+
204+
git status | grep -v "^?" >actual.1 &&
205+
test_i18ngrep "renamed: *first -> second" actual.1 &&
206+
test_i18ngrep "renamed: *second -> third" actual.1 &&
207+
208+
git status --porcelain | grep -v "^?" >actual.2 &&
209+
cat >expected.2 <<-\EOF &&
210+
R first -> second
211+
R second -> third
212+
EOF
213+
test_cmp expected.2 actual.2 &&
214+
215+
hash=12f00e90b6ef79117ce6e650416b8cf517099b78 &&
216+
git status --porcelain=v2 | grep -v "^?" >actual.3 &&
217+
cat >expected.3 <<-EOF &&
218+
2 R. N... 100644 100644 100644 $hash $hash R100 second first
219+
2 .R N... 100644 100644 100644 $hash $hash R100 third second
220+
EOF
221+
test_cmp expected.3 actual.3
222+
)
223+
'
224+
165225
test_done
166226

wt-status.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
361361
switch (change_type) {
362362
case WT_STATUS_UPDATED:
363363
status = d->index_status;
364-
if (d->rename_source)
365-
one_name = d->rename_source;
366364
break;
367365
case WT_STATUS_CHANGED:
368366
if (d->new_submodule_commits || d->dirty_submodule) {
@@ -383,6 +381,14 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
383381
change_type);
384382
}
385383

384+
/*
385+
* Only pick up the rename it's relevant. If the rename is for
386+
* the changed section and we're printing the updated section,
387+
* ignore it.
388+
*/
389+
if (d->rename_status == status)
390+
one_name = d->rename_source;
391+
386392
one = quote_path(one_name, s->prefix, &onebuf);
387393
two = quote_path(two_name, s->prefix, &twobuf);
388394

@@ -434,7 +440,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
434440
struct wt_status_change_data *d;
435441

436442
p = q->queue[i];
437-
it = string_list_insert(&s->change, p->one->path);
443+
it = string_list_insert(&s->change, p->two->path);
438444
d = it->util;
439445
if (!d) {
440446
d = xcalloc(1, sizeof(*d));
@@ -461,6 +467,14 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
461467
/* mode_worktree is zero for a delete. */
462468
break;
463469

470+
case DIFF_STATUS_COPIED:
471+
case DIFF_STATUS_RENAMED:
472+
if (d->rename_status)
473+
die("BUG: multiple renames on the same target? how?");
474+
d->rename_source = xstrdup(p->one->path);
475+
d->rename_score = p->score * 100 / MAX_SCORE;
476+
d->rename_status = p->status;
477+
/* fallthru */
464478
case DIFF_STATUS_MODIFIED:
465479
case DIFF_STATUS_TYPE_CHANGED:
466480
case DIFF_STATUS_UNMERGED:
@@ -532,6 +546,8 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
532546

533547
case DIFF_STATUS_COPIED:
534548
case DIFF_STATUS_RENAMED:
549+
if (d->rename_status)
550+
die("BUG: multiple renames on the same target? how?");
535551
d->rename_source = xstrdup(p->one->path);
536552
d->rename_score = p->score * 100 / MAX_SCORE;
537553
d->rename_status = p->status;

0 commit comments

Comments
 (0)