diff options
author | Junio C Hamano <gitster@pobox.com> | 2011-04-20 18:11:19 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2011-04-23 23:13:28 -0700 |
commit | 75973b2cb58bf2b3038c5c214fc0a1b96d6868fe (patch) | |
tree | 0e87eaf82131220184af67f945738157d706c58d /builtin | |
parent | 095ce9538b738db28d5e9a6e05d94c7e3f55f39d (diff) | |
download | git-75973b2cb58bf2b3038c5c214fc0a1b96d6868fe.tar.gz git-75973b2cb58bf2b3038c5c214fc0a1b96d6868fe.tar.xz |
Fix "add -u" that sometimes fails to resolve unmerged paths
"git add -u" updates the index with the updated contents from the working
tree by internally running "diff-files" to grab the set of paths that are
different from the index. Then it updates the index entries for the paths
that are modified in the working tree, and deletes the index entries for
the paths that are deleted in the working tree.
It ignored the output from the diff-files that indicated that a path is
unmerged. For these paths, it instead relied on the fact that an unmerged
path is followed by the result of comparison between stage #2 (ours) and
the working tree, and used that to update or delete such a path when it is
used to record the resolution of a conflict.
As the result, when a path did not have stage #2 (e.g. "we deleted while
the other side added"), these unmerged stages were left behind, instead of
recording what the user resolved in the working tree.
Since we recently fixed "diff-files" to indicate if the corresponding path
exists on the working tree for an unmerged path, we do not have to rely on
the comparison with stage #2 anymore. We can instead tell the diff-files
not to compare with higher stages, and use the unmerged output to update
the index to reflect the state of the working tree.
The changes to the test vector in t2200 illustrates the nature of the bug
and the fix. The test expected stage #1 and #3 entries be left behind,
but it was codifying the buggy behaviour.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'builtin')
-rw-r--r-- | builtin/add.c | 45 |
1 files changed, 23 insertions, 22 deletions
diff --git a/builtin/add.c b/builtin/add.c index 56a4e0af6..027ca3b6d 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -27,6 +27,27 @@ struct update_callback_data int add_errors; }; +static int fix_unmerged_status(struct diff_filepair *p, + struct update_callback_data *data) +{ + if (p->status != DIFF_STATUS_UNMERGED) + return p->status; + if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode) + /* + * This is not an explicit add request, and the + * path is missing from the working tree (deleted) + */ + return DIFF_STATUS_DELETED; + else + /* + * Either an explicit add request, or path exists + * in the working tree. An attempt to explicitly + * add a path that does not exist in the working tree + * will be caught as an error by the caller immediately. + */ + return DIFF_STATUS_MODIFIED; +} + static void update_callback(struct diff_queue_struct *q, struct diff_options *opt, void *cbdata) { @@ -36,30 +57,9 @@ static void update_callback(struct diff_queue_struct *q, for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; const char *path = p->one->path; - switch (p->status) { + switch (fix_unmerged_status(p, data)) { default: die("unexpected diff status %c", p->status); - case DIFF_STATUS_UNMERGED: - /* - * ADD_CACHE_IGNORE_REMOVAL is unset if "git - * add -u" is calling us, In such a case, a - * missing work tree file needs to be removed - * if there is an unmerged entry at stage #2, - * but such a diff record is followed by - * another with DIFF_STATUS_DELETED (and if - * there is no stage #2, we won't see DELETED - * nor MODIFIED). We can simply continue - * either way. - */ - if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL)) - continue; - /* - * Otherwise, it is "git add path" is asking - * to explicitly add it; we fall through. A - * missing work tree file is an error and is - * caught by add_file_to_index() in such a - * case. - */ case DIFF_STATUS_MODIFIED: case DIFF_STATUS_TYPE_CHANGED: if (add_file_to_index(&the_index, path, data->flags)) { @@ -92,6 +92,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags) data.flags = flags; data.add_errors = 0; rev.diffopt.format_callback_data = &data; + rev.max_count = 0; /* do not compare unmerged paths with stage #2 */ run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); return !!data.add_errors; } |