Reputation: 1796
I have been working with Git for a year now and I stumbled upon a confusing thing today while merging the branches. Here is the .config file section on my local :
[branch "develop"]
remote = origin
merge = refs/heads/develop
[branch "Feature/Sprint4.4/mybranch"]
remote = origin
merge = refs/heads/Feature/Sprint4.4/mybranch
Now I am on local branch mybranch. I executed following commands in sequence.
1) git pull
--> 2d6cf1e..655001e develop -> origin/develop
--> already up-to-date
2) git merge origin develop
--> already up-to-date
3) git merge origin/develop
--> merge made by recursive strategy...
....(all the changes were indicated)
7 files changed, 202 insertions(+), 181 deletions(-)
My question is what is the exact difference between command (2) and (3) above and why the outputs changed?
I know I can go through Git docs and other books but looking for an answer simpler language.
Upvotes: 0
Views: 528
Reputation: 489828
I see this is already answered and accepted but I think there's a crucial bit missing from the accepted answer.
git merge
with more than one additional argument.In other words, git merge origin develop
is rarely a good idea; don't do that. To see why, read on.
git pull
and git merge
take different argumentsIt's true (i.e., it's correct) that git pull
simply runs git fetch
followed by git merge
(or, if you instruct it, git pull
followed by git rebase
). The tricky part is how it runs them, and that the pull
command generally takes two arguments:
$ git pull remote refspec
but the merge
command generally takes one:
$ git merge commit
Note that the names here (remote
, refspec
, and commit
) are all different: this is one of the keys to understanding the commands. Also, note the word "generally" when talking about the number of arguments used: it's possible to omit some arguments sometimes, or to use more arguments.
In its simplest form, a remote is just a name like origin
that refers to an entry in your git config file (.git/config
). The pull
script has a long history of alternative ways to use it, so you can put other things here, but as long as you just use these names, things work pretty well and are less confusing, so let's leave it at that.
A complete refspec has up to three parts. All three matter a lot with git push
but much less so with git fetch
, and since git pull
runs git fetch
, we can use the simplest version, which is just a branch or tag name. When using git fetch
(and therefore also git pull
), the branch name you supply is the name of the branch on the remote. Hence:
git pull origin branch
makes your git contact the remote origin
and ask it about its branch named branch
.
Assuming the branch name exists on the remote, your git will obtain the latest version of that branch (and, generally, update refs/remotes/origin/branch
as well, at least if you have git version 1.8.4 or newer—that is, with newer versions of git, your "remote-tracking branches" always get updated).
As a side effect that git pull
relies on, git fetch
also writes all its updated branch information1 to a file called FETCH_HEAD
in git's private (.git
) directory. The only thing you really need to know about this is that the pull
script uses it (but you can read the contents of the file if you like).
Summary so far: git pull
runs git fetch
and stores updates in FETCH_HEAD
, and sometimes also in your "remote-tracking branches" like origin/master
and origin/develop
and so on.
git merge
Now let's move on to git merge
.
I said above that it "generally" takes three arguments, git merge commit
.
The pull
command always gives merge
three arguments (and a lot of flags, including some flags to supply the merge commit message). The pull
script gets the commit argument—and, for that matter, the merge commit message—out of the FETCH_HEAD
file.2
If you wanted to do this manually, you could do it the same way the pull
script does, but extracting and typing in long SHA-1 IDs is painful. And, in any case, what you wanted to do was to merge a different, local branch, not the raw SHA-1 of a commit brought over by a fetch
step.
Here's where you went wrong initially, when you entered the command:
git merge origin develop
This passes two arguments to merge
. They are both taken as commit IDs and this tells git merge
to do something called an "octopus merge". Unless you're an advanced git master, you probably don't want to do that. (Luckily, in your case, there was nothing to do anyway, given the multiple commit IDs you gave to git.)
I will note here that origin
does not look like a commit-ID, and develop
doesn't resemble an SHA-1 either. But in fact, both of these names do name specific commits—if they didn't, you would have gotten an error message from your git merge
command. To find out which commits, you can use the command git rev-parse
:
$ git rev-parse origin
a17c56c056d5fea0843b429132904c429a900229
(yours won't match this SHA-1, but the idea should be clear enough). The branch name also resolves to a raw SHA-1—I don't have a develop
but I do have master
and origin/master
so I will use those here:
$ git rev-parse master
a17c56c056d5fea0843b429132904c429a900229
$ git rev-parse origin/master
a17c56c056d5fea0843b429132904c429a900229
(in this case, the two are the same as my master
is now up to date with origin/master
).
In general, a branch name resolves to the commit at the tip of the branch.
I'll repeat that, because it's another key to understanding git. A branch name resolves to the commit at the tip of the branch, with one very big and very important exception, which is when doing git checkout
. Since git checkout
is one of the first things people do with git, they learn that branch names are special—which is true for git checkout
, but not for most other git commands. Most commands just turn the name into a raw SHA-1, and when they do, they get the tip of the branch.
git merge origin/develop
Running git merge
with a symbolic name, like origin/develop
, is quite reasonable: git will look up the raw commit ID for you, and will set up the default merge message to use the symbolic name, which will mean more to a human reading the log later. If you use the raw commit ID you'll get that in the log.
Summary for merge: git merge origin/develop
is a reasonable thing to do. It means "find the tip commit of origin/develop
and merge that, as a regular merge." However, git merge origin develop
is not a good idea as it means "find the tip commits of origin
and develop
and do an octopus merge of those two commits."
1When you use the four-argument form, git fetch remote refspec
, the fetch
step brings over only the one branch you ask for. With three arguments, i.e., git fetch remote
, the fetch
step normally brings over all branches. With just two arguments, git fetch
generally fetches from origin
automatically as needed, and again brings over all branches. Therefore, you can generally just run git fetch
.
2If you examine the file you'll see something like this:
a17c56c056d5fea0843b429132904c429a900229 branch 'master' of [url]
You might also see more lines, e.g.:
ca00f80b58d679e59fc271650f68cd25cdb72b09 not-for-merge branch 'maint' of [url]
(or you might not)—this is all the stuff git fetch
brought over. In this particular case, I ran git pull
with no arguments and it brought over master
and maint
(and a few more items). All the commit IDs wind up in FETCH_HEAD
and then the pull
script extracts the one that is "for merge" by excluding all the ones marked "not-for-merge".
Upvotes: 1
Reputation: 4202
You are on mybranch
1) git pull // This will pull changes from origin and write them into your my branch.(It will fetch for your develop branch)
--> 2d6cf1e..655001e develop -> origin/develop
--> already up-to-date
2) git merge origin develop // This will take almost no effect as you already pulled as it shows develop -> origin/develop
// This is same as git merge origin/develop develop
--> already up-to-date
3) git merge origin/develop // This will merge changes from origin/develop to mybranch
// This is same as git merge origin/develop mybranch
--> merge made by recursive strategy...
....(all the changes were indicated)
7 files changed, 202 insertions(+), 181 deletions(-)
Upvotes: 1