From 2b06b0a02f8d0aa01c8ff19b72c9292afd7a84fe Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 30 Dec 2009 06:54:44 +0100 Subject: [PATCH 1/8] reset: improve mixed reset error message when in a bare repo When running a "git reset --mixed" in a bare repository, the message displayed is something like: fatal: This operation must be run in a work tree fatal: Could not reset index file to revision 'HEAD^'. This message is a little bit misleading because a mixed reset is ok in a git directory, so it is not absolutely needed to run it in a work tree. So this patch improves upon the above by changing the message to: fatal: mixed reset is not allowed in a bare repository And if "git reset" is ever sped up by using unpack_tree() directly (instead of execing "git read-tree"), this patch will also make sure that a mixed reset is still disallowed in a bare repository. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- builtin-reset.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builtin-reset.c b/builtin-reset.c index 11d1c6e4d6..3180c2b582 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -291,6 +291,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) die("%s reset requires a work tree", reset_type_names[reset_type]); + if (reset_type == MIXED && is_bare_repository()) + die("%s reset is not allowed in a bare repository", + reset_type_names[reset_type]); + /* Soft reset does not touch the index file nor the working tree * at all, but requires them in a good order. Other resets reset * the index file to the tree object we are switching to. */ From 4086010c7c83d907321ae1357569dfcc01710cb6 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 30 Dec 2009 06:54:45 +0100 Subject: [PATCH 2/8] Documentation: reset: add some tables to describe the different options This patch adds a DISCUSSION section that contains some tables to show how the different "git reset" options work depending on the states of the files in the working tree, the index, HEAD and the target commit. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 2d27e405a3..2198c8ebd6 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -67,6 +67,72 @@ linkgit:git-add[1]). :: Commit to make the current HEAD. If not given defaults to HEAD. +DISCUSSION +---------- + +The tables below show what happens when running: + +---------- +git reset --option target +---------- + +to reset the HEAD to another commit (`target`) with the different +reset options depending on the state of the files. + + working index HEAD target working index HEAD + ---------------------------------------------------- + A B C D --soft A B D + --mixed A D D + --hard D D D + --merge (disallowed) + + working index HEAD target working index HEAD + ---------------------------------------------------- + A B C C --soft A B C + --mixed A C C + --hard C C C + --merge (disallowed) + + working index HEAD target working index HEAD + ---------------------------------------------------- + B B C D --soft B B D + --mixed B D D + --hard D D D + --merge D D D + + working index HEAD target working index HEAD + ---------------------------------------------------- + B B C C --soft B B C + --mixed B C C + --hard C C C + --merge C C C + +In these tables, A, B, C and D are some different states of a +file. For example, the last line of the last table means that if a +file is in state B in the working tree and the index, and in a +different state C in HEAD and in the target, then "git reset +--merge target" will put the file in state C in the working tree, +in the index and in HEAD. + +The following tables show what happens when there are unmerged +entries: + + working index HEAD target working index HEAD + ---------------------------------------------------- + X U A B --soft (disallowed) + --mixed X B B + --hard B B B + --merge (disallowed) + + working index HEAD target working index HEAD + ---------------------------------------------------- + X U A A --soft (disallowed) + --mixed X A A + --hard A A A + --merge (disallowed) + +X means any state and U means an unmerged index. + Examples -------- From c93966906f5d578d06eee0ead8745e608a6e18cf Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 30 Dec 2009 06:54:46 +0100 Subject: [PATCH 3/8] reset: add a few tests for "git reset --merge" Commit 9e8eceab ("Add 'merge' mode to 'git reset'", 2008-12-01), added the --merge option to git reset, but there were no test cases for it. This was not a big problem because "git reset" was just forking and execing "git read-tree", but this will change in a following patch. So let's add a few test cases to make sure that there will be no regression. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t7110-reset-merge.sh | 159 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100755 t/t7110-reset-merge.sh diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh new file mode 100755 index 0000000000..4c46083bec --- /dev/null +++ b/t/t7110-reset-merge.sh @@ -0,0 +1,159 @@ +#!/bin/sh +# +# Copyright (c) 2009 Christian Couder +# + +test_description='Tests for "git reset --merge"' + +. ./test-lib.sh + +test_expect_success setup ' + for i in 1 2 3; do echo line $i; done >file1 && + cat file1 >file2 && + git add file1 file2 && + test_tick && + git commit -m "Initial commit" && + git tag initial && + echo line 4 >>file1 && + cat file1 >file2 && + test_tick && + git commit -m "add line 4 to file1" file1 && + git tag second +' + +# The next test will test the following: +# +# working index HEAD target working index HEAD +# ---------------------------------------------------- +# file1: C C C D --merge D D D +# file2: C D D D --merge C D D +test_expect_success 'reset --merge is ok with changes in file it does not touch' ' + git reset --merge HEAD^ && + ! grep 4 file1 && + grep 4 file2 && + test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && + test -z "$(git diff --cached)" +' + +test_expect_success 'reset --merge is ok when switching back' ' + git reset --merge second && + grep 4 file1 && + grep 4 file2 && + test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && + test -z "$(git diff --cached)" +' + +# The next test will test the following: +# +# working index HEAD target working index HEAD +# ---------------------------------------------------- +# file1: B B C D --merge D D D +# file2: C D D D --merge C D D +test_expect_success 'reset --merge discards changes added to index (1)' ' + git reset --hard second && + cat file1 >file2 && + echo "line 5" >> file1 && + git add file1 && + git reset --merge HEAD^ && + ! grep 4 file1 && + ! grep 5 file1 && + grep 4 file2 && + test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && + test -z "$(git diff --cached)" +' + +test_expect_success 'reset --merge is ok again when switching back (1)' ' + git reset --hard initial && + echo "line 5" >> file2 && + git add file2 && + git reset --merge second && + ! grep 4 file2 && + ! grep 5 file1 && + grep 4 file1 && + test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && + test -z "$(git diff --cached)" +' + +# The next test will test the following: +# +# working index HEAD target working index HEAD +# ---------------------------------------------------- +# file1: C C C D --merge D D D +# file2: C C D D --merge D D D +test_expect_success 'reset --merge discards changes added to index (2)' ' + git reset --hard second && + echo "line 4" >> file2 && + git add file2 && + git reset --merge HEAD^ && + ! grep 4 file2 && + test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" && + test -z "$(git diff)" && + test -z "$(git diff --cached)" +' + +test_expect_success 'reset --merge is ok again when switching back (2)' ' + git reset --hard initial && + git reset --merge second && + ! grep 4 file2 && + grep 4 file1 && + test "$(git rev-parse HEAD)" = "$(git rev-parse second)" && + test -z "$(git diff --cached)" +' + +# The next test will test the following: +# +# working index HEAD target working index HEAD +# ---------------------------------------------------- +# file1: A B B C --merge (disallowed) +test_expect_success 'reset --merge fails with changes in file it touches' ' + git reset --hard second && + echo "line 5" >> file1 && + test_tick && + git commit -m "add line 5" file1 && + sed -e "s/line 1/changed line 1/" file3 && + mv file3 file1 && + test_must_fail git reset --merge HEAD^ 2>err.log && + grep file1 err.log | grep "not uptodate" +' + +test_expect_success 'setup 2 different branches' ' + git reset --hard second && + git branch branch1 && + git branch branch2 && + git checkout branch1 && + echo "line 5 in branch1" >> file1 && + test_tick && + git commit -a -m "change in branch1" && + git checkout branch2 && + echo "line 5 in branch2" >> file1 && + test_tick && + git commit -a -m "change in branch2" && + git tag third +' + +# The next test will test the following: +# +# working index HEAD target working index HEAD +# ---------------------------------------------------- +# file1: X U B C --merge (disallowed) +test_expect_success '"reset --merge HEAD^" fails with pending merge' ' + test_must_fail git merge branch1 && + test_must_fail git reset --merge HEAD^ && + test "$(git rev-parse HEAD)" = "$(git rev-parse third)" && + test -n "$(git diff --cached)" +' + +# 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' ' + git reset --hard third && + test_must_fail git merge branch1 && + test_must_fail git reset --merge HEAD && + test "$(git rev-parse HEAD)" = "$(git rev-parse third)" && + test -n "$(git diff --cached)" +' + +test_done From d0f379c2dcf7198d373b3c64444019bed2e24336 Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Wed, 30 Dec 2009 06:54:47 +0100 Subject: [PATCH 4/8] reset: use "unpack_trees()" directly instead of "git read-tree" This patch makes "reset_index_file()" call "unpack_trees()" directly instead of forking and execing "git read-tree". So the code is more efficient. And it's also easier to see which unpack_tree() options will be used, as we don't need to follow "git read-tree"'s command line parsing which is quite complex. As Daniel Barkalow found, there is a difference between this new version and the old one. The old version gives an error for "git reset --merge" with unmerged entries, and the new version does not when we reset the entries to some states that differ from HEAD. Instead, it resets the index entry and succeeds, while leaving the conflict markers in the corresponding file in the work tree (which will be corrected by the next patch). The code comes from the sequencer GSoC project: git://repo.or.cz/git/sbeyer.git (at commit 5a78908b70ceb5a4ea9fd4b82f07ceba1f019079) Mentored-by: Daniel Barkalow Mentored-by: Christian Couder Signed-off-by: Stephan Beyer Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 2 +- builtin-reset.c | 41 +++++++++++++++++++++++++++---------- t/t7110-reset-merge.sh | 12 ++++++----- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 2198c8ebd6..cf2433d52c 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -122,7 +122,7 @@ entries: X U A B --soft (disallowed) --mixed X B B --hard B B B - --merge (disallowed) + --merge X B B working index HEAD target working index HEAD ---------------------------------------------------- diff --git a/builtin-reset.c b/builtin-reset.c index 3180c2b582..2c880a7e7a 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -18,6 +18,8 @@ #include "tree.h" #include "branch.h" #include "parse-options.h" +#include "unpack-trees.h" +#include "cache-tree.h" static const char * const git_reset_usage[] = { "git reset [--mixed | --soft | --hard | --merge] [-q] []", @@ -54,27 +56,44 @@ static inline int is_merge(void) static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet) { - int i = 0; - const char *args[6]; + int nr = 1; + int newfd; + struct tree_desc desc[2]; + struct unpack_trees_options opts; + struct lock_file *lock = xcalloc(1, sizeof(struct lock_file)); - args[i++] = "read-tree"; + memset(&opts, 0, sizeof(opts)); + opts.head_idx = 1; + opts.src_index = &the_index; + opts.dst_index = &the_index; + opts.fn = oneway_merge; + opts.merge = 1; if (!quiet) - args[i++] = "-v"; + opts.verbose_update = 1; switch (reset_type) { case MERGE: - args[i++] = "-u"; - args[i++] = "-m"; + opts.update = 1; break; case HARD: - args[i++] = "-u"; + opts.update = 1; /* fallthrough */ default: - args[i++] = "--reset"; + opts.reset = 1; } - args[i++] = sha1_to_hex(sha1); - args[i] = NULL; - return run_command_v_opt(args, RUN_GIT_CMD); + newfd = hold_locked_index(lock, 1); + + read_cache_unmerged(); + + if (!fill_tree_descriptor(desc + nr - 1, sha1)) + return error("Failed to find tree of %s.", sha1_to_hex(sha1)); + if (unpack_trees(nr, desc, &opts)) + return -1; + if (write_cache(newfd, active_cache, active_nr) || + commit_locked_index(lock)) + return error("Could not write new index file."); + + return 0; } static void print_new_head_line(struct commit *commit) diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh index 4c46083bec..ff2875c00b 100755 --- a/t/t7110-reset-merge.sh +++ b/t/t7110-reset-merge.sh @@ -135,12 +135,14 @@ test_expect_success 'setup 2 different branches' ' # # working index HEAD target working index HEAD # ---------------------------------------------------- -# file1: X U B C --merge (disallowed) -test_expect_success '"reset --merge HEAD^" fails with pending merge' ' +# file1: X U B C --merge X C C +test_expect_success '"reset --merge HEAD^" is ok with pending merge' ' test_must_fail git merge branch1 && - test_must_fail git reset --merge HEAD^ && - test "$(git rev-parse HEAD)" = "$(git rev-parse third)" && - test -n "$(git diff --cached)" + 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 ' # The next test will test the following: From e11d7b5969704cd5ce39d053414d905bb203886b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 31 Dec 2009 23:04:04 -0800 Subject: [PATCH 5/8] "reset --merge": fix unmerged case Commit 9e8ecea (Add 'merge' mode to 'git reset', 2008-12-01) disallowed "git reset --merge" when there was unmerged entries. But it wished if unmerged entries were reset as if --hard (instead of --merge) has been used. This makes sense because all "mergy" operations makes sure that any path involved in the merge does not have local modifications before starting, so resetting such a path away won't lose any information. The previous commit changed the behavior of --merge to accept resetting unmerged entries if they are reset to a different state than HEAD, but it did not reset the changes in the work tree, leaving the conflict markers in the resulting file in the work tree. Fix it by doing three things: - Update the documentation to match the wish of original "reset --merge" better, namely, "An unmerged entry is a sign that the path didn't have any local modification and can be safely resetted to whatever the new HEAD records"; - Update read_index_unmerged(), which reads the index file into the cache while dropping any higher-stage entries down to stage #0, not to copy the object name from the higher stage entry. The code used to take the object name from the a stage entry ("base" if you happened to have stage #1, or "ours" if both sides added, etc.), which essentially meant that you are getting random results depending on what the merge did. The _only_ reason we want to keep a previously unmerged entry in the index at stage #0 is so that we don't forget the fact that we have corresponding file in the work tree in order to be able to remove it when the tree we are resetting to does not have the path. In order to differentiate such an entry from ordinary cache entry, the cache entry added by read_index_unmerged() is marked as CE_CONFLICTED. - Update merged_entry() and deleted_entry() so that they pay attention to cache entries marked as CE_CONFLICTED. They are previously unmerged entries, and the files in the work tree that correspond to them are resetted away by oneway_merge() to the version from the tree we are resetting to. Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 4 ++-- cache.h | 1 + read-cache.c | 3 +-- t/t7110-reset-merge.sh | 40 ++++++++++++++++++++++++++++--------- unpack-trees.c | 21 ++++++++++++------- 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); From bf96c931994382d99e4e4d55f7b22b6779787a9d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 4 Jan 2010 00:02:00 -0800 Subject: [PATCH 6/8] Fix bit assignment for CE_CONFLICTED CE_WT_REMOVE has already grabbed the same value. Signed-off-by: Junio C Hamano --- cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache.h b/cache.h index da8ec92415..7759c2cf94 100644 --- a/cache.h +++ b/cache.h @@ -177,7 +177,7 @@ struct cache_entry { #define CE_HASHED (0x100000) #define CE_UNHASHED (0x200000) -#define CE_CONFLICTED (0x400000) +#define CE_CONFLICTED (0x800000) /* * Extended on-disk flags From 397d596f84209d0e9d17621ce56b5432bc98d368 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Tue, 5 Jan 2010 06:58:30 +0100 Subject: [PATCH 7/8] Documentation: reset: add some missing tables and while at it also explain why --merge option is disallowed in some cases. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-reset.txt | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index dc73dca736..c137183574 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -79,6 +79,13 @@ git reset --option target to reset the HEAD to another commit (`target`) with the different reset options depending on the state of the files. +In these tables, A, B, C and D are some different states of a +file. For example, the first line of the first table means that if a +file is in state A in the working tree, in state B in the index, in +state C in HEAD and in state D in the target, then "git reset --soft +target" will put the file in state A in the working tree, in state B +in the index and in state D in HEAD. + working index HEAD target working index HEAD ---------------------------------------------------- A B C D --soft A B D @@ -107,12 +114,28 @@ reset options depending on the state of the files. --hard C C C --merge C C C -In these tables, A, B, C and D are some different states of a -file. For example, the last line of the last table means that if a -file is in state B in the working tree and the index, and in a -different state C in HEAD and in the target, then "git reset ---merge target" will put the file in state C in the working tree, -in the index and in HEAD. + working index HEAD target working index HEAD + ---------------------------------------------------- + B C C D --soft B C D + --mixed B D D + --hard D D D + --merge (disallowed) + + working index HEAD target working index HEAD + ---------------------------------------------------- + B C C C --soft B C C + --mixed B C C + --hard C C C + --merge B C C + +"reset --merge" is meant to be used when resetting out of a conflicted +merge. Any mergy operation guarantees that the work tree file that is +involved in the merge does not have local change wrt the index before +it starts, and that it writes the result out to the work tree. So if +we see some difference between the index and the target and also +between the index and the work tree, then it means that we are not +resetting out from a state that a mergy operation left after failing +with a conflict. That is why we disallow --merge option in this case. The following tables show what happens when there are unmerged entries: From d7eed8cbef2d15e87e9002f5e3ce08830b40b292 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 8 Jan 2010 05:45:10 +0100 Subject: [PATCH 8/8] t7111: check that reset options work as described in the tables Some previous patches added some tables to the "git reset" documentation. These tables describe the behavior of "git reset" depending on the option it is passed and the state of the files in the working tree, the index, HEAD and the target commit. This patch adds some tests to make sure that the tables describe the behavior of "git reset". Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t7111-reset-table.sh | 113 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100755 t/t7111-reset-table.sh diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh new file mode 100755 index 0000000000..0a362a12bf --- /dev/null +++ b/t/t7111-reset-table.sh @@ -0,0 +1,113 @@ +#!/bin/sh +# +# Copyright (c) 2010 Christian Couder +# + +test_description='Tests to check that "reset" options follow a known table' + +. ./test-lib.sh + + +test_expect_success 'creating initial commits' ' + test_commit E file1 && + test_commit D file1 && + test_commit C file1 +' + +while read W1 I1 H1 T opt W2 I2 H2 +do + test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" ' + git reset --hard C && + if test "$I1" != "$H1" + then + echo "$I1" >file1 && + git add file1 + fi && + if test "$W1" != "$I1" + then + echo "$W1" >file1 + fi && + if test "$W2" != "XXXXX" + then + git reset --$opt $T && + test "$(cat file1)" = "$W2" && + git checkout-index -f -- file1 && + test "$(cat file1)" = "$I2" && + git checkout -f HEAD -- file1 && + test "$(cat file1)" = "$H2" + else + test_must_fail git reset --$opt $T + fi + ' +done <<\EOF +A B C D soft A B D +A B C D mixed A D D +A B C D hard D D D +A B C D merge XXXXX +A B C C soft A B C +A B C C mixed A C C +A B C C hard C C C +A B C C merge XXXXX +B B C D soft B B D +B B C D mixed B D D +B B C D hard D D D +B B C D merge D D D +B B C C soft B B C +B B C C mixed B C C +B B C C hard C C C +B B C C merge C C C +B C C D soft B C D +B C C D mixed B D D +B C C D hard D D D +B C C D merge XXXXX +B C C C soft B C C +B C C C mixed B C C +B C C C hard C C C +B C C C merge B C C +EOF + +test_expect_success 'setting up branches to test with unmerged entries' ' + git reset --hard C && + git branch branch1 && + git branch branch2 && + git checkout branch1 && + test_commit B1 file1 && + git checkout branch2 && + test_commit B2 file1 +' + +while read W1 I1 H1 T opt W2 I2 H2 +do + test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" ' + git reset --hard B2 && + test_must_fail git merge branch1 && + cat file1 >X_file1 && + if test "$W2" != "XXXXX" + then + git reset --$opt $T && + if test "$W2" = "X" + then + test_cmp file1 X_file1 + else + test "$(cat file1)" = "$W2" + fi && + git checkout-index -f -- file1 && + test "$(cat file1)" = "$I2" && + git checkout -f HEAD -- file1 && + test "$(cat file1)" = "$H2" + else + test_must_fail git reset --$opt $T + fi + ' +done <<\EOF +X U C D soft XXXXX +X U C D mixed X D D +X U C D hard D D D +X U C D merge D D D +X U C C soft XXXXX +X U C C mixed X C C +X U C C hard C C C +X U C C merge C C C +EOF + +test_done