(On re-reading your question, I might have answered something you did not actually intend to ask! It's all about what happens to the other Git's working tree, if there is one.)
John Zwinck's answer is correct. I have (much) more in my answer to What does GIT PUSH do exactly? on this part, about how git push
pushes commits, not files (which makes your work-tree irrelevant). Please read that to see what the other Git on the remote does with your git push
(and remember that that other Git has the right to either accept your push reference update request(s), or reject one, or many, or all of them).
Part 3—what happens to the working directory for the receiving Git—is the trickiest. It depends on the configuration set up on that other Git. It also depends on the version of the other Git, because there is a configuration that only works in newer versions.
First, let's assume that the remote Git accepts your push ref-update, because if it rejects it, well, nothing happens.1 If nothing happens, the question of "what happens" is pretty boring! So the other Git has accepted your push, and has some new commits in its repository,2 and has a reference—a branch or tag name—pointing to one of those new commits. If this is a new branch, or a new tag, then the receiving Git repository just has a new branch or tag, and any existing branch or tag is not affected, and again the question is kind of boring: there is clearly no need to change anything in the working tree.
Next, we need to talk-about a so-called bare repository.
You already know that a Git repository has two parts of interest here, and it's important to mention the third:
The repository itself, where all commits, and everything they need, are stored forever, in a format usable only by Git itself, so that you can git checkout
any particular branch, or git checkout
any older commit to see what was in that—this gives you the dreaded "detached HEAD" after which you must git checkout
some branch again to re-attach your HEAD. You can also git checkout
a particular tag, which also gives you a detached HEAD.3 Git's repository objects (commits, plus the other items commits use to store files) within the repository are permanent and unchanging, and Git can only ever add to to them.4
The work tree or working tree (two names for the same thing): normal, computer- and human-accessible files with normal names, where you can view and edit the files, run your program or app, work with your web page, or whatever it is that you are doing with Git.
The index or staging-area (again, two names for the same thing—though there's a third name, the cache, as in git diff --cached
, which does exactly the same thing as git diff --staged
). The index is mostly "what will go into the next commit": after you edit a file in the work-tree, you git add
the file. This copies its new contents into the index,5 so that it is ready to go into the next commit. Otherwise the next commit you make will save the file the way it was/is in the current commit, i.e., the way it is right now in the index.
(The index also has a big and important role in doing merges, but this is for another topic. It has a smaller role in making Git as fast as Git is, which is where the "cache" aspect comes in.)
Now, a bare repository is quite simply a Git repository without a work tree. Since it has no work tree, there's nothing to worry about here. You can push to any branch in a bare repository (provided you meet whatever requirements that Git sets for accepting pushes).
So that leaves us with a non-bare ("regular"?) Git repository, to which you are pushing, that accepts your push. What happens to its work tree? Well, now we're back to: "that depends."
All Git repositories—including bare ones, for that matter—still have the notion of the current branch. The current branch is the one you set with git checkout branchname
. In most repositories it starts out as master
, and you git checkout
some other branch to change it as needed.
All Git repositories also have configurations. One of the configuration items is receive.denyCurrentBranch
. This can be set to true
, refuse
, false
, warn
, ignore
, or—in newer Git versions—updateInstead
. The default, if you do not set anything, is to pretend it is set to refuse
. Each setting results in different behavior:
If the setting is refuse
or true
, the receiving Git simply automatically rejects any attempt to push to its current branch. The update request for this one branch is rejected. That makes the problem go away. Update requests within the same push, but for other branches, may still be accepted, of course.
If the setting is false
, ignore
, or warn
, the receiving Git accepts the update, but does not update the working tree (nor the index). They become "out of sync", which is generally bad; but it's up to whoever manages that Git to deal with it. If the setting is warn
, then you (the one doing the push) get a warning message about this, which does them (the people running the receiving Git) not one bit of good.6
If the setting is updateInstead
—available only since Git version 2.3—then the receiving Git actually checks: "is the branch all checked-in nicely?" If so, it allows the update and does a full checkout, so that the index and work-tree get synchronized. If not—if either the index, or the work-tree, have uncommitted changes—it refuses the push.
There's another tricky case here, available in versions of Git at 2.5 or higher: you can now have more than one work-tree associated with a repository. Each work-tree gets its own independent index, and its own current branch. How a receiving Git treats these, I don't know. Logically, "refuse" modes should refuse the update if any associated work-tree has that branch as its current branch, and "updateInstead" modes should update whichever work-tree has that branch, but I have not tested this.
That, I think, covers all the cases available today. Most central repositories are bare, so that the current branch on that repository is irrelevant. (Note as well that none of this affects any post-receive
deploy scripts, some of which will automatically check out certain branches to special alternative work-trees, overriding the "bare-ness". But this process is separate from Git itself, and is entirely up to the post-receive hook.)
1As usual, the complete truth is even more complicated: the other end could reject your push through a pre-receive or update hook, while—via that hook—making something happen. But that's quite unlikely, so let's just ignore it.
2In a few cases—the classic examples are a force-push to "retract" a commit, or a simple lightweight-tag push to tag some existing commit—there's no new commit, just a change to a branch name, or the addition of a new tag.
3A work-tree with a "detached HEAD" is on no branch. However, you can still make new commits in it. When you do so, they effectively go into an anonymous (unnamed) branch. Because that branch has no name, it doesn't enter into the rest of this discussion: A push request that succeeds updates some reference name, and this no-branch / anonymous-branch "detached HEAD" work-tree has no name, so it cannot be updated. (You could say "it has the name HEAD
": which is true, but HEAD
is not a branch name. Moreover, because it lives outside the refs/
name-space, it can never be pushed-to.)
4There's one exception to this "only ever add new things" rule. Some objects are meant to be temporary, so while no object can ever change, Git does have a mechanism for getting rid of obsolete, unused objects. That's Git's "garbage collector", or git gc
. You can also use this to discard unwanted commits, by rewinding a branch so that it no longer refers to them. Such commits become "abandoned" and are eventually—not immediately—swept away.
(Note that they're never changed, only ghosted, and then eventually deleted entirely. The "ghosts" can be found through reflogs, until the reflog entries expire, and then until git gc
cleans them out they can be found even more painfully with git fsck
, which is a lot like git gc
except that it checks everything extra-carefully, and optionally restores lost items, instead of discarding them.)
5In fact, rather than copying the entire file into the index, what Git does is store the file's hash ID in the index. Running git add
actually sticks the file into the repository, as a "blob" object. Git then puts the blob's ID into the index. If you update and git add
a file, then update and git add
again, you get an unreferenced ("dangling", in git fsck
terms) blob, which the garbage collector eventually reaps; but until then, it's actually possible to recover the added version.
6The warn
mode is really intended for the case where you are in control of the receiving repository. It reminds you to go log in to the other machine if necessary, then go over to that repository and take care of the problem.