5

I saw the command in .git/config when I learn pull & request from this article.

fetch = +refs/pull/*/head:refs/pull/origin/*

I open my config file which is different as this fetch = +refs/heads/*:refs/remotes/origin/*.

After I modify fetch setting line as the article read and run those git commands:

git fetch origin
git checkout -b 1 pull/origin/1

The config file automatic append this:

[branch "1"]
        remote = origin
        merge = refs/pull/1/head

Could someone can explain the means and difference settings of the fetch = ...?
Why [branch "1"] will be auto appended?

Thanks

3
  • git checkout -b 1 pull/origin/1 has an effect that pull/origin/1 is set as the upstream of 1. The config records this relationship. Commented Dec 14, 2017 at 13:48
  • @ElpieKay, what's the means of pull origin? Specify it's a pull request? Commented Dec 14, 2017 at 14:02
  • Here refs/pull/origin/* is a group of refs used as pull request by Github. The most common "built-in" refs are refs/heads/* as branches, refs/tags/* as tags and refs/remotes/* as remote branches. You could define your own refs. Commented Dec 14, 2017 at 14:31

1 Answer 1

10

There's quite a lot to unpack here. Let's start from the beginning.

The Refspec

The value of the fetch setting is what's called a refspec, a special Git syntax used to map local branch references with remote ones.

It takes the form:

<source>:<destination>

In the case of fetch, <source> is a branch that exists on a remote repository, while <destination> is the branch it should map to in the local repository. So, for example:

fetch: +refs/heads/master:refs/remotes/origin/master

tells Git to map the master branch that exists in the remote repository (the source) to the origin/master branch in the local one (the destination).

The + character is optional and means that Git should update the destination branch even if it would require a merge commit (which shouldn't happen since you normally don't commit directly to the origin/master branch).

Pull Request Branches

Now that you know about the refspec syntax, let's talk about mapping the pull request branches from GitHub.

As you know, every pull request gets assigned a unique number within a given repository. What you may not know, however, is that this number ends up becoming the name of the branch associated to the pull request.

For example, a pull request with number 42 will get a branch reference named:

refs/pull/42

Notice how pull request branches are created in a subdirectory called pull in the repository hosted on GitHub.

If you wanted to map every pull request branch that exists in the remote repository on GitHub to a corresponding local branch with the same name, you would say:

fetch = +refs/pull/*/head:refs/pull/origin/*

where * is a wildcard character that matches any name. With this setting, next time you do git fetch in your local repository, Git will download all branch references that exist in the pull directory on GitHub and create corresponding branches in the local repository under the directory pull/origin.

This means that for our pull request 42, for example, the mapping becomes:

    GitHub        Your Repo
pull/42/head -> pull/origin/42

Remote Branches

Note that the pull/origin/* branches—while they do exist in your local repository—are not meant for you to commit to. They're called remote-tracking branches and are used by Git to literally keep track of a given branch that exist in a remote repository.

The documentation says it best:

Remote-tracking branches are references to the state of remote branches.

More specifically:

Think of them as bookmarks, to remind you where the branches in your remote repositories were the last time you connected to them.

You can't commit to a remote branch directly. If you want to move it forward, you must first create a local branch (i.e. one that exists only in your local repository) and associate it to the remote branch. From that point on, every time you do git pull or git push on that local branch, Git is going to update the corresponding remote tracking branch.

You can do that with a single command:

git checkout -b 42 pull/origin/42

This creates a local branch named 42 and associate it to the remote tracking branch pull/origin/42, which in turn is mapped to the pull/42 branch on GitHub. Here's how it looks like:

 Local      Remote        GitHub
  42 -> pull/origin/42 -> pull/42

The relationship between local branch and remote branch is expressed in the local repository's configuration file as:

[branch "42"]
remote = origin
merge = refs/pull/42/head

The relationship between the remote branch and the branch that's physically on another machine (i.e. on GitHub) is expressed by using the refspec syntax that we looked at in the beginning.

Sign up to request clarification or add additional context in comments.

4 Comments

Upvoted; but I have a few minor items to mention: while Git calls origin/master a remote-tracking branch, I think this is a poor name and have started using remote-tracking name instead (though I may never get anywhere in convincing everyone else to do the same :-) ). Remote-tracking names internally are only those starting with refs/remotes/ so if you name things refs/pull/*, those aren't remote-tracking names either, they're a new namespace entirely. Also, it's worth being careful with branch names like -b 123: when you hit 4 digits, they get ambiguous vs hash IDs.
@torek I like the idea of not calling them branches since you never commit to them directly. Remote-tracking names is fine but I feel name is a bit too generic. Maybe remote-tracking refs? The ones outside the remote namespace could then simply be remote refs. Thanks for the feedback on the branch name – I agree, so I made it shorter ;)
Hi, can I push back to pull/origin/42 remote repository to modify something for my forker?
Yes, you can as long as you have permission from the owner of the fork. You can read more about this in the GitHub documentation.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.