зеркало из https://github.com/microsoft/git.git
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:
Родитель
5134ccde64
Коммит
176ea74793
|
@ -125,14 +125,15 @@ the status.relativePaths config option below.
|
|||
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
|
||||
shown only when `PATH1` corresponds to a different path in the
|
||||
index/worktree (i.e. the file is renamed). The `XY` is a two-letter
|
||||
status code.
|
||||
where `ORIG_PATH` is where the renamed/copied contents came
|
||||
from. `ORIG_PATH` is only shown when the entry is renamed or
|
||||
copied. The `XY` is a two-letter status code.
|
||||
|
||||
The fields (including the `->`) are separated from each other by a
|
||||
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] M work tree changed since index
|
||||
[ MARC] D deleted in work tree
|
||||
[ D] R renamed in work tree
|
||||
[ D] C copied in work tree
|
||||
-------------------------------------------------
|
||||
D D unmerged, both deleted
|
||||
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
|
||||
move or copy). For example "R100" or "C75".
|
||||
<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
|
||||
with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
|
||||
byte separates them.
|
||||
<origPath> The pathname in the commit at HEAD. This is only
|
||||
present in a renamed/copied entry, and tells
|
||||
where the renamed/copied contents came from.
|
||||
<origPath> The pathname in the commit at HEAD or in the index.
|
||||
This is only present in a renamed/copied entry, and
|
||||
tells where the renamed/copied contents came from.
|
||||
--------------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
|
|
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) {
|
||||
case WT_STATUS_UPDATED:
|
||||
status = d->index_status;
|
||||
if (d->rename_source)
|
||||
one_name = d->rename_source;
|
||||
break;
|
||||
case WT_STATUS_CHANGED:
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
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;
|
||||
|
||||
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;
|
||||
if (!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. */
|
||||
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_TYPE_CHANGED:
|
||||
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_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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче