I'm figuring out working with branches in git. At the time I had unrelated commits in master I wasn't quite ready to push, but I had to do a bug fix. I didn't want to create a new repo just to do a small bug fix, so I created a branch based off of origin/master
git checkout -b example origin/master
Its my understanding this creates a branch based off of the origin/master branch, and switches to the example branch.
I then went and did my fix, committed it, and tested it.
$ git status
On branch example
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
I then went to push it to master.
$ git push origin master
but instead of it pushing the commit in the example branch, I found this instead
$ git status
On branch example
Your branch and 'origin/master' have diverged,
and have 1 and 3 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working directory clean
I don't understand why
The syntax for git push
is a bit obscure, but easy enough to explain for the particular case you are using:
$ git push remote refspec
The remote
you named is the typical one, origin
; this lets your git find the URL for their git. That part is all good so far.
The problem occurs with the refspec
you gave, master
.
You might wonder: what the heck is a "refspec" anyway? The answer is that it's a two—or sometimes three—part thing, with two branch1 names separated by a colon:
ours:theirs
for instance.2
"But wait," you might say, "there's no :
in master
, there's only one branch name!" Which is true, but git push
needs two, so it makes up the other one: when you give one name, it duplicates the one you give it, so that master
just means master:master
.
This tells git push
to push your current master
and, on the other side, ask their git to update their master
too.
If you want to push your current commit (HEAD
) and have their git put that in as their master
, you must3 write that out:
git push origin HEAD:master
It may help to realize that when you git push
something, their side doesn't care (or even know) anything about any branches on your side: your git turns your branches into raw SHA-1 values, and sends those—the raw SHA-1s—to the remote. If your HEAD
maps to commit-ID 1234567, for instance, git push origin HEAD:master
has your git call theirs up and deliver commit 1234567, then send them a message: "now that I've given you commit 1234567, please make your master
point to that commit-ID." Their end then says "OK" (push succeeded) or "no, I refuse" (push failed, e.g., not a fast-forward, or permission denied by a script like gitolite, or whatever). So any branch names you use that refer to your side—the left hand side of localname:remotename
—are handled entirely on your end, and only the right-hand-side name is passed on to the remote git. That's why you can use HEAD:master
.
1Actually, any valid reference can go here. Tags are references that start with refs/tags/
, and git now has "notes" and such; all of these use references. But branches are the ones most people work with most of the time.
2The optional third part is a leading plus sign, for forcing updates, a la git push -f
. Also, note that git fetch
uses refspecs too, but in this case the names are reversed: theirs:ours
, and you would normally fetch their master
into your remotes/origin/master
, for instance (with forced update, hence with a leading +
).
3As usual with git, there's a configuration knob you can set to change the defaults. In this case there are multiple settings, but the main one to consider is push.default
. See the git config
documentation for details.