I am new to git and trying to wrap my head around the way branches work. According to the documentation git checkout
Updates files in the working tree to match the version in the index or the specified tree. If >no paths are given, git checkout will also update HEAD to set the specified branch as the >current branch.
So as I understand it the files in my directory that I work in (the file I performed the git init in) would should change according to what branch I am in. I am confused because this does not happen when I change between branches. Edits I was working on before I switched branches are present in the branch I switched to. Am I doing something wrong or does git checkout not work this way and I am just misunderstanding the docs?
When you create a branch, that branch will automatically get the files of the branch you were in when you created this new branch.
Let's say you are in master branch and you want to create a
develop
branch. All together should look like this:And then you won't see
text.txt
since you are inmaster
.That confusion is acknowledged by Git 2.23.
Git 2.23 (Q3 2019) will replace
git checkout
with two new commands:git switch
git restore
(illustrated here)See commit 97ed685, commit d16dc42, commit bcba406 (20 Jun 2019), commit 4e43b7f, commit 1235875, commit 80f537f, commit fc991b4, commit 75f4c7c, commit 4df3ec6, commit 2f0896e, commit a5e5f39, commit 3a733ce, commit e3ddd3b, commit 183fb44, commit 4058199, commit a6cfb9b, commit be8ed50, commit c9c935f, commit 46e91b6 (25 Apr 2019), and commit 328c6cb (29 Mar 2019) by Nguyễn Thái Ngọc Duy (
pclouds
).(Merged by Junio C Hamano --
gitster
-- in commit f496b06, 09 Jul 2019)And:
See
git switch
man pageGit has this general issue of cramming about eight or ten different things into one command. Note: Git 2.23 is splitting some of these up—helpful, to be sure, but also a very big change. (Should Git 2.23 be called Git 3.0? Git 2.0 changed the behavior of
git add
, which seems to me similar in degree.) See also VonC's answer.git checkout
can update the working tree, and usually does.It can change where
HEAD
points, and sometimes does, sometimes doesn't.It can overwrite work you did to a file, in case you want to reset the file and undo your work. Or it can refuse to overwrite work you did to a file, leaving it unchanged while either changing
HEAD
, or not changingHEAD
.The thing about all this is that, while it's remarkably difficult to describe, it actually all makes sense and after a while you get used to this and find that the one command does what you mean, most of the time. (It's that "most of the time" that can be a problem, of course....)
Anyway, the particular behavior you're seeing is a deliberate feature. Let's say you start out on branch
master
, as most repositories do:At this point you might edit some file(s), get some work going, and only then realize: "gah! I meant to do this on branch
develop
!"1What git lets you do at this point is switch to (or create) branch
develop
, keeping your modifications, under one condition: that switching todevelop
does not require wiping them out. Let's say you modified filef1
and created a newf2
, and you now want to create and check out local branchdevelop
that should start from, and automatically "track",2origin/develop
:(in very old versions of git, you have to spell this
git checkout -b develop --track origin/develop
).Let's also say that file
f1
is the same at the tips of branchmaster
and branchdevelop
.3 What this means, to git, is that it can do this checkout, because it does not have to modify filef1
, so it can leave your existing changes tof1
in place.If file
f2
is also the same in both commits, or (as in this case) does not exist in either one, then no files will be clobbered, andgit checkout
will create your new local branchdevelop
, modifying the work tree to matchorigin/develop
as needed—and this does not include modifyingf1
, nor removingf2
, so the work you have done so far remains intact.This allows you to commit your new changes to your local
develop
.(If you run into cases where git does have to undo your changes, but still want to "move" them to another branch, the usual trick is to use the
git stash
script. This sounds like a simple thing, andgit stash
is often simple to use, but it is actually quite a complicated little beast under the covers. Don't worry about that until you need it, though.)1This happens to me all the time. Many times I want to create a new non-tracking branch, which is a bit simpler than switching to an existing branch, but the principle still applies.
2This automatic tracking allows you to more easily bring in changes other people have done: once git picks them up with
git fetch
, git will inform you about those other-people's-changes, and let you usegit merge
orgit rebase
to combine your changes with theirs, without a lot of extra poking-about to figure out whose changes go where.3Since you're new to git, concepts like distinguishing "the tip of a branch", which is a specific commit, from "the branch", which is actually ambiguous—there are branch labels, and then there are branch structures formed by the commit tree—is something else you mostly should ignore for a while. The main thing to note is that there's a special file in the git repository named
HEAD
, and in that special file, git writes the stringref: refs/heads/master
orref: refs/heads/develop
, in order to keep track of which branch you're on. Sogit checkout X
will writeref: refs/heads/X
intoHEAD
once it switches to branchX
.Meanwhile, another set of special files in the repository tell git that branch
master
refers to one of those big ugly SHA-1s likec06f8d11b75e28328cdc809397eddd768ebeb533
. This is the "tip" of branchmaster
. When you make a new commit onmaster
, git creates the new commit "one past the old tip", then writes the new SHA-1 into the branch files, so thatmaster
is now your new commit.The precise details don't matter so much as the idea that new commits simply advance the branch-tip.