wt-status.c: handle worktree renames
Before 425a28e0a4
(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 <alexmv@dropbox.com>
Helped-by: Igor Djordjevic <igor.d.djordjevic@gmail.com>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
5134ccde64
commit
176ea74793
@ -125,14 +125,15 @@ the status.relativePaths config option below.
|
|||||||
Short Format
|
Short Format
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
In the short-format, the status of each path is shown as
|
In the short-format, the status of each path is shown as one of these
|
||||||
|
forms
|
||||||
|
|
||||||
XY PATH1 -> PATH2
|
XY PATH
|
||||||
|
XY ORIG_PATH -> PATH
|
||||||
|
|
||||||
where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
|
where `ORIG_PATH` is where the renamed/copied contents came
|
||||||
shown only when `PATH1` corresponds to a different path in the
|
from. `ORIG_PATH` is only shown when the entry is renamed or
|
||||||
index/worktree (i.e. the file is renamed). The `XY` is a two-letter
|
copied. The `XY` is a two-letter status code.
|
||||||
status code.
|
|
||||||
|
|
||||||
The fields (including the `->`) are separated from each other by a
|
The fields (including the `->`) are separated from each other by a
|
||||||
single space. If a filename contains whitespace or other nonprintable
|
single space. If a filename contains whitespace or other nonprintable
|
||||||
@ -168,6 +169,8 @@ in which case `XY` are `!!`.
|
|||||||
[MARC] index and work tree matches
|
[MARC] index and work tree matches
|
||||||
[ MARC] M work tree changed since index
|
[ MARC] M work tree changed since index
|
||||||
[ MARC] D deleted in work tree
|
[ MARC] D deleted in work tree
|
||||||
|
[ D] R renamed in work tree
|
||||||
|
[ D] C copied in work tree
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
D D unmerged, both deleted
|
D D unmerged, both deleted
|
||||||
A U unmerged, added by us
|
A U unmerged, added by us
|
||||||
@ -285,13 +288,13 @@ Renamed or copied entries have the following format:
|
|||||||
of similarity between the source and target of the
|
of similarity between the source and target of the
|
||||||
move or copy). For example "R100" or "C75".
|
move or copy). For example "R100" or "C75".
|
||||||
<path> The pathname. In a renamed/copied entry, this
|
<path> The pathname. In a renamed/copied entry, this
|
||||||
is the path in the index and in the working tree.
|
is the target path.
|
||||||
<sep> When the `-z` option is used, the 2 pathnames are separated
|
<sep> When the `-z` option is used, the 2 pathnames are separated
|
||||||
with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
|
with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
|
||||||
byte separates them.
|
byte separates them.
|
||||||
<origPath> The pathname in the commit at HEAD. This is only
|
<origPath> The pathname in the commit at HEAD or in the index.
|
||||||
present in a renamed/copied entry, and tells
|
This is only present in a renamed/copied entry, and
|
||||||
where the renamed/copied contents came from.
|
tells where the renamed/copied contents came from.
|
||||||
--------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
Unmerged entries have the following format; the first character is
|
Unmerged entries have the following format; the first character is
|
||||||
|
@ -162,5 +162,65 @@ test_expect_success 'commit: ita entries ignored in empty commit check' '
|
|||||||
)
|
)
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'rename detection finds the right names' '
|
||||||
|
git init rename-detection &&
|
||||||
|
(
|
||||||
|
cd rename-detection &&
|
||||||
|
echo contents >first &&
|
||||||
|
git add first &&
|
||||||
|
git commit -m first &&
|
||||||
|
mv first third &&
|
||||||
|
git add -N third &&
|
||||||
|
|
||||||
|
git status | grep -v "^?" >actual.1 &&
|
||||||
|
test_i18ngrep "renamed: *first -> third" actual.1 &&
|
||||||
|
|
||||||
|
git status --porcelain | grep -v "^?" >actual.2 &&
|
||||||
|
cat >expected.2 <<-\EOF &&
|
||||||
|
R first -> third
|
||||||
|
EOF
|
||||||
|
test_cmp expected.2 actual.2 &&
|
||||||
|
|
||||||
|
hash=12f00e90b6ef79117ce6e650416b8cf517099b78 &&
|
||||||
|
git status --porcelain=v2 | grep -v "^?" >actual.3 &&
|
||||||
|
cat >expected.3 <<-EOF &&
|
||||||
|
2 .R N... 100644 100644 100644 $hash $hash R100 third first
|
||||||
|
EOF
|
||||||
|
test_cmp expected.3 actual.3
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'double rename detection in status' '
|
||||||
|
git init rename-detection-2 &&
|
||||||
|
(
|
||||||
|
cd rename-detection-2 &&
|
||||||
|
echo contents >first &&
|
||||||
|
git add first &&
|
||||||
|
git commit -m first &&
|
||||||
|
git mv first second &&
|
||||||
|
mv second third &&
|
||||||
|
git add -N third &&
|
||||||
|
|
||||||
|
git status | grep -v "^?" >actual.1 &&
|
||||||
|
test_i18ngrep "renamed: *first -> second" actual.1 &&
|
||||||
|
test_i18ngrep "renamed: *second -> third" actual.1 &&
|
||||||
|
|
||||||
|
git status --porcelain | grep -v "^?" >actual.2 &&
|
||||||
|
cat >expected.2 <<-\EOF &&
|
||||||
|
R first -> second
|
||||||
|
R second -> third
|
||||||
|
EOF
|
||||||
|
test_cmp expected.2 actual.2 &&
|
||||||
|
|
||||||
|
hash=12f00e90b6ef79117ce6e650416b8cf517099b78 &&
|
||||||
|
git status --porcelain=v2 | grep -v "^?" >actual.3 &&
|
||||||
|
cat >expected.3 <<-EOF &&
|
||||||
|
2 R. N... 100644 100644 100644 $hash $hash R100 second first
|
||||||
|
2 .R N... 100644 100644 100644 $hash $hash R100 third second
|
||||||
|
EOF
|
||||||
|
test_cmp expected.3 actual.3
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
|
||||||
|
22
wt-status.c
22
wt-status.c
@ -361,8 +361,6 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
|
|||||||
switch (change_type) {
|
switch (change_type) {
|
||||||
case WT_STATUS_UPDATED:
|
case WT_STATUS_UPDATED:
|
||||||
status = d->index_status;
|
status = d->index_status;
|
||||||
if (d->rename_source)
|
|
||||||
one_name = d->rename_source;
|
|
||||||
break;
|
break;
|
||||||
case WT_STATUS_CHANGED:
|
case WT_STATUS_CHANGED:
|
||||||
if (d->new_submodule_commits || d->dirty_submodule) {
|
if (d->new_submodule_commits || d->dirty_submodule) {
|
||||||
@ -383,6 +381,14 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
|
|||||||
change_type);
|
change_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only pick up the rename it's relevant. If the rename is for
|
||||||
|
* the changed section and we're printing the updated section,
|
||||||
|
* ignore it.
|
||||||
|
*/
|
||||||
|
if (d->rename_status == status)
|
||||||
|
one_name = d->rename_source;
|
||||||
|
|
||||||
one = quote_path(one_name, s->prefix, &onebuf);
|
one = quote_path(one_name, s->prefix, &onebuf);
|
||||||
two = quote_path(two_name, s->prefix, &twobuf);
|
two = quote_path(two_name, s->prefix, &twobuf);
|
||||||
|
|
||||||
@ -434,7 +440,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
|
|||||||
struct wt_status_change_data *d;
|
struct wt_status_change_data *d;
|
||||||
|
|
||||||
p = q->queue[i];
|
p = q->queue[i];
|
||||||
it = string_list_insert(&s->change, p->one->path);
|
it = string_list_insert(&s->change, p->two->path);
|
||||||
d = it->util;
|
d = it->util;
|
||||||
if (!d) {
|
if (!d) {
|
||||||
d = xcalloc(1, sizeof(*d));
|
d = xcalloc(1, sizeof(*d));
|
||||||
@ -461,6 +467,14 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
|
|||||||
/* mode_worktree is zero for a delete. */
|
/* mode_worktree is zero for a delete. */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DIFF_STATUS_COPIED:
|
||||||
|
case DIFF_STATUS_RENAMED:
|
||||||
|
if (d->rename_status)
|
||||||
|
die("BUG: multiple renames on the same target? how?");
|
||||||
|
d->rename_source = xstrdup(p->one->path);
|
||||||
|
d->rename_score = p->score * 100 / MAX_SCORE;
|
||||||
|
d->rename_status = p->status;
|
||||||
|
/* fallthru */
|
||||||
case DIFF_STATUS_MODIFIED:
|
case DIFF_STATUS_MODIFIED:
|
||||||
case DIFF_STATUS_TYPE_CHANGED:
|
case DIFF_STATUS_TYPE_CHANGED:
|
||||||
case DIFF_STATUS_UNMERGED:
|
case DIFF_STATUS_UNMERGED:
|
||||||
@ -532,6 +546,8 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
|
|||||||
|
|
||||||
case DIFF_STATUS_COPIED:
|
case DIFF_STATUS_COPIED:
|
||||||
case DIFF_STATUS_RENAMED:
|
case DIFF_STATUS_RENAMED:
|
||||||
|
if (d->rename_status)
|
||||||
|
die("BUG: multiple renames on the same target? how?");
|
||||||
d->rename_source = xstrdup(p->one->path);
|
d->rename_source = xstrdup(p->one->path);
|
||||||
d->rename_score = p->score * 100 / MAX_SCORE;
|
d->rename_score = p->score * 100 / MAX_SCORE;
|
||||||
d->rename_status = p->status;
|
d->rename_status = p->status;
|
||||||
|
Loading…
Reference in New Issue
Block a user