summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-reset.txt4
-rw-r--r--cache.h1
-rw-r--r--read-cache.c3
-rwxr-xr-xt/t7110-reset-merge.sh40
-rw-r--r--unpack-trees.c21
5 files changed, 49 insertions, 20 deletions
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index cf2433d52c..dc73dca736 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -122,14 +122,14 @@ entries:
X U A B --soft (disallowed)
--mixed X B B
--hard B B B
- --merge X B B
+ --merge B B B
working index HEAD target working index HEAD
----------------------------------------------------
X U A A --soft (disallowed)
--mixed X A A
--hard A A A
- --merge (disallowed)
+ --merge A A A
X means any state and U means an unmerged index.
diff --git a/cache.h b/cache.h
index bf468e5235..da8ec92415 100644
--- a/cache.h
+++ b/cache.h
@@ -177,6 +177,7 @@ struct cache_entry {
#define CE_HASHED (0x100000)
#define CE_UNHASHED (0x200000)
+#define CE_CONFLICTED (0x400000)
/*
* Extended on-disk flags
diff --git a/read-cache.c b/read-cache.c
index 1bbaf1cffb..16c4548a74 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1606,9 +1606,8 @@ int read_index_unmerged(struct index_state *istate)
len = strlen(ce->name);
size = cache_entry_size(len);
new_ce = xcalloc(1, size);
- hashcpy(new_ce->sha1, ce->sha1);
memcpy(new_ce->name, ce->name, len);
- new_ce->ce_flags = create_ce_flags(len, 0);
+ new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
new_ce->ce_mode = ce->ce_mode;
if (add_index_entry(istate, new_ce, 0))
return error("%s: cannot drop to stage #0",
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
index ff2875c00b..8704d00196 100755
--- a/t/t7110-reset-merge.sh
+++ b/t/t7110-reset-merge.sh
@@ -116,10 +116,11 @@ test_expect_success 'reset --merge fails with changes in file it touches' '
grep file1 err.log | grep "not uptodate"
'
-test_expect_success 'setup 2 different branches' '
+test_expect_success 'setup 3 different branches' '
git reset --hard second &&
git branch branch1 &&
git branch branch2 &&
+ git branch branch3 &&
git checkout branch1 &&
echo "line 5 in branch1" >> file1 &&
test_tick &&
@@ -128,34 +129,55 @@ test_expect_success 'setup 2 different branches' '
echo "line 5 in branch2" >> file1 &&
test_tick &&
git commit -a -m "change in branch2" &&
- git tag third
+ git tag third &&
+ git checkout branch3 &&
+ echo a new file >file3 &&
+ rm -f file1 &&
+ git add file3 &&
+ test_tick &&
+ git commit -a -m "change in branch3"
'
# The next test will test the following:
#
# working index HEAD target working index HEAD
# ----------------------------------------------------
-# file1: X U B C --merge X C C
+# file1: X U B C --merge C C C
test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
+ git checkout third &&
test_must_fail git merge branch1 &&
- cat file1 >orig_file1 &&
git reset --merge HEAD^ &&
test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
test -z "$(git diff --cached)" &&
- test_cmp file1 orig_file1
+ test -z "$(git diff)"
'
# The next test will test the following:
#
# working index HEAD target working index HEAD
# ----------------------------------------------------
-# file1: X U B B --merge (disallowed)
-test_expect_success '"reset --merge HEAD" fails with pending merge' '
+# file1: X U B B --merge B B B
+test_expect_success '"reset --merge HEAD" is ok with pending merge' '
git reset --hard third &&
test_must_fail git merge branch1 &&
- test_must_fail git reset --merge HEAD &&
+ git reset --merge HEAD &&
test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
- test -n "$(git diff --cached)"
+ test -z "$(git diff --cached)" &&
+ test -z "$(git diff)"
+'
+
+test_expect_success '--merge with added/deleted' '
+ git reset --hard third &&
+ rm -f file2 &&
+ test_must_fail git merge branch3 &&
+ ! test -f file2 &&
+ test -f file3 &&
+ git diff --exit-code file3 &&
+ git diff --exit-code branch3 file3 &&
+ git reset --merge HEAD &&
+ ! test -f file3 &&
+ ! test -f file2 &&
+ git diff --exit-code --cached
'
test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index dd5999c356..3df0de6005 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -436,6 +436,8 @@ static int same(struct cache_entry *a, struct cache_entry *b)
return 0;
if (!a && !b)
return 1;
+ if ((a->ce_flags | b->ce_flags) & CE_CONFLICTED)
+ return 0;
return a->ce_mode == b->ce_mode &&
!hashcmp(a->sha1, b->sha1);
}
@@ -666,7 +668,11 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
{
int update = CE_UPDATE;
- if (old) {
+ if (!old) {
+ if (verify_absent(merge, "overwritten", o))
+ return -1;
+ invalidate_ce_path(merge, o);
+ } else if (!(old->ce_flags & CE_CONFLICTED)) {
/*
* See if we can re-use the old CE directly?
* That way we get the uptodate stat info.
@@ -682,11 +688,12 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
return -1;
invalidate_ce_path(old, o);
}
- }
- else {
- if (verify_absent(merge, "overwritten", o))
- return -1;
- invalidate_ce_path(merge, o);
+ } else {
+ /*
+ * Previously unmerged entry left as an existence
+ * marker by read_index_unmerged();
+ */
+ invalidate_ce_path(old, o);
}
add_entry(o, merge, update, CE_STAGEMASK);
@@ -702,7 +709,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
return -1;
return 0;
}
- if (verify_uptodate(old, o))
+ if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
return -1;
add_entry(o, ce, CE_REMOVE, 0);
invalidate_ce_path(ce, o);