{"thread":{"id":"21898","subject":"[PATCH v4 1/6] reset: add a few tests for \"git reset --merge\"","startedAt":"2009-12-08T07:56:09Z","lastAt":"2009-12-12T04:19:03Z","messageCount":10,"participants":["Christian Couder","Junio C Hamano","Stephen Boyd"],"isPatch":true,"patchVersion":4,"patchTotal":6},"messages":[{"id":"129506","messageId":"20091208075005.4475.26582.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":null,"subject":"[PATCH v4 0/6] \"git reset --merge\" related improvements","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:09Z","receivedAt":"2009-12-08T07:56:09Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This is a reroll of a previous series from last september:\n\nhttp://thread.gmane.org/gmane.comp.version-control.git/128706/focus=128707\n\nThe changes in this reroll are the following:\n\n- new option was renamed \"--keep-local-changes\" instead of \"--merge-safe\",\n- some test cases have been added,\n- a bug was fixed (head_sha1 is now \"unsigned char head_sha1[20]\"),\n- I took ownership of the third patch,\n- some commit messages were improved,\n- there are 2 new documentation patches at the end,\n- the last documentation patch adds some tables about what all the reset\noptions are doing in the different cases.\n\nThe new option name is \"--keep-local-changes\" because that's what\nJunio used in the last email of the previous discussion, but my\nopinion is that it is a bit long and so I'd like to rename it \"--keep\"\nor another such short name.\n\nChristian Couder (5):\n  reset: add a few tests for \"git reset --merge\"\n  reset: add option \"--keep-local-changes\" to \"git reset\"\n  reset: add test cases for \"--keep-local-changes\" option\n  Documentation: reset: describe new \"--keep-local-changes\" option\n  Documentation: reset: add some tables to describe the different\n    options\n\nStephan Beyer (1):\n  reset: use \"unpack_trees()\" directly instead of \"git read-tree\"\n\n Documentation/git-reset.txt |   80 ++++++++++++++++++++++-\n builtin-reset.c             |   81 ++++++++++++++++++-----\n t/t7110-reset-merge.sh      |  156 +++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 300 insertions(+), 17 deletions(-)\n create mode 100755 t/t7110-reset-merge.sh\n"},{"id":"129501","messageId":"20091208075616.4475.21452.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"[PATCH v4 1/6] reset: add a few tests for \"git reset --merge\"","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:10Z","receivedAt":"2009-12-08T07:56:10Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Commit 9e8eceab (\"Add 'merge' mode to 'git reset'\", 2008-12-01),\nadded the --merge option to git reset, but there were no test cases\nfor it.\n\nThis was not a big problem because \"git reset\" was just forking and\nexecing \"git read-tree\", but this will change in a following patch.\n\nSo let's add a few test cases to make sure that there will be no\nregression.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t7110-reset-merge.sh |   94 ++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 94 insertions(+), 0 deletions(-)\n create mode 100755 t/t7110-reset-merge.sh\n\ndiff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh\nnew file mode 100755\nindex 0000000..8190da1\n--- /dev/null\n+++ b/t/t7110-reset-merge.sh\n@@ -0,0 +1,94 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2009 Christian Couder\n+#\n+\n+test_description='Tests for \"git reset --merge\"'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'creating initial files' '\n+     echo \"line 1\" >> file1 &&\n+     echo \"line 2\" >> file1 &&\n+     echo \"line 3\" >> file1 &&\n+     cp file1 file2 &&\n+     git add file1 file2 &&\n+     test_tick &&\n+     git commit -m \"Initial commit\"\n+'\n+\n+test_expect_success 'reset --merge is ok with changes in file it does not touch' '\n+     echo \"line 4\" >> file1 &&\n+     echo \"line 4\" >> file2 &&\n+     test_tick &&\n+     git commit -m \"add line 4\" file1 &&\n+     git reset --merge HEAD^ &&\n+     ! grep 4 file1 &&\n+     grep 4 file2 &&\n+     git reset --merge HEAD@{1} &&\n+     grep 4 file1 &&\n+     grep 4 file2\n+'\n+\n+test_expect_success 'reset --merge discards changes added to index (1)' '\n+     echo \"line 5\" >> file1 &&\n+     git add file1 &&\n+     git reset --merge HEAD^ &&\n+     ! grep 4 file1 &&\n+     ! grep 5 file1 &&\n+     grep 4 file2 &&\n+     echo \"line 5\" >> file2 &&\n+     git add file2 &&\n+     git reset --merge HEAD@{1} &&\n+     ! grep 4 file2 &&\n+     ! grep 5 file1 &&\n+     grep 4 file1\n+'\n+\n+test_expect_success 'reset --merge discards changes added to index (2)' '\n+     echo \"line 4\" >> file2 &&\n+     git add file2 &&\n+     git reset --merge HEAD^ &&\n+     ! grep 4 file2 &&\n+     git reset --merge HEAD@{1} &&\n+     ! grep 4 file2 &&\n+     grep 4 file1\n+'\n+\n+test_expect_success 'reset --merge fails with changes in file it touches' '\n+     echo \"line 5\" >> file1 &&\n+     test_tick &&\n+     git commit -m \"add line 5\" file1 &&\n+     sed -e \"s/line 1/changed line 1/\" <file1 >file3 &&\n+     mv file3 file1 &&\n+     test_must_fail git reset --merge HEAD^ 2>err.log &&\n+     grep file1 err.log | grep \"not uptodate\" &&\n+     git reset --hard HEAD^\n+'\n+\n+test_expect_success 'setup 2 different branches' '\n+     git branch branch1 &&\n+     git branch branch2 &&\n+     git checkout branch1 &&\n+     echo \"line 5 in branch1\" >> file1 &&\n+     test_tick &&\n+     git commit -a -m \"change in branch1\" &&\n+     git checkout branch2 &&\n+     echo \"line 5 in branch2\" >> file1 &&\n+     test_tick &&\n+     git commit -a -m \"change in branch2\"\n+'\n+\n+test_expect_success '\"reset --merge HEAD^\" fails with pending merge' '\n+     test_must_fail git merge branch1 &&\n+     test_must_fail git reset --merge HEAD^ &&\n+     git reset --hard HEAD\n+'\n+\n+test_expect_success '\"reset --merge HEAD\" fails with pending merge' '\n+     test_must_fail git merge branch1 &&\n+     test_must_fail git reset --merge HEAD &&\n+     git reset --hard HEAD\n+'\n+\n+test_done\n-- \n1.6.5.1.gaf97d\n"},{"id":"129505","messageId":"20091208075616.4475.46720.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"[PATCH v4 2/6] reset: use \"unpack_trees()\" directly instead of \"git read-tree\"","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:11Z","receivedAt":"2009-12-08T07:56:11Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"From: Stephan Beyer <s-beyer@gmx.net>\n\nThis patch makes \"reset_index_file()\" call \"unpack_trees()\" directly\ninstead of forking and execing \"git read-tree\". So the code is more\nefficient.\n\nAnd it's also easier to see which unpack_tree() options will be used,\nas we don't need to follow \"git read-tree\"'s command line parsing\nwhich is quite complex.\n\nAs Daniel Barkalow found, there is a difference between this new\nversion and the old one. The old version gives an error for\n\"git reset --merge\" with unmerged entries and the new version does\nnot. But this can be seen as a bug fix, because \"--merge\" was the\nonly \"git reset\" option with this behavior and this behavior was\nnot documented.\n\nIn fact there is still an error with unmerge entries if we reset\nthe unmerge entries to the same state as HEAD. So the bug is not\ncompletely fixed.\n\nThe code comes from the sequencer GSoC project:\n\ngit://repo.or.cz/git/sbeyer.git\n\n(at commit 5a78908b70ceb5a4ea9fd4b82f07ceba1f019079)\n\nMentored-by: Daniel Barkalow <barkalow@iabervon.org>\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n builtin-reset.c        |   51 +++++++++++++++++++++++++++++++++++++----------\n t/t7110-reset-merge.sh |    8 ++++--\n 2 files changed, 45 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin-reset.c b/builtin-reset.c\nindex 73e6022..ddb81f3 100644\n--- a/builtin-reset.c\n+++ b/builtin-reset.c\n@@ -18,6 +18,8 @@\n #include \"tree.h\"\n #include \"branch.h\"\n #include \"parse-options.h\"\n+#include \"unpack-trees.h\"\n+#include \"cache-tree.h\"\n \n static const char * const git_reset_usage[] = {\n \t\"git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]\",\n@@ -52,29 +54,56 @@ static inline int is_merge(void)\n \treturn !access(git_path(\"MERGE_HEAD\"), F_OK);\n }\n \n+static int parse_and_init_tree_desc(const unsigned char *sha1,\n+\t\t\t\t\t     struct tree_desc *desc)\n+{\n+\tstruct tree *tree = parse_tree_indirect(sha1);\n+\tif (!tree)\n+\t\treturn 1;\n+\tinit_tree_desc(desc, tree->buffer, tree->size);\n+\treturn 0;\n+}\n+\n static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)\n {\n-\tint i = 0;\n-\tconst char *args[6];\n+\tint nr = 1;\n+\tint newfd;\n+\tstruct tree_desc desc[2];\n+\tstruct unpack_trees_options opts;\n+\tstruct lock_file *lock = xcalloc(1, sizeof(struct lock_file));\n \n-\targs[i++] = \"read-tree\";\n+\tmemset(&opts, 0, sizeof(opts));\n+\topts.head_idx = 1;\n+\topts.src_index = &the_index;\n+\topts.dst_index = &the_index;\n+\topts.fn = oneway_merge;\n+\topts.merge = 1;\n \tif (!quiet)\n-\t\targs[i++] = \"-v\";\n+\t\topts.verbose_update = 1;\n \tswitch (reset_type) {\n \tcase MERGE:\n-\t\targs[i++] = \"-u\";\n-\t\targs[i++] = \"-m\";\n+\t\topts.update = 1;\n \t\tbreak;\n \tcase HARD:\n-\t\targs[i++] = \"-u\";\n+\t\topts.update = 1;\n \t\t/* fallthrough */\n \tdefault:\n-\t\targs[i++] = \"--reset\";\n+\t\topts.reset = 1;\n \t}\n-\targs[i++] = sha1_to_hex(sha1);\n-\targs[i] = NULL;\n \n-\treturn run_command_v_opt(args, RUN_GIT_CMD);\n+\tnewfd = hold_locked_index(lock, 1);\n+\n+\tread_cache_unmerged();\n+\n+\tif (parse_and_init_tree_desc(sha1, desc + nr - 1))\n+\t\treturn error(\"Failed to find tree of %s.\", sha1_to_hex(sha1));\n+\tif (unpack_trees(nr, desc, &opts))\n+\t\treturn -1;\n+\tif (write_cache(newfd, active_cache, active_nr) ||\n+\t    commit_locked_index(lock))\n+\t\treturn error(\"Could not write new index file.\");\n+\n+\treturn 0;\n }\n \n static void print_new_head_line(struct commit *commit)\ndiff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh\nindex 8190da1..6afaf73 100755\n--- a/t/t7110-reset-merge.sh\n+++ b/t/t7110-reset-merge.sh\n@@ -79,10 +79,12 @@ test_expect_success 'setup 2 different branches' '\n      git commit -a -m \"change in branch2\"\n '\n \n-test_expect_success '\"reset --merge HEAD^\" fails with pending merge' '\n+test_expect_success '\"reset --merge HEAD^\" is ok with pending merge' '\n      test_must_fail git merge branch1 &&\n-     test_must_fail git reset --merge HEAD^ &&\n-     git reset --hard HEAD\n+     git reset --merge HEAD^ &&\n+     test -z \"$(git diff --cached)\" &&\n+     test -n \"$(git diff)\" &&\n+     git reset --hard HEAD@{1}\n '\n \n test_expect_success '\"reset --merge HEAD\" fails with pending merge' '\n-- \n1.6.5.1.gaf97d\n"},{"id":"129504","messageId":"20091208075616.4475.31260.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"[PATCH v4 3/6] reset: add option \"--keep-local-changes\" to \"git reset\"","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:12Z","receivedAt":"2009-12-08T07:56:12Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"The purpose of this new option is to discard some of the\nlast commits but to keep current changes in the work tree\nand the index.\n\nThe table below shows what happens when running\n\"git reset --option target\" to reset the HEAD to another\ncommit (as a special case \"target\" could be the same as\nHEAD) in the cases where \"--merge\" and\n\"--keep-local-changes\" (abreviated --k-l-c) behave\ndifferently.\n\nworking index HEAD target         working index HEAD\n----------------------------------------------------\n  B      B     A     A   --k-l-c    B      A     A\n                         --merge    A      A     A\n  B      B     A     C   --k-l-c       (disallowed)\n                         --merge    C      C     C\n\nIn this table, A, B and C are some different states of\na file. For example the first line of the table means\nthat if a file is in state B in the working tree and\nthe index, and in a different state A in HEAD and in\nthe target, then \"git reset --keep-local-changes target\"\nwill put the file in state B in the working tree and in\nstate A in the index and HEAD.\n\nSo as can be seen in the table, \"--merge\" discards changes\nin the index, while \"--keep-local-changes\" does not. So\nif one wants to avoid using \"git stash\" before and after\nusing \"git reset\" to save current changes, it is better to\nuse \"--keep-local-changes\" rather than \"--merge\".\n\nThe following table shows what happens on unmerged entries:\n\nworking index HEAD target         working index HEAD\n----------------------------------------------------\n X       U     A    B     --k-l-c  X       B     B\n                          --merge  X       B     B\n X       U     A    A     --k-l-c  X       A     A\n                          --merge (disallowed)\n\nIn this table X can be any state and U means an unmerged\nentry.\n\nA following patch will add some test cases for\n\"--keep-local-changes\", where the differences between\n\"--merge\" and \"--keep-local-changes\" can also be seen.\n\nThe \"--keep-local-changes\" option is implemented by doing\na 2 way merge between HEAD and the reset target, and if\nthis succeeds by doing a mixed reset to the target.\n\nThe code comes from the sequencer GSoC project, where\nsuch an option was developed by Stephan Beyer:\n\ngit://repo.or.cz/git/sbeyer.git\n\n(at commit 5a78908b70ceb5a4ea9fd4b82f07ceba1f019079)\n\nBut in the sequencer project the \"reset\" flag was set\nin the \"struct unpack_trees_options\" passed to\n\"unpack_trees()\". With this flag the changes in the\nworking tree were discarded if the file was different\nbetween HEAD and the reset target.\n\nMentored-by: Daniel Barkalow <barkalow@iabervon.org>\nMentored-by: Christian Couder <chriscool@tuxfamily.org>\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n builtin-reset.c |   30 +++++++++++++++++++++++++-----\n 1 files changed, 25 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin-reset.c b/builtin-reset.c\nindex ddb81f3..3cbc4fd 100644\n--- a/builtin-reset.c\n+++ b/builtin-reset.c\n@@ -22,13 +22,15 @@\n #include \"cache-tree.h\"\n \n static const char * const git_reset_usage[] = {\n-\t\"git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]\",\n+\t\"git reset [--mixed | --soft | --hard | --merge | --keep-local-changes] [-q] [<commit>]\",\n \t\"git reset [--mixed] <commit> [--] <paths>...\",\n \tNULL\n };\n \n-enum reset_type { MIXED, SOFT, HARD, MERGE, NONE };\n-static const char *reset_type_names[] = { \"mixed\", \"soft\", \"hard\", \"merge\", NULL };\n+enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP_LOCAL_CHANGES, NONE };\n+static const char *reset_type_names[] = {\n+\t\"mixed\", \"soft\", \"hard\", \"merge\", \"keep_local_changes\", NULL\n+};\n \n static char *args_to_str(const char **argv)\n {\n@@ -81,6 +83,7 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet\n \tif (!quiet)\n \t\topts.verbose_update = 1;\n \tswitch (reset_type) {\n+\tcase KEEP_LOCAL_CHANGES:\n \tcase MERGE:\n \t\topts.update = 1;\n \t\tbreak;\n@@ -95,6 +98,16 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet\n \n \tread_cache_unmerged();\n \n+\tif (reset_type == KEEP_LOCAL_CHANGES) {\n+\t\tunsigned char head_sha1[20];\n+\t\tif (get_sha1(\"HEAD\", head_sha1))\n+\t\t\treturn error(\"You do not have a valid HEAD.\");\n+\t\tif (parse_and_init_tree_desc(head_sha1, desc))\n+\t\t\treturn error(\"Failed to find tree of HEAD.\");\n+\t\tnr++;\n+\t\topts.fn = twoway_merge;\n+\t}\n+\n \tif (parse_and_init_tree_desc(sha1, desc + nr - 1))\n \t\treturn error(\"Failed to find tree of %s.\", sha1_to_hex(sha1));\n \tif (unpack_trees(nr, desc, &opts))\n@@ -238,6 +251,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \t\t\t\t\"reset HEAD, index and working tree\", HARD),\n \t\tOPT_SET_INT(0, \"merge\", &reset_type,\n \t\t\t\t\"reset HEAD, index and working tree\", MERGE),\n+\t\tOPT_SET_INT(0, \"keep-local-changes\", &reset_type,\n+\t\t\t\t\"reset HEAD but keep local changes\",\n+\t\t\t\tKEEP_LOCAL_CHANGES),\n \t\tOPT_BOOLEAN('q', NULL, &quiet,\n \t\t\t\t\"disable showing new HEAD in hard reset and progress message\"),\n \t\tOPT_BOOLEAN('p', \"patch\", &patch_mode, \"select hunks interactively\"),\n@@ -324,9 +340,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix)\n \tif (reset_type == SOFT) {\n \t\tif (is_merge() || read_cache() < 0 || unmerged_cache())\n \t\t\tdie(\"Cannot do a soft reset in the middle of a merge.\");\n+\t} else {\n+\t\tint err = reset_index_file(sha1, reset_type, quiet);\n+\t\tif (reset_type == KEEP_LOCAL_CHANGES)\n+\t\t\terr = err || reset_index_file(sha1, MIXED, quiet);\n+\t\tif (err)\n+\t\t\tdie(\"Could not reset index file to revision '%s'.\", rev);\n \t}\n-\telse if (reset_index_file(sha1, reset_type, quiet))\n-\t\tdie(\"Could not reset index file to revision '%s'.\", rev);\n \n \t/* Any resets update HEAD to the head being switched to,\n \t * saving the previous head in ORIG_HEAD before. */\n-- \n1.6.5.1.gaf97d\n"},{"id":"129507","messageId":"20091208075616.4475.28704.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"[PATCH v4 4/6] reset: add test cases for \"--keep-local-changes\" option","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:13Z","receivedAt":"2009-12-08T07:56:13Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This shows that with the \"--keep-local-changes\" option, changes that\nare both in the work tree and the index are kept in the work tree\nafter the reset (but discarded in the index). As with the \"--merge\"\noption, changes that are in both the work tree and the index are\ndiscarded after the reset.\n\nIn the case of unmerged entries, we can see that\n\"git reset --merge HEAD\" is disallowed while it works using\n\"--keep-local-changes\" instead.\n\nAnd this shows that otherwise \"--merge\" and \"--keep-local-changes\"\nhave the same behavior.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n t/t7110-reset-merge.sh |   62 +++++++++++++++++++++++++++++++++++++++++++++++-\n 1 files changed, 61 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh\nindex 6afaf73..c8f72cd 100755\n--- a/t/t7110-reset-merge.sh\n+++ b/t/t7110-reset-merge.sh\n@@ -3,7 +3,7 @@\n # Copyright (c) 2009 Christian Couder\n #\n \n-test_description='Tests for \"git reset --merge\"'\n+test_description='Tests for \"git reset\" with --merge and --keep-local-changes'\n \n . ./test-lib.sh\n \n@@ -30,6 +30,20 @@ test_expect_success 'reset --merge is ok with changes in file it does not touch'\n      grep 4 file2\n '\n \n+test_expect_success 'reset --keep-local-changes is ok with changes in file it does not touch' '\n+     git reset --hard HEAD^ &&\n+     echo \"line 4\" >> file1 &&\n+     echo \"line 4\" >> file2 &&\n+     test_tick &&\n+     git commit -m \"add line 4\" file1 &&\n+     git reset --keep-local-changes HEAD^ &&\n+     ! grep 4 file1 &&\n+     grep 4 file2 &&\n+     git reset --keep-local-changes HEAD@{1} &&\n+     grep 4 file1 &&\n+     grep 4 file2\n+'\n+\n test_expect_success 'reset --merge discards changes added to index (1)' '\n      echo \"line 5\" >> file1 &&\n      git add file1 &&\n@@ -55,6 +69,25 @@ test_expect_success 'reset --merge discards changes added to index (2)' '\n      grep 4 file1\n '\n \n+test_expect_success 'reset --keep-local-changes fails with changes in index in files it touches' '\n+     echo \"line 4\" >> file2 &&\n+     echo \"line 5\" >> file1 &&\n+     git add file1 &&\n+     test_must_fail git reset --keep-local-changes HEAD^ &&\n+     git reset --hard HEAD\n+'\n+\n+test_expect_success 'reset --keep-local-changes keeps changes it does not touch' '\n+     echo \"line 4\" >> file2 &&\n+     git add file2 &&\n+     git reset --keep-local-changes HEAD^ &&\n+     grep 4 file2 &&\n+     git reset --keep-local-changes HEAD@{1} &&\n+     grep 4 file2 &&\n+     grep 4 file1 &&\n+     git reset --hard HEAD\n+'\n+\n test_expect_success 'reset --merge fails with changes in file it touches' '\n      echo \"line 5\" >> file1 &&\n      test_tick &&\n@@ -66,6 +99,17 @@ test_expect_success 'reset --merge fails with changes in file it touches' '\n      git reset --hard HEAD^\n '\n \n+test_expect_success 'reset --keep-local-changes fails with changes in file it touches' '\n+     echo \"line 5\" >> file1 &&\n+     test_tick &&\n+     git commit -m \"add line 5\" file1 &&\n+     sed -e \"s/line 1/changed line 1/\" <file1 >file3 &&\n+     mv file3 file1 &&\n+     test_must_fail git reset --keep-local-changes HEAD^ 2>err.log &&\n+     grep file1 err.log | grep \"not uptodate\" &&\n+     git reset --hard HEAD^\n+'\n+\n test_expect_success 'setup 2 different branches' '\n      git branch branch1 &&\n      git branch branch2 &&\n@@ -87,10 +131,26 @@ test_expect_success '\"reset --merge HEAD^\" is ok with pending merge' '\n      git reset --hard HEAD@{1}\n '\n \n+test_expect_success '\"reset --keep-local-changes HEAD^\" is ok with pending merge' '\n+     test_must_fail git merge branch1 &&\n+     git reset --keep-local-changes HEAD^ &&\n+     test -z \"$(git diff --cached)\" &&\n+     test -n \"$(git diff)\" &&\n+     git reset --hard HEAD@{1}\n+'\n+\n test_expect_success '\"reset --merge HEAD\" fails with pending merge' '\n      test_must_fail git merge branch1 &&\n      test_must_fail git reset --merge HEAD &&\n      git reset --hard HEAD\n '\n \n+test_expect_success '\"reset --keep-local-changes HEAD\" is ok with pending merge' '\n+     test_must_fail git merge branch1 &&\n+     git reset --keep-local-changes HEAD &&\n+     test -z \"$(git diff --cached)\" &&\n+     test -n \"$(git diff)\" &&\n+     git reset --hard HEAD\n+'\n+\n test_done\n-- \n1.6.5.1.gaf97d\n"},{"id":"129503","messageId":"20091208075616.4475.46978.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"[PATCH v4 5/6] Documentation: reset: describe new \"--keep-local-changes\" option","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:14Z","receivedAt":"2009-12-08T07:56:14Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n Documentation/git-reset.txt |    7 ++++++-\n 1 files changed, 6 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex 2d27e40..a6c080e 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -8,7 +8,7 @@ git-reset - Reset current HEAD to the specified state\n SYNOPSIS\n --------\n [verse]\n-'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]\n+'git reset' [--mixed | --soft | --hard | --merge | --keep-local-changes] [-q] [<commit>]\n 'git reset' [-q] [<commit>] [--] <paths>...\n 'git reset' --patch [<commit>] [--] [<paths>...]\n \n@@ -52,6 +52,11 @@ OPTIONS\n \tand updates the files that are different between the named commit\n \tand the current commit in the working tree.\n \n+--keep-local-changes::\n+\tThis does the same thing as --merge, but it keeps changes in\n+\tthe working tree and the index or it aborts if it is not\n+\tpossible.\n+\n -p::\n --patch::\n \tInteractively select hunks in the difference between the index\n-- \n1.6.5.1.gaf97d\n"},{"id":"129502","messageId":"20091208075616.4475.78725.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"[PATCH v4 6/6] Documentation: reset: add some tables to describe the different options","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-08T07:56:15Z","receivedAt":"2009-12-08T07:56:15Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This patch adds a DISCUSSION section that contains some tables to\nshow how the different \"git reset\" options work depending on the\nstates of the files in the working tree, the index, HEAD and the\ntarget commit.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n Documentation/git-reset.txt |   73 +++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 73 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt\nindex a6c080e..8bc8808 100644\n--- a/Documentation/git-reset.txt\n+++ b/Documentation/git-reset.txt\n@@ -72,6 +72,79 @@ linkgit:git-add[1]).\n <commit>::\n \tCommit to make the current HEAD. If not given defaults to HEAD.\n \n+DISCUSSION\n+----------\n+\n+The tables below show what happens when running:\n+\n+----------\n+git reset --option target\n+----------\n+\n+to reset the HEAD to another commit (`target`) with the different\n+reset options (`--keep-local-changes` is abreviated --k-l-c)\n+depending on the state of the files.\n+\n+      working index HEAD target         working index HEAD\n+      ----------------------------------------------------\n+       A       B     C    D     --soft   A       B     D\n+                                --mixed  A       D     D\n+                                --hard   D       D     D\n+                                --merge (disallowed)\n+                                --k-l-c (disallowed)\n+\n+      working index HEAD target         working index HEAD\n+      ----------------------------------------------------\n+       A       B     C    C     --soft   A       B     C\n+                                --mixed  A       C     C\n+                                --hard   C       C     C\n+                                --merge (disallowed)\n+                                --k-l-c  A       C     C\n+\n+      working index HEAD target         working index HEAD\n+      ----------------------------------------------------\n+       B       B     C    D     --soft   B       B     D\n+                                --mixed  B       D     D\n+                                --hard   D       D     D\n+                                --merge  D       D     D\n+                                --k-l-c (disallowed)\n+\n+      working index HEAD target         working index HEAD\n+      ----------------------------------------------------\n+       B       B     C    C     --soft   B       B     C\n+                                --mixed  B       C     C\n+                                --hard   C       C     C\n+                                --merge  C       C     C\n+                                --k-l-c  B       C     C\n+\n+In these tables, A, B, C and D are some different states of a\n+file. For example, the last line of the last table means that if a\n+file is in state B in the working tree and the index, and in a\n+different state C in HEAD and in the target, then \"git reset\n+--keep-local-changes target\" will put the file in state B in the\n+working tree and in state C in the index and HEAD.\n+\n+The following tables show what happens when there are unmerged\n+entries:\n+\n+      working index HEAD target         working index HEAD\n+      ----------------------------------------------------\n+       X       U     A    B     --k-l-c  X       B     B\n+                                --merge  X       B     B\n+                                --hard   B       B     B\n+                                --mixed  X       B     B\n+                                --soft  (disallowed)\n+\n+      working index HEAD target         working index HEAD\n+      ----------------------------------------------------\n+       X       U     A    A     --k-l-c  X       A     A\n+                                --merge (disallowed)\n+                                --hard   A       A     A\n+                                --mixed  X       A     A\n+                                --soft  (disallowed)\n+\n+X means any state and U means an unmerged index.\n+\n Examples\n --------\n \n-- \n1.6.5.1.gaf97d\n"},{"id":"129509","messageId":"7vk4wxkgw8.fsf@alter.siamese.dyndns.org","threadId":"21898","inReplyTo":"20091208075005.4475.26582.chriscool@tuxfamily.org","subject":"Re: [PATCH v4 0/6] \"git reset --merge\" related improvements","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-12-08T08:29:43Z","receivedAt":"2009-12-08T08:29:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> The new option name is \"--keep-local-changes\" because that's what\n> Junio used in the last email of the previous discussion, but my\n> opinion is that it is a bit long and so I'd like to rename it \"--keep\"\n> or another such short name.\n\nI vaguely recall that I mentioned something like \"I still don't know what\nyou are going to use this for, even though I think I am starting to\nunderstand it a bit better than before. In any case, it sounds like 'keep\nlocal changes' rather than 'safe'\".\n\nPlease don't take that as a serious suggestion of a better name.\n\nIOW, don't mind me---come up with a name that describes what you\nare doing better.  But please don't blame me either ;-)\n"},{"id":"129510","messageId":"1260261919.1554.11.camel@swboyd-laptop","threadId":"21898","inReplyTo":"20091208075616.4475.46720.chriscool@tuxfamily.org","subject":"Re: [PATCH v4 2/6] reset: use \"unpack_trees()\" directly instead of \"git read-tree\"","fromName":"Stephen Boyd","fromEmail":"bebarino@gmail.com","sentAt":"2009-12-08T08:45:19Z","receivedAt":"2009-12-08T08:45:19Z","isPatch":true,"sender":{"key":"bebarino@gmail.com","avatar":"https://avatars.githubusercontent.com/u/38832?v=4"},"body":"On Tue, 2009-12-08 at 08:56 +0100, Christian Couder wrote:\n> +static int parse_and_init_tree_desc(const unsigned char *sha1,\n> +\t\t\t\t\t     struct tree_desc *desc)\n> +{\n> +\tstruct tree *tree = parse_tree_indirect(sha1);\n> +\tif (!tree)\n> +\t\treturn 1;\n> +\tinit_tree_desc(desc, tree->buffer, tree->size);\n> +\treturn 0;\n> +}\n> +\n>  \n\nIs there a reason why you use this function instead of\nfill_tree_descriptor()?\n"},{"id":"129777","messageId":"200912120519.04328.chriscool@tuxfamily.org","threadId":"21898","inReplyTo":"1260261919.1554.11.camel@swboyd-laptop","subject":"Re: [PATCH v4 2/6] reset: use \"unpack_trees()\" directly instead of \"git read-tree\"","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2009-12-12T04:19:03Z","receivedAt":"2009-12-12T04:19:03Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"On mardi 08 décembre 2009, Stephen Boyd wrote:\n> On Tue, 2009-12-08 at 08:56 +0100, Christian Couder wrote:\n> > +static int parse_and_init_tree_desc(const unsigned char *sha1,\n> > +\t\t\t\t\t     struct tree_desc *desc)\n> > +{\n> > +\tstruct tree *tree = parse_tree_indirect(sha1);\n> > +\tif (!tree)\n> > +\t\treturn 1;\n> > +\tinit_tree_desc(desc, tree->buffer, tree->size);\n> > +\treturn 0;\n> > +}\n> > +\n>\n> Is there a reason why you use this function instead of\n> fill_tree_descriptor()?\n\nI didn't know about fill_tree_descriptor(). I will use it in the next \nseries.\n\nThanks,\nChristian.\n"}]}