views:

46

answers:

1

I had a weird situation today.. I'll try and recreate what I think happened.

I wanted to move a speculative change (made to a_file) to a new branch and continue working on the main branch without it.

On master with a_file and b_file dirty I did:

git checkout -b saved_feature

git commit a_file -m "Putting this modification on a different branch"

git checkout master

At this point git complained that b_file was dirty - this was a warning sign but I didn't recognise it.

I was still on branch saved_feature so I thought I could do:

git stash save

git checkout master

So far so good

git stash pop

At this point I get an error saying that the stash couldn't be merged.

I examine the log - for some reason there are about 6 days worth of commits that were originally committed to the master branch but which were not in the log. These commits were only on the new branch that I had created (I checked in case I had been committing them to a different branch).

After examining the log of the saved_feature branch, I can see that they are all there.

What happened?

I next tried on master:

git merge saved_feature

But the fast forward option failed with a ton of conflicts.

I also use git-svn to push changes to an external repository so on master again I did:

git svn rebase

This recovered some of the previously pushed commits from svn.

I then cherry-picked the rest of the most recent commits from the saved_feature branch, I then did a git stash pop, fixed the conflicts that should have been automatically merged, but were not, and finally had my master in the state it was originally.

Could anyone point out in this sorry tale where my mental model and git parted ways & how I can avoid getting into messes such as these again?

+3  A: 

You were probably working those last 6 days on a detached HEAD from an ancestor of master HEAD.
(thank you Chris Johnsen for suggesting that last part: it explains potentially everything)

Suppose you have:

x--x--x--x <- master
      ^
      |
      T1 (tag)

If you do a 'git checkout T1' to see that T1 content, boom: detached HEAD right there.
If you do some fixes from T1, you end up with

x--x--x--x <- master
      ^\
      | -y--y--y--y--y--y <- detached HEAD (a dirty, b dirty)
      |
     T1(tag)

Now, a git checkout -b saved_feature at this point would create a branch with the last 6 (days worth of) commits in it:

x--x--x--x <- master
      ^\
      | -y--y--y--y--y--y--a <- saved_feature (a committed, b dirty)
      |
     T1(tag)

, but the checkout to master is not trivial for file b.
And a merge from master to saved_feature won't be trivial either (certainly not a fast-forward one)

Anyhow, that is why it is important to keep an eye at all time at the current branch you are actually working in.

VonC
I'd definitely recommend using the `__git_ps1` function from git's bash completion (and use the completion too!) instead of that blog post's homegrown solution.
Jefromi
@Jefromi: I agree. At least that blog post does mention the `__git_ps1 ` function. (even though that other "homegrown" one is nice too: http://asimilatorul.com/index.php/2010/04/13/bash-prompt-with-git-branch-and-status/#readmore-entry100413-221424)
VonC
One possible explanation of why merging *saved_feature* into *master* could be a non-fast-forward merge and have conflicts: The OP detached his HEAD at some ancestor of the tip of *master* (e.g. investigating an old snapshot) and his new changes had conflicts with the state of the tip of *master* (e.g. fixing bugs that were already fixed at the tip of *master*, but in different ways).
Chris Johnsen
@Chris: very true; I have included that scenario in my answer. Thank you.
VonC
Hmmm I'm pretty sure I didn't detach my HEAD by checking out old commits, the only thing I've been doing that I can imagine could have detached the head would have been `git svn rebase`
EoghanM
@EoghanM: and you didn't have any issue during your last "`git svn rebase`" like in this thread? http://us.generation-nt.com/bug-557477-git-svn-git-svn-dcommit-doesnt-handle-network-failure-help-168985541.html
VonC
@EoghanM: Or you didn't '`git checkout svn/trunk`, which would check out a detached head, since `svn/trunk` is a remote. (see this thread: http://www.spinics.net/lists/git/msg133724.html)
VonC