threads / discuss / 23717

Problem with git merge/rebase with conflicts

Subject: Problem with git merge/rebase with conflicts

## tl;dr

5 messages between May 6, 2010 and May 8, 2010.

replies: 4people: 3as markdown or json

Eli Barzilay· May 6, 2010, 10:06 UTC · lore

I'm writing about merging and rebasing with conflicts, and there's something weird with it -- after this setup:

  mkdir /tmp/r1; cd /tmp/r1; git init
  echo foo > file; git add file; git commit -m first
  cd /tmp; git clone r1 r2
  cd /tmp/r1; echo abc > file; git commit -m abc file
  cd /tmp/r2; echo xyz > file; git commit -m xyz file
  git fetch
1. I get this:
     $ git merge origin
     ...CONFLICT...
     $ git status -s
     UU file
     $ echo xyz > file
     $ git add file
     $ git status -s
   and there's nothing -- it's all clean after that `add', without
   committing and without anything said.  If this is intentional, it
   would be nice if there was some message from `git add'...  And it
   would also be nice if I'd seen it mentioned anywhere, but
   practically everything I've read said the same: `git add' and then
   `git commit', no mention of that second step being optional.
2. I have some other sample repository that exhibits the behavior with
   the "resolution" going the other way, but here -- starting from the
   same setup:
     $ git merge origin
     ...CONFLICT...
     $ echo abc > file
     $ git add file
     $ git status -s
     M  file
     $ git commit
     ...get an editor without the conflict resolution template thing...
     $ git log --graph --all --oneline
     * 2d3744b Huh?
     * 98c46da xyz
     | * 4744b19 abc
     |/  
     * c8915c2 first
   This looks like I never did any merge.
     $ git merge origin
     Merge made by recursive.
     $ git log --graph --all --oneline
     *   94e69e9 Merge remote branch 'origin'
     |\  
     | * 4744b19 abc
     * | 2d3744b Huh?
     * | 98c46da xyz
     |/  
     * c8915c2 first
   And this looks even more wrong...
3. Again, starting from the same setup:
     $ git rebase origin
     ...CONFLICT...
     $ git status -s
     UU file
     $ echo abc > file
     $ git add file
     $ git status -s
                       <- nothing
     $ git rebase --continue
     Applying: xyz
     No changes - did you forget to use 'git add'?
   which is probably related to the above.  I could use --skip, but
   now that I edited it manually I can't remember that --skip does
   what I already did.
-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!
Junio C Hamano· May 6, 2010, 19:13 UTC · re: Eli Barzilay · lore

Re: Problem with git merge/rebase with conflicts

Eli Barzilay <eli@barzilay.org> writes:
> 1. I get this:
> 3. Again, starting from the same setup:

I don't have time to look at the second one, but these are quite obvious and natural. "status" in these cases notices that you haven't made any change relative to HEAD (in either the index nor in the working tree) after all, and that is what is being reported.

Eli Barzilay· May 6, 2010, 20:45 UTC · re: Junio C Hamano · lore

Re: Problem with git merge/rebase with conflicts

On May  6, Junio C Hamano wrote:
Show 6 quoted lines
> Eli Barzilay <eli@barzilay.org> writes:
> 
> > 1. I get this:
> > 3. Again, starting from the same setup:
> 
> I don't have time to look at the second one,

(FWIW, I can't recreate it now -- very weird since when I sent that message it was definitely repeatable.)

> but these are quite obvious and natural.  "status" in these cases
> notices that you haven't made any change relative to HEAD (in either
> the index nor in the working tree) after all, and that is what is
> being reported.

Yes, I agree that they're doing "obviously" doing the right thing, but I don't think that they're natural enough... I'm not saying that it's doing something broken, just that some of the interface (=> printouts) could eliminate surprises. Here are some problems that I've seen, IMO, some of them are indications that this is not natural. I'll prefix each one with the false belief that I had (and that I didn't see any text that would correct it...).

1. False belief: After you edit and `git add' a file, you will need to
   commit it to make the change part of your history.
   Obvious counter example: edit a file and `git add' it; undo the
   edit in the file and `git add' again -- you now have nothing to
   commit.
   A way that I think will solve this: make `git add' notify you that
   the changes that you just added canceled earlier changes and you're
   back to a clean state.
2. False belief: If you have a completely clear working directory and
   index, then there's nothing for you to commit.  (At least without
   forcing an empty commit.)
   Counter example: run this (simplified example)
     rm -rf /tmp/test; mkdir /tmp/test; cd /tmp/test; git init
     echo foo > file; git add file; git commit -m first
     git branch a-branch;   echo abc > file; git commit -m abc file
     git checkout a-branch; echo xyz > file; git commit -m xyz file
     git merge master; echo xyz > file; git add file
     git status
   That last `status' will happily tell you that "nothing to commit
   (working directory clean)" -- but `git commit' *will* commit the
   merge.
   A way that I think will solve this: clearly there is somewhere
   something that makes the commit possible (probably .git/MERGE*
   files) -- so make `git status' inspect these and say something
   about it instead of saying that there's nothing to commit.  (The
   other option would be to make `git add' clear out the merge state
   too, but this is probably very broken.)
2a. A related problem: usually when I try to checkout a different
   branch when I have changes, git barfs at me.  But after that script
   is run, I can `git checkout master' with no errors or warnings, and
   the merge commit is gone.  It looks like checkout is even careful
   enough to remove the .git/MERGE* files, so making it barf (or at
   least spit out some warning) is probably easy...
3. False belief: if your working directory and your index are dirty,
   then there *is* something for you to commit.
   Obvious counter example: edit a file and `git add' it; undo the
   edit in the file but *don't* `git add' again -- `git status -s'
   will now say:
     MM file
   And now:
     $ git commit .
     # On branch master
     nothing to commit (working directory clean)
   but the status is still not cleared.  Trying to checkout a
   different branch gives me the barf.
   A way that I think will solve this: make `git commit <path>' add
   the paths before committing -- and if `add' says something, it will
   say that here and clarify what happened.  But this is probably too
   much of a change, so alternatively `git commit' would notice that
   there was nothing to commit because of this state, and `add' the
   right files to clear out the status (after saying something about
   it).
4. `git rebase' issues -- I've sent a separate message about that, and
   IMO adding those notes to what rebase says would clear up most of
   the confusion, and probably if the above are done then it will also
   help rebase to be more robust.
-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!
Jeff King· May 8, 2010, 05:20 UTC · re: Eli Barzilay · lore

Re: Re: Problem with git merge/rebase with conflicts

On Thu, May 06, 2010 at 04:45:44PM -0400, Eli Barzilay wrote:
Show 10 quoted lines
> 1. False belief: After you edit and `git add' a file, you will need to
>    commit it to make the change part of your history.
> 
>    Obvious counter example: edit a file and `git add' it; undo the
>    edit in the file and `git add' again -- you now have nothing to
>    commit.
> 
>    A way that I think will solve this: make `git add' notify you that
>    the changes that you just added canceled earlier changes and you're
>    back to a clean state.

I don't understand how this is a false belief. You _haven't_ made anything a part of your history. It was already there, and you've done nothing to the history.

But I thought your original question started out with a conflicted merge:

Show 10 quoted lines
>   mkdir /tmp/r1; cd /tmp/r1; git init
>   echo foo > file; git add file; git commit -m first
>   cd /tmp; git clone r1 r2
>   cd /tmp/r1; echo abc > file; git commit -m abc file
>   cd /tmp/r2; echo xyz > file; git commit -m xyz file
>   git fetch
>   git merge origin
>   echo xyz > file
>   git add file
>   git status -s

In that case you _do_ have to commit, or your history will not contain the merge! Even though you happened to reject the other sides changes, you still want to generate a merge commit showing that in your history you have examined and rejected the other side's changes.

The only failing here, if any, is that "git status" does not say "you are in the middle of a merge, and should commit it".

Show 16 quoted lines
> 2. False belief: If you have a completely clear working directory and
>    index, then there's nothing for you to commit.  (At least without
>    forcing an empty commit.)
> 
>    Counter example: run this (simplified example)
> 
>      rm -rf /tmp/test; mkdir /tmp/test; cd /tmp/test; git init
>      echo foo > file; git add file; git commit -m first
>      git branch a-branch;   echo abc > file; git commit -m abc file
>      git checkout a-branch; echo xyz > file; git commit -m xyz file
>      git merge master; echo xyz > file; git add file
>      git status
> 
>    That last `status' will happily tell you that "nothing to commit
>    (working directory clean)" -- but `git commit' *will* commit the
>    merge.

Yeah, probably it should detect the presence of .git/MERGE_HEAD and print a different message.

Show 17 quoted lines
> 3. False belief: if your working directory and your index are dirty,
>    then there *is* something for you to commit.
> 
>    Obvious counter example: edit a file and `git add' it; undo the
>    edit in the file but *don't* `git add' again -- `git status -s'
>    will now say:
> 
>      MM file
> 
>    And now:
> 
>      $ git commit .
>      # On branch master
>      nothing to commit (working directory clean)
> 
>    but the status is still not cleared.  Trying to checkout a
>    different branch gives me the barf.

I think you misunderstand how "git commit ." works. It already means "git add . && git commit". So you are actually _removing_ the thing to commit in the first step. Running "git commit" would work.

Which is what you suggest:
Show 7 quoted lines
>    A way that I think will solve this: make `git commit <path>' add
>    the paths before committing -- and if `add' says something, it will
>    say that here and clarify what happened.  But this is probably too
>    much of a change, so alternatively `git commit' would notice that
>    there was nothing to commit because of this state, and `add' the
>    right files to clear out the status (after saying something about
>    it).

Except that I guess you want the add process to say "the thing you added is the same as HEAD, so you just reverted the original thing you added". Personally I would find such a notification annoying.

-Peff
Eli Barzilay· May 8, 2010, 05:52 UTC · re: Jeff King · lore

Re: Re: Problem with git merge/rebase with conflicts

On May  8, Jeff King wrote:
Show 16 quoted lines
> On Thu, May 06, 2010 at 04:45:44PM -0400, Eli Barzilay wrote:
> 
> > 1. False belief: After you edit and `git add' a file, you will
> >    need to commit it to make the change part of your history.
> > 
> >    Obvious counter example: edit a file and `git add' it; undo the
> >    edit in the file and `git add' again -- you now have nothing to
> >    commit.
> > 
> >    A way that I think will solve this: make `git add' notify you
> >    that the changes that you just added canceled earlier changes
> >    and you're back to a clean state.
> 
> I don't understand how this is a false belief. You _haven't_ made
> anything a part of your history. It was already there, and you've
> done nothing to the history.

Well, the false belief here is following every text that (very reasonably) tells you that you should `git add' the file and then `git commit' it. Obviously, with a cooked example like that things are made very clear -- but what if I worked on something for a while (adding stuff to the index as I go), and missed the fact that the file is eventually at exactly where it started? Even with this it's probably not too bad as is, but with merges it gets even more potential to be confusing. In any case, I'm not saying that anything is wrong -- just that it would be nice if `git add' told me about it, so I can never see a transcript that ends with:

  $ git add foo
  $ git commit
  ... nothing to commit

since that canceling add will tell me explicitly that this addition got things back to a state of having no changes. (And I don't see any harm in that -- if you want to explicitly revert things back you'd use reset or something, so this change is something that you'd only see if you were confused as above.)

Show 22 quoted lines
> But I thought your original question started out with a conflicted
> merge:
> 
> >   mkdir /tmp/r1; cd /tmp/r1; git init
> >   echo foo > file; git add file; git commit -m first
> >   cd /tmp; git clone r1 r2
> >   cd /tmp/r1; echo abc > file; git commit -m abc file
> >   cd /tmp/r2; echo xyz > file; git commit -m xyz file
> >   git fetch
> >   git merge origin
> >   echo xyz > file
> >   git add file
> >   git status -s
> 
> In that case you _do_ have to commit, or your history will not
> contain the merge! Even though you happened to reject the other
> sides changes, you still want to generate a merge commit showing
> that in your history you have examined and rejected the other side's
> changes.
> 
> The only failing here, if any, is that "git status" does not say
> "you are in the middle of a merge, and should commit it".

Yes, that was exactly the conclusion of my following point. Well, even more than that -- if `git add' will tell you about getting into a clean state, then you'd get this for the first case above:

  $ git add foo
  note: this addition canceled out all changes
  $ git commit
  ... nothing to commit         # <-- this is obvious now
  $ git status -s
                                # <-- as is the lack of output here

and for the merge case, `git add' could note that, in addition to a clean state, you still have a merge to finish, so it could say this:

  $ git merge blah
  ...
  $ git add foo
  note: this addition canceled out all changes,
  but you still have a pending merge to commit
  $ git status -s
  (merge pending)               # <-- this is my item#2 suggestion
Show 35 quoted lines
> > 3. False belief: if your working directory and your index are dirty,
> >    then there *is* something for you to commit.
> > 
> >    Obvious counter example: edit a file and `git add' it; undo the
> >    edit in the file but *don't* `git add' again -- `git status -s'
> >    will now say:
> > 
> >      MM file
> > 
> >    And now:
> > 
> >      $ git commit .
> >      # On branch master
> >      nothing to commit (working directory clean)
> > 
> >    but the status is still not cleared.  Trying to checkout a
> >    different branch gives me the barf.
> 
> I think you misunderstand how "git commit ." works. It already means
> "git add . && git commit". So you are actually _removing_ the thing to
> commit in the first step. Running "git commit" would work.
> 
> Which is what you suggest:
> 
> >    A way that I think will solve this: make `git commit <path>'
> >    add the paths before committing -- and if `add' says something,
> >    it will say that here and clarify what happened.  But this is
> >    probably too much of a change, so alternatively `git commit'
> >    would notice that there was nothing to commit because of this
> >    state, and `add' the right files to clear out the status (after
> >    saying something about it).
> 
> Except that I guess you want the add process to say "the thing you
> added is the same as HEAD, so you just reverted the original thing
> you added".
Yes.
> Personally I would find such a notification annoying.

Isn't this something that you'd never do intentionally? I'm not talking about doing this for any specific file in the commit (actually the `add' part of the commit) -- I'm suggesting that it will say that only if the result of adding stuff made the following commit do nothing. So this message would only annoy you if you've done some changes and added them, then you edit the files back to the original state, and then use `git add' or `git commit <paths>' (!) to add the changes so you're back to a clean state. This sounds like a very odd way of undoing changes (especially with git that gives me about 200 ways to undo changes more directly...).

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

← back to recent threads