The purpose of this new option is to discard some of the last commits but to keep current changes in the work tree and the index. The table below shows what happens when running "git reset --option target" to reset the HEAD to another commit (as a special case "target" could be the same as HEAD) in the cases where "--merge" and "--keep-local-changes" (abreviated --k-l-c) behave differently. working index HEAD target working index HEAD ---------------------------------------------------- B B A A --k-l-c B A A --merge A A A B B A C --k-l-c (disallowed) --merge C C C In this table, A, B and C are some different states of a file. For example the first line of the table means that if a file is in state B in the working tree and the index, and in a different state A in HEAD and in the target, then "git reset --keep-local-changes target" will put the file in state B in the working tree and in state A in the index and HEAD. So as can be seen in the table, "--merge" discards changes in the index, while "--keep-local-changes" does not. So if one wants to avoid using "git stash" before and after using "git reset" to save current changes, it is better to use "--keep-local-changes" rather than "--merge". The following table shows what happens on unmerged entries: working index HEAD target working index HEAD ---------------------------------------------------- X U A B --k-l-c X B B --merge X B B X U A A --k-l-c X A A --merge (disallowed) In this table X can be any state and U means an unmerged entry. A following patch will add some test cases for "--keep-local-changes", where the differences between "--merge" and "--keep-local-changes" can also be seen. The "--keep-local-changes" option is implemented by doing a 2 way merge between HEAD and the reset target, and if this succeeds by doing a mixed reset to the target. The code comes from the sequencer GSoC project, where such an option was developed by Stephan Beyer: git://repo.or.cz/git/sbeyer.git (at commit 5a78908b70ceb5a4ea9fd4b82f07ceba1f019079) But in the sequencer project the "reset" flag was set in the "struct unpack_trees_options" passed to "unpack_trees()". With this flag the changes in the working tree were discarded if the file was different between HEAD and the reset target. Mentored-by: Daniel Barkalow <barkalow@xxxxxxxxxxxx> Mentored-by: Christian Couder <chriscool@xxxxxxxxxxxxx> Signed-off-by: Stephan Beyer <s-beyer@xxxxxxx> Signed-off-by: Christian Couder <chriscool@xxxxxxxxxxxxx> --- builtin-reset.c | 30 +++++++++++++++++++++++++----- 1 files changed, 25 insertions(+), 5 deletions(-) diff --git a/builtin-reset.c b/builtin-reset.c index ddb81f3..3cbc4fd 100644 --- a/builtin-reset.c +++ b/builtin-reset.c @@ -22,13 +22,15 @@ #include "cache-tree.h" static const char * const git_reset_usage[] = { - "git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]", + "git reset [--mixed | --soft | --hard | --merge | --keep-local-changes] [-q] [<commit>]", "git reset [--mixed] <commit> [--] <paths>...", NULL }; -enum reset_type { MIXED, SOFT, HARD, MERGE, NONE }; -static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL }; +enum reset_type { MIXED, SOFT, HARD, MERGE, KEEP_LOCAL_CHANGES, NONE }; +static const char *reset_type_names[] = { + "mixed", "soft", "hard", "merge", "keep_local_changes", NULL +}; static char *args_to_str(const char **argv) { @@ -81,6 +83,7 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet if (!quiet) opts.verbose_update = 1; switch (reset_type) { + case KEEP_LOCAL_CHANGES: case MERGE: opts.update = 1; break; @@ -95,6 +98,16 @@ static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet read_cache_unmerged(); + if (reset_type == KEEP_LOCAL_CHANGES) { + unsigned char head_sha1[20]; + if (get_sha1("HEAD", head_sha1)) + return error("You do not have a valid HEAD."); + if (parse_and_init_tree_desc(head_sha1, desc)) + return error("Failed to find tree of HEAD."); + nr++; + opts.fn = twoway_merge; + } + if (parse_and_init_tree_desc(sha1, desc + nr - 1)) return error("Failed to find tree of %s.", sha1_to_hex(sha1)); if (unpack_trees(nr, desc, &opts)) @@ -238,6 +251,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix) "reset HEAD, index and working tree", HARD), OPT_SET_INT(0, "merge", &reset_type, "reset HEAD, index and working tree", MERGE), + OPT_SET_INT(0, "keep-local-changes", &reset_type, + "reset HEAD but keep local changes", + KEEP_LOCAL_CHANGES), OPT_BOOLEAN('q', NULL, &quiet, "disable showing new HEAD in hard reset and progress message"), OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), @@ -324,9 +340,13 @@ int cmd_reset(int argc, const char **argv, const char *prefix) if (reset_type == SOFT) { if (is_merge() || read_cache() < 0 || unmerged_cache()) die("Cannot do a soft reset in the middle of a merge."); + } else { + int err = reset_index_file(sha1, reset_type, quiet); + if (reset_type == KEEP_LOCAL_CHANGES) + err = err || reset_index_file(sha1, MIXED, quiet); + if (err) + die("Could not reset index file to revision '%s'.", rev); } - else if (reset_index_file(sha1, reset_type, quiet)) - die("Could not reset index file to revision '%s'.", rev); /* Any resets update HEAD to the head being switched to, * saving the previous head in ORIG_HEAD before. */ -- 1.6.5.1.gaf97d -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html