On Sat, Oct 27, 2012 at 03:51:00PM -0700, Matt Schoen wrote:

> This is my first post so I'm not sure if there's a more appropriate way to 
> post a "bug" like this one but I've been using git for a few years now and 
> having learned most of the idiosyncrasies, there's one that still bugs me. 
>  So I'm sure we're all familiar with the behavior or switching branches. 
>  Sometimes, when I switch to a branch at an earlier state, some newly added 
> files will stick around from the previous (newer) branch which count as 
> "new" files to the current (older) branch.  When I do this I'm often just 
> switching to the branch to test things out, poke around, and switch right 
> back.  At that point, when I try to switch back to the newer branch, I 
> can't because "some files will be overwritten" even though those files came 
> from the branch I'm trying to switch to!
> 
> I'm not exactly sure what should be the preferred behavior.  Maybe git 
> could compare these files the ones that would overwrite them, or maybe it 
> could delete the files when switching out of that branch.  It would be 
> great if this issue could be addressed, though.

You seem to maintain wrong mental model of how Git handles local
modifications in your work tree (and this appears to be quite common).

The thing is, while the work tree is always *based* on some recorded
repository state referenced to by a commit, neither the work tree nor
any local modifications you do there nor the index do belong to any branch.
This is crucial to understand.

Basically when you do `git checkout <whatever>`, Git ensures your work
tree and the index match the repository state pointed to by <whatever>,
and then sets the special reference named "HEAD" to contain that
<whatever>.  Then you start to modify files, record your changes to the
index etc, and then you do a commit.  Then you run `git commit` -- Git
takes the index, builds a new commit out of it, *and only then* it does
consult HEAD to see which commit will be the parent of that new commit.
Git also checks if HEAD points to a branch, and if it is, that branch
reference is updated to point to the newly created commit.
You might now see that contrary to how it might feel, the work tree, the
index and your local modifications to work tree do not really belong to
any branch -- they are just used to build a new commit, and only then
that commit might start to "belong" to somewhere.

Consequently, when you're switching branches, you're changing the base
versions of files your local modifications were made against.
When populating the work tree and the index, Git sees which files in the
work tree have local modifications; for each file found, Git checks if
its "old" base version differs from its "new" base version, and if
they're differ, Git refuses to proceed.  Possibly you can now see that
within the Git's model just described, this behaviour is logical.

To combat this problem, there are two approaches: trying to merge the
local modifications with the new base versions of modified files and
saving/restoring local modifications.

The first approach is about passing the "--merge" command-line option to
`git checkout`.  Read more in its man page.

The second approach has two options: the stash and temporary ("work in
progress") commits.

With stashing, you just run `git stash` before switching the work tree
to another commit -- Git saves away all your local changes and then
resets the index and the work tree to HEAD so that they are "clean" and
checking out another commit is guaranteed to succeed.  When you're done
with another branch and switch back, run `git stash pop` to recreate
your local modifications.

WIP commits do mostly the same thing: just commit all your local
changes, stating your intent clearly in the commit message and switch to
another branch.  When you're done with that branch and switched back,
run `git reset HEAD^` or `git reset --soft HEAD^` (the first form
implies the "--mixed" command-line option) -- you'll get your local
modifications back, but the WIP commit itself will go away.

There are pros and cons of both approaches (stashing is simpler and
quickier, more fine grained -- is able to restore the index to exact
state it was when stashing, when you had both staged and unstaged local
modifications; WIP commits can be pushed or pulled and possibly more
"visible" to the user).

One more thing: bug reporting should be done on the main (for
developers) Git list which is git at vger.kernel.org; this list is for
helping users dealing with their Git-related problems.

-- 
You received this message because you are subscribed to the Google Groups "Git 
for human beings" group.
To post to this group, send email to git-users@googlegroups.com.
To unsubscribe from this group, send email to 
git-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/git-users?hl=en.

Reply via email to