• Linus Torvalds's avatar
    Add 'merge' mode to 'git reset' · 9e8eceab
    Linus Torvalds authored
    We have always had a nice way to reset a working tree to another state
    while carrying our changes around: "git read-tree -u -m". Yes, it fails if
    the target tree is different in the paths that are dirty in the working
    tree, but this is how we used to switch branches in "git checkout", and it
    worked fine.
    
    However, perhaps exactly _because_ we've supported this from very early
    on, another low-level command, namely "git reset", never did.
    
    But as time went on, 'git reset' remains as a very common command, while
    'git read-tree' is now a very odd and low-level plumbing thing that nobody
    sane should ever use, because it only makes sense together with other
    operations like either switching branches or just rewriting HEAD.
    
    Which means that we have effectively lost the ability to do something very
    common: jump to another point in time without always dropping all our
    dirty state.
    
    So add this kind of mode to "git reset", and since it merges your changes
    to what you are resetting to, just call it that: "git reset --merge".
    
    I've wanted this for a long time, since I very commonly carry a dirty
    tree while working on things. My main 'Makefile' file quite often has the
    next version already modified, and sometimes I have local modifications
    that I don't want to commit, but I still do pulls and patch applications,
    and occasionally want to do "git reset" to undo them - while still keeping
    my local modifications.
    
    (Maybe we could eventually change it to something like "if we have a
    working tree, default to --merge, otherwise default to --mixed").
    
    NOTE! This new mode is certainly not perfect. There's a few things to look
    out for:
    
     - if the index has unmerged entries, "--merge" will currently simply
       refuse to reset ("you need to resolve your current index first").
       You'll need to use "--hard" or similar in this case.
    
       This is sad, because normally a unmerged index means that the working
       tree file should have matched the source tree, so the correct action is
       likely to make --merge reset such a path to the target (like --hard),
       regardless of dirty state in-tree or in-index. But that's not how
       read-tree has ever worked, so..
    
     - "git checkout -m" actually knows how to do a three-way merge, rather
       than refuse to update the working tree. So we do know how to do that,
       and arguably that would be even nicer behavior.
    
       At the same time it's also arguably true that there is a chance of loss
       of state (ie you cannot get back to the original tree if the three-way
       merge ends up resolving cleanly to no diff at all), so the "refuse to
       do it" is in some respects the safer - but less user-friendly - option.
    
    In other words, I think 'git reset --merge' could become a bit more
    friendly, but this is already a big improvement. It allows you to undo a
    recent commit without having to throw your current work away.
    
    Yes, yes, with a dirty tree you could always do
    
    	git stash
    	git reset --hard
    	git stash apply
    
    instead, but isn't "git reset --merge" a nice way to handle one particular
    simple case?
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    --
    
    Hmm? Maybe I'm the only one that does a lot of work with a dirty tree, and
    sure, I can do other things like the "git stash" thing, or using "git
    checkout" to actually create a new branch, and then playing games with
    branch renaming etc to make it work like this one.
    
    But I suspect others dislike how "git reset" works too. But see the
    suggested improvements above.
    
     builtin-reset.c |   26 ++++++++++++++++++--------
     1 files changed, 18 insertions(+), 8 deletions(-)
    9e8eceab
builtin-reset.c 8.17 KB