Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are typical use cases of git-reset's --merge and --keep flags?

Tags:

git

git-reset

In a recent answer in which he details the typical use cases of git-reset's three most commonly used options (--hard, --mixed, and --soft), torek mentions in passing that git-reset also offers two relatively esoteric flags, called --merge and --keep. The git-reset man page describes those two flags as follows:

--merge
           
   Resets the index and updates the files in the working tree
   that are different between <commit> and HEAD, but keeps
   those which are different between the index and working tree
   (i.e. which have changes which have not been added). If a
   file that is different between <commit> and the index has
   unstaged changes, reset is aborted.

   In other words, --merge does something like a git read-tree
   -u -m <commit>, but carries forward unmerged index entries.

--keep
    Resets index entries and updates files in the working tree
    that are different between <commit> and HEAD. If a file that
    is different between <commit> and HEAD has local changes,
    reset is aborted.

I perfectly understand when to use --hard, --mixed, or --soft, but I only learned that --merge and --keep existed while reading torek's answer, and I can't think of practical use cases of those two flags... In what situations do you typically use those two flags?

I'm mainly looking for a plain-English explanation. Take the following passage of this answer by VonC, which spells out a typical use case for git reset --soft, as a model:

[...] each time:

  • you are satisfied with what you end up with (in term of working tree and index)
  • you are not satisfied with all the commits that took you to get there:

git reset --soft is the answer.

However, I'm not averse to a little experiment with those flags, similar in spirit to the silly shopping-list example I posted in this answer of mine.

like image 821
jub0bs Avatar asked Aug 28 '14 15:08

jub0bs


People also ask

What does git merge do?

Merging is Git's way of putting a forked history back together again. The git merge command lets you take the independent lines of development created by git branch and integrate them into a single branch.

What is reset and keep changes in git?

git reset --soft , which will keep your files, and stage all changes back automatically. git reset --hard , which will completely destroy any changes and remove them from the local directory. Only use this if you know what you're doing.

What is soft reset in git?

Soft Reset It will just move the HEAD explicitly to the commit. All changes in the working area and staging area will stay intact. The syntax to perform a hard reset using the HEAD pointer will be − git reset --soft HEAD~<i>

How do I keep git from merging?

How do I cancel a git merge? Use git-reset or git merge --abort to cancel a merge that had conflicts. Please note that all the changes will be reset, and this operation cannot be reverted, so make sure to commit or git-stash all your changes before you start a merge.


3 Answers

Frankly I'm not really sure about this; I've never even used the --merge and --keep modes myself. However, the release notes indicate that git reset --merge was added in git version 1.6.2, with the following note:

  • git reset --merge is a new mode that works similar to the way git checkout switches branches, taking the local changes while switching to another commit.

and --keep was added in 1.7.1:

  • git reset learned --keep option that lets you discard commits near the tip while preserving your local changes in a way similar to how git checkout branch does.

Then, in 1.7.9:

  • git checkout -B <current branch> <elsewhere> is a more intuitive way to spell git reset --keep <elsewhere>.

which tells us that the idea behind --keep is, you have started work on some branch, and then you realize: oh, this branch should fork off from some other point (probably the tip of some other branch). For instance you might:

$ git checkout devel
$ git checkout -b fix-bug-1234

and then do some work to fix bug 1234 for the next release; but then someone says: "hey, we need bug 1234 fixed for the old release version!" So now you want fix-bug-1234 to branch off from release instead of devel. Meanwhile you haven't committed anything yet. So you:

$ git checkout -B fix-bug-1234 release

to move it to "coming off release" instead of "coming off devel". Which works in 1.7.9 or later, but in 1.7.1 through 1.7.8 you have to spell it git reset --keep.

That might explain --keep but --merge is still a bit of a mystery.

like image 79
torek Avatar answered Oct 17 '22 13:10

torek


I agree that at a first look those flags seem to be exotic. It took me hours to understand them, however the difference is pretty clear: --keep unstages the index while --merge completely discards the index.

Going back to a typical use cases: Imagine that you have a configuration file specific to your local environement, e.g., containing credentials to your local database. When you are going to do a "hard" reset, then for sure you don't want to loose your local changes. In that case

git reset --keep <commit>

perfectly does the job. This shows that more often you want to use --keep flag instead of --hard flag.

The flag --merge is a bit more aggressive than --keep since it completely discardes the index (notice that in this case you can lose your work in opposite to --keep), but in my opinion the practical use cases for both of them are the same.

Originally, git reset --merge was introduced to abort a merge, but now git merge --abort is prefered (the first option was introduced in 1.6.2 version while the second one in 1.7.4).

like image 34
szydra Avatar answered Oct 17 '22 12:10

szydra


From https://git-scm.com/docs/git-reset

reset --keep is meant to be used when removing some of the last commits in the current branch while keeping changes in the working tree. If there could be conflicts between the changes in the commit we want to remove and the changes in the working tree we want to keep, the reset is disallowed. That’s why it is disallowed if there are both changes between the working tree and HEAD, and between HEAD and the target. To be safe, it is also disallowed when there are unmerged entries.

I can think about scenario where there are some local commits: Commit0, Commit1, Commit2, ..., CommitX, CommitY, ..., CommitN

and I want to discard changes in Commit1..CommitX, but preserve changes in CommitY..CommitN

In order to do this I could use git reset --soft CommitX, and then git reset --keep Commit0

I guess without --keep, someone would need to use stash in order to do this.

like image 1
anth Avatar answered Oct 17 '22 11:10

anth