resolve-undo: "checkout -m path" uses resolve-undo information
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 Dec 2009 19:57:11 +0000 (11:57 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 26 Dec 2009 01:10:10 +0000 (17:10 -0800)
Once you resolved conflicts by "git add path", you cannot recreate the
conflicted state with "git checkout -m path", because you lost information
from higher stages in the index when you resolved them.

Since we record the necessary information in the resolve-undo index
extension these days, we can reproduce the unmerged state in the index and
check it out.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-checkout.c
cache.h
resolve-undo.c
resolve-undo.h
t/t2030-unresolve-info.sh

index a0fe7a4e6d300e347a6c5251f41596a95ee72323..bdef1aa38699e6c74ca30b313406ef093890e902 100644 (file)
@@ -235,6 +235,10 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
        if (report_path_error(ps_matched, pathspec, 0))
                return 1;
 
+       /* "checkout -m path" to recreate conflicted state */
+       if (opts->merge)
+               unmerge_cache(pathspec);
+
        /* Any unmerged paths? */
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
diff --git a/cache.h b/cache.h
index 310d9e672c8b1a66d5dfaf057db3af9eac028423..f479f091900e684455de122c2b146c174d447ca1 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -338,6 +338,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
 #define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 #define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
 #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
+#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
 #endif
 
 enum object_type {
index 86e8547ca25c72f0cce5a4ca2780a3f93dac33ae..37d73cd949bd884397615d0f6399bd64a88e20a1 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "dir.h"
 #include "resolve-undo.h"
 #include "string-list.h"
 
@@ -115,3 +116,61 @@ void resolve_undo_clear_index(struct index_state *istate)
        istate->resolve_undo = NULL;
        istate->cache_changed = 1;
 }
+
+int unmerge_index_entry_at(struct index_state *istate, int pos)
+{
+       struct cache_entry *ce;
+       struct string_list_item *item;
+       struct resolve_undo_info *ru;
+       int i, err = 0;
+
+       if (!istate->resolve_undo)
+               return pos;
+
+       ce = istate->cache[pos];
+       if (ce_stage(ce)) {
+               /* already unmerged */
+               while ((pos < istate->cache_nr) &&
+                      ! strcmp(istate->cache[pos]->name, ce->name))
+                       pos++;
+               return pos - 1; /* return the last entry processed */
+       }
+       item = string_list_lookup(ce->name, istate->resolve_undo);
+       if (!item)
+               return pos;
+       ru = item->util;
+       if (!ru)
+               return pos;
+       remove_index_entry_at(istate, pos);
+       for (i = 0; i < 3; i++) {
+               struct cache_entry *nce;
+               if (!ru->mode[i])
+                       continue;
+               nce = make_cache_entry(ru->mode[i], ru->sha1[i],
+                                      ce->name, i + 1, 0);
+               if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
+                       err = 1;
+                       error("cannot unmerge '%s'", ce->name);
+               }
+       }
+       if (err)
+               return pos;
+       free(ru);
+       item->util = NULL;
+       return unmerge_index_entry_at(istate, pos);
+}
+
+void unmerge_index(struct index_state *istate, const char **pathspec)
+{
+       int i;
+
+       if (!istate->resolve_undo)
+               return;
+
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
+                       continue;
+               i = unmerge_index_entry_at(istate, i);
+       }
+}
index 74194d0eba7d6dca49fefff11ed8089b49363650..e4e5c1b1ad5b3fc7d5a2d516ab3ccccb3e88b993 100644 (file)
@@ -10,5 +10,7 @@ extern void record_resolve_undo(struct index_state *, struct cache_entry *);
 extern void resolve_undo_write(struct strbuf *, struct string_list *);
 extern struct string_list *resolve_undo_read(void *, unsigned long);
 extern void resolve_undo_clear_index(struct index_state *);
+extern int unmerge_index_entry_at(struct index_state *, int);
+extern void unmerge_index(struct index_state *, const char **);
 
 #endif
index 984480271c0deef50dddae215a59aa62471c0e47..ea65f391c1671b141d2d071aad0f7df7ca4ea4ac 100755 (executable)
@@ -97,4 +97,15 @@ test_expect_success 'plumbing clears' '
        check_resolve_undo cleared
 '
 
+test_expect_success 'add records checkout -m undoes' '
+       prime_resolve_undo &&
+       git diff HEAD &&
+       git checkout --conflict=merge file &&
+       echo checkout used the record and removed it &&
+       check_resolve_undo removed &&
+       echo the index and the work tree is unmerged again &&
+       git diff >actual &&
+       grep "^++<<<<<<<" actual
+'
+
 test_done