Confused by git checkout

前端 未结 3 2097
夕颜
夕颜 2020-11-21 22:21

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

3条回答
  •  小鲜肉
    小鲜肉 (楼主)
    2020-11-21 23:21

    Git 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 changing HEAD.

    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:

    $ git clone ...
    $ git branch
    * master
    $
    

    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!"1

    What git lets you do at this point is switch to (or create) branch develop, keeping your modifications, under one condition: that switching to develop does not require wiping them out. Let's say you modified file f1 and created a new f2, and you now want to create and check out local branch develop that should start from, and automatically "track",2 origin/develop:

    $ git checkout 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 branch master and branch develop.3 What this means, to git, is that it can do this checkout, because it does not have to modify file f1, so it can leave your existing changes to f1 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, and git checkout will create your new local branch develop, modifying the work tree to match origin/develop as needed—and this does not include modifying f1, nor removing f2, 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, and git 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 use git merge or git 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 string ref: refs/heads/master or ref: refs/heads/develop, in order to keep track of which branch you're on. So git checkout X will write ref: refs/heads/X into HEAD once it switches to branch X.

    Meanwhile, another set of special files in the repository tell git that branch master refers to one of those big ugly SHA-1s like c06f8d11b75e28328cdc809397effffd768ebeb533. This is the "tip" of branch master. When you make a new commit on master, git creates the new commit "one past the old tip", then writes the new SHA-1 into the branch files, so that master is now your new commit.

    The precise details don't matter so much as the idea that new commits simply advance the branch-tip.

提交回复
热议问题