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@xxxxxxxxxxx> Helped-by: Igor Djordjevic <igor.d.djordjevic@xxxxxxxxx> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- t/t2203-add-intent.sh | 28 ++++++++++++++++++++ wt-status.c | 72 +++++++++++++++++++++++++++++++++++++++------------ wt-status.h | 4 +-- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 878e73fe98..e5bfda1853 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -162,5 +162,33 @@ 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 && + + oid=12f00e90b6ef79117ce6e650416b8cf517099b78 && + git status --porcelain=v2 | grep -v "^?" >actual.3 && + cat >expected.3 <<-EOF && + 2 .R N... 100644 100644 100644 $oid $oid R100 first third + EOF + test_cmp expected.3 actual.3 + ) +' + test_done diff --git a/wt-status.c b/wt-status.c index c124d7589c..d5bdf4c2e9 100644 --- a/wt-status.c +++ b/wt-status.c @@ -376,6 +376,8 @@ static void wt_longstatus_print_change_data(struct wt_status *s, strbuf_addch(&extra, ')'); } status = d->worktree_status; + if (d->worktree_path) + two_name = d->worktree_path; break; default: die("BUG: unhandled change_type %d in wt_longstatus_print_change_data", @@ -460,6 +462,12 @@ 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: + d->worktree_path = xstrdup(p->two->path); + d->worktree_score = p->score * 100 / MAX_SCORE; + /* fallthru */ + case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: case DIFF_STATUS_UNMERGED: @@ -1712,6 +1720,7 @@ static void wt_shortstatus_status(struct string_list_item *it, struct wt_status *s) { struct wt_status_change_data *d = it->util; + const char *from, *to; if (d->index_status) color_fprintf(s->fp, color(WT_STATUS_UPDATED, s), "%c", d->index_status); @@ -1722,15 +1731,30 @@ static void wt_shortstatus_status(struct string_list_item *it, else putchar(' '); putchar(' '); + + if (d->head_path && d->worktree_path) + die("BUG: to be addressed in the next patch"); + + if (d->head_path) { + from = d->head_path; + to = it->string; + } else if (d->worktree_path) { + from = it->string; + to = d->worktree_path; + } else { + from = it->string; + to = NULL; + } if (s->null_termination) { - fprintf(stdout, "%s%c", it->string, 0); - if (d->head_path) - fprintf(stdout, "%s%c", d->head_path, 0); + fprintf(stdout, "%s%c", from, 0); + if (to) + fprintf(stdout, "%s%c", to, 0); } else { struct strbuf onebuf = STRBUF_INIT; const char *one; - if (d->head_path) { - one = quote_path(d->head_path, s->prefix, &onebuf); + + if (to) { + one = quote_path(from, s->prefix, &onebuf); if (*one != '"' && strchr(one, ' ') != NULL) { putchar('"'); strbuf_addch(&onebuf, '"'); @@ -1738,8 +1762,9 @@ static void wt_shortstatus_status(struct string_list_item *it, } printf("%s -> ", one); strbuf_release(&onebuf); - } - one = quote_path(it->string, s->prefix, &onebuf); + one = quote_path(to, s->prefix, &onebuf); + } else + one = quote_path(from, s->prefix, &onebuf); if (*one != '"' && strchr(one, ' ') != NULL) { putchar('"'); strbuf_addch(&onebuf, '"'); @@ -2036,12 +2061,13 @@ static void wt_porcelain_v2_print_changed_entry( { struct wt_status_change_data *d = it->util; struct strbuf buf_index = STRBUF_INIT; - struct strbuf buf_head = STRBUF_INIT; + struct strbuf buf_other = STRBUF_INIT; const char *path_index = NULL; - const char *path_head = NULL; - char key[3]; + const char *path_other = NULL; + char key[3], status_other; char submodule_token[5]; char sep_char, eol_char; + int score; wt_porcelain_v2_fix_up_changed(it, s); wt_porcelain_v2_submodule_state(d, submodule_token); @@ -2050,6 +2076,19 @@ static void wt_porcelain_v2_print_changed_entry( key[1] = d->worktree_status ? d->worktree_status : '.'; key[2] = 0; + if (d->head_path && d->worktree_path) + die("BUG: to be addressed in the next patch"); + + if (d->head_path) { + path_other = d->head_path; + status_other = d->index_status; + score = d->head_score; + } else if (d->worktree_path) { + path_other = d->worktree_path; + status_other = d->worktree_status; + score = d->worktree_score; + } + if (s->null_termination) { /* * In -z mode, we DO NOT C-quote pathnames. Current path is ALWAYS first. @@ -2058,7 +2097,6 @@ static void wt_porcelain_v2_print_changed_entry( sep_char = '\0'; eol_char = '\0'; path_index = it->string; - path_head = d->head_path; } else { /* * Path(s) are C-quoted if necessary. Current path is ALWAYS first. @@ -2069,17 +2107,17 @@ static void wt_porcelain_v2_print_changed_entry( sep_char = '\t'; eol_char = '\n'; path_index = quote_path(it->string, s->prefix, &buf_index); - if (d->head_path) - path_head = quote_path(d->head_path, s->prefix, &buf_head); + if (path_other) + path_other = quote_path(path_other, s->prefix, &buf_other); } - if (path_head) + if (path_other) fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c", key, submodule_token, d->mode_head, d->mode_index, d->mode_worktree, oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index), - key[0], d->head_score, - path_index, sep_char, path_head, eol_char); + status_other, score, + path_index, sep_char, path_other, eol_char); else fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c", key, submodule_token, @@ -2088,7 +2126,7 @@ static void wt_porcelain_v2_print_changed_entry( path_index, eol_char); strbuf_release(&buf_index); - strbuf_release(&buf_head); + strbuf_release(&buf_other); } /* diff --git a/wt-status.h b/wt-status.h index f9330982ac..332ff545aa 100644 --- a/wt-status.h +++ b/wt-status.h @@ -44,10 +44,10 @@ struct wt_status_change_data { int worktree_status; int index_status; int stagemask; - int head_score; + int head_score, worktree_score; int mode_head, mode_index, mode_worktree; struct object_id oid_head, oid_index; - char *head_path; + char *head_path, *worktree_path; unsigned dirty_submodule : 2; unsigned new_submodule_commits : 1; }; -- 2.15.0.320.g0453912d77