Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
546 views
in Technique[技术] by (71.8m points)

git merge - Understanding .git/config's 'remote' and 'branch' sections

Here's the contents of the remote and branch sections of my .git/config file.

[remote "origin"]  
    url = https://[email protected]/EvanAad/bitbucketstationlocations.git  
    fetch = +refs/heads/*:refs/remotes/origin/*  
[branch "master"]  
    remote = origin  
    merge = refs/heads/master

What is the meaning and purpose of the contents of these sections, in particular the fetch and merge subsections? How is this information used by Git to guide its operation?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TL;DR summary

Overall, we're interested in two or three things:

  1. If you run git fetch with no additional arguments, what happens?
  2. If you run git merge or git rebase with no additional arguments, what happens?
  3. If you run git pull with no additional arguments, what happens?

The short answer to question #1 is: Git picks one of your remotes to fetch from, and then fetches from that remote. The remote Git picks is from the remote = name setting under the [branch] section. If there isn't any such setting, Git uses origin.

The answer to question #2 is: Git picks some name to use, as if you had run git merge name or git rebase name. The name is based on the merge = ref setting under the [branch] section—but the way this works is somewhat obscure: if this same section says, for instance, remote = origin and branch = refs/heads/master, the name Git picks to use is not master but rather origin/master. If it says remote = origin and branch = develop, the name Git picks is origin/develop, and so on.

(While this looks very straightforward, the actual mapping inside Git is rather tricky: if the section says remote = . and branch = master, the name is master, not ./master, for instance. If you set up unusual fetch refspecs, even more weirdness can happen. This answer does not cover this last case at all.)

The answer to question #3 is in some ways the easiest: git pull simply runs git fetch first, then—provided that succeeds—one of the other two commands, git merge or git rebase, so you really only have to look at questions 1 and 2.

Long

The merge entry under each branch section is, I think, the least obvious. The Git documentation keeps it a bit obscure. Let's cover the others first.

Settings under a [remote "..."] section

There are many possible settings. In general, you do not have to set any of them with git config directly—almost all of them have wrapper commands to set them in a more "user friendly" manner. That includes both of the settings you see here. It's rare to want to change these, either.

The remote section for each named remote, such as origin, lists the URL for git fetch (and optionally a separate push URL for git push, and other remote.* configuration items as described in the git config documentation). It also has one or more fetch lines which supply the default refspec arguments for git fetch from that remote.

That is, if you run:

git fetch origin

Git will look up remote.origin.url to see where to connect, then connect there, then retrieve references based on all the remote.origin.fetch entries. The default that you see here:

+refs/heads/*:refs/remotes/origin/*

tells Git to copy all branches1 from the remote, renaming them to an origin/-prefixed remote-tracking branch2 in your own repository, so:

git fetch origin

basically fetches everything. (The leading + says that Git should do this regardless of whether the remote-tracking branch update is a fast-forward operation. That is, it's like using --force, but without having to specify --force.)

On the other hand, if you run:

git fetch origin a:b c:d

Git will completely ignore all the fetch = lines, retrieving only references a and c from the remote, writing them to references b and d in your repository. (And since this has neither + nor --force, none of these will be force-updated—though in most cases that makes no difference anyway.)


1, 2 A reference is a generic term that covers both branches and tags (and more things as well). Branch names like master are just short-hand for references that begin with refs/heads/. Remote-tracking branch names like origin/master are just short-hand for references that begin with refs/remotes/. Note that the origin/ part comes from the fetch = line—but for all this to work the way it is supposed to, that line must match the name of the remote in the square brackets.


Settings under a [branch "..."] section

There are many possible settings. In general, you do not have to set any of them with git config directly—almost all of them have wrapper commands to set them in a more "user friendly" manner. That includes both of the settings you see here. It's not that rare to want to change one or both of them, using a command we will see in a moment.

The remote part is pretty clear on its own, though: it means that if you are on branch master and run git fetch without giving a remote name at all, Git should fetch from the remote named origin.

The merge part is the tricky one. It lists the name of a branch as seen on the remote. Note that when we run git fetch origin, we tell our Git to call up another Git, find the other Git's master, and copy that into our repository but call it origin/master. And yet ... this merge line says merge = refs/heads/master. Shouldn't it say: merge = refs/remotes/origin/master?

It probably should—but this setting predates the very invention of remotes in the first place. So it doesn't; instead, it lists the full name of the reference as it appears on the remote.

This setting is what gets used if you run git merge or git rebase without providing a branch name to merge or rebase-upon. Git runs the name through the mappings provided by the fetch = line for the remote, to figure out that it should merge with origin/master, for instance.

This setting is also used by the git pull convenience command, which is effectively3 the same as running git fetch followed by running git merge.

You might want to change one or both of these. For instance, if you create a new local branch feature/tall, it may have no branch.feature/tall.remote and branch.feature/tall.merge settings at all.

Since you just created this branch, there is no origin/feature/tall. The Git over at origin does not have feature/tall yet, so you do not have a copy of it.

Then you git push origin feature/tall:feature/tall to have your Git call up origin's Git and have their Git create that branch, so that you now do have origin/feature/tall. You might want your Git to remember that.

You could run two git config commands, but instead, you can run one higher level wrapper command:

git branch --set-upstream-to=origin/feature/tall feature/tall

This tells your Git to set branch.feature/tall.remote to origin, and branch.feature/tall.merge to refs/heads/feature/tall (that being the name on origin).

You can combine the git push and the git branch --set-upstream-to steps using git push -u, which is even better, but the point here remains: you use a wrapper to get both values set at once, since setting only one value is not that useful.4

The special remote name . means this repository (as opposed to some remote repository). If the [branch "xyzzy"] section says remote = . and branch = refs/heads/whatever, then branch xyzzy has local branch whatever as its upstream, rather than having, e.g., origin/whatever as its upstream.


3This deliberately glosses over a lot of fiddly details.

4Setting just the remote part does affect a future git push, but git merge and git rebase won't be able to do the remote-tracking branch mapping without both entries.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...