git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 3/6] reset: add option "--keep-local-changes" to "git reset"

From
Christian Couder <chriscool@tuxfamily.org>
Date
Dec 8, 2009, 07:56 UTC
Message-ID
<20091208075616.4475.31260.chriscool@tuxfamily.org>
In-Reply-To
<20091208075005.4475.26582.chriscool@tuxfamily.org>

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@iabervon.org>
Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Stephan Beyer <s-beyer@gmx.net>
Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
---
 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
Previous: Christian CouderNext: Christian Couder
Message 6 of 10 in “"git reset --merge" related improvements”
  1. 0/6 "git reset --merge" related improvementsChristian Couder, Dec 8, 2009
  2. 1/6 reset: add a few tests for "git reset --merge"Christian Couder, Dec 8, 2009
  3. 2/6 reset: use "unpack_trees()" directly instead of "git read-tree"Christian Couder, Dec 8, 2009
  4. Stephen BoydDec 8, 2009
  5. Christian CouderDec 12, 2009
  6. 3/6 reset: add option "--keep-local-changes" to "git reset"Christian Couder, Dec 8, 2009
  7. 4/6 reset: add test cases for "--keep-local-changes" optionChristian Couder, Dec 8, 2009
  8. 5/6 Documentation: reset: describe new "--keep-local-changes" optionChristian Couder, Dec 8, 2009
  9. 6/6 Documentation: reset: add some tables to describe the different optionsChristian Couder, Dec 8, 2009
  10. Junio C HamanoDec 8, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.