Git's terminology here is ... not so great. In particular, the terms remote branch and tracking branch are not defined at all. See the gitglossary for what is defined.
People do, however, sometimes use the phrase remote branch to mean either remote-tracking branch name or the result of examining branch names on the remote. People—and the Git book—sometimes use the phrase tracking branch to mean a branch that has an upstream set.
Your definition of remote-tracking branch matches that in the Gitglossary. I dislike this term, as it leads to people dropping the adjective tracking and calling it a remote branch, which different people interpret differently. (I prefer to call this a remote-tracking name, although this isn't really much of an improvement. The main improvement, if there is one, lies not using the word "branch" again. :-) )
A more general term that does not suffer from all the above is ref (which is short for reference; I tend to spell it out the long way most of the time). If you look at the Git glossary, you'll see that this one is defined as:
A name that begins with refs/
(e.g. refs/heads/master
) that points to an object name or another ref (the latter is called a symbolic ref). For convenience, a ref can sometimes be abbreviated when used as an argument to a Git command; see gitrevisions[7] for details. Refs are stored in the repository.
In any case: yes, you can produce arbitrary modifications of names as you're suggesting. The fetch =
line(s) in your .git/config
determine how each ref gets modified.
When you run git fetch
, the first step of the process is that your Git calls up some other Git. The URL by which your Git reaches that Git comes from:
- the command line, if you run
git fetch https://github.com/owner/repo.git
for instance, or
- the stored URL under the name of a remote, in this case the one stored under
hehe_server
since you used git fetch hehe_server
.
(There are several other ways to specify repository URLs as there was a lot of history that happened before remotes were invented. These are the two common methods.)
Having made this connection, the other Git then spills out all its refs.1 You can observe this for yourself using git ls-remote
:
git ls-remote hehe_server
The output of this command is the set of refs, and hash IDs, that your Git sees when their Git presents them.
In any case, your Git can now take these refs and operate on them as instructed by the fetch =
settings. Each setting consists of a refspec. Refspecs are described in the git fetch
documentation, but mostly they consist of:
- an optional leading
+
character meaning force;
- a source name or pattern;
- a colon character
:
; and
- a destination name or pattern.
The source name or pattern is matched against the names that the other Git presents. The resulting destination name or pattern is used to construct the name that your Git will create or update (or, with --prune
, delete if no input name matches it).
There are some odd constraints here. In particular, if you set up multiple source names that match to a single destination, or a single source that matches to multiple destinations, it doesn't work. For instance:
+refs/heads/master:refs/remotes/hehe_server/foo
+refs/heads/master:refs/remotes/hehe_server/bar
causes one source, master
, to map to two outputs, and:
[remote "hehe_server"]
fetch = +refs/heads/master:refs/remotes/hehe_server/foo
fetch = +refs/heads/develop:refs/remotes/hehe_server/foo
causes two sources, master
and develop
, to map to a single output. Neither can be handled usefully.
I want to rename hehe_server/master_remote@local
to hehe_server/master_haha@local
, while keeping everything else the same. Can I do that?
Sort of yes, but mostly no. In particular, if you want to take their refs/heads/master
and call it refs/remotes/hehe_server/master_haha
, that part is easy:
fetch = +refs/heads/master:refs/remotes/hehe_server/master_haha
does the trick. But if you now want to take all the remaining names and handle them in the usual way:
fetch = +refs/heads/*:refs/remotes/origin/*
you have told Git that refs/heads/master
should become two names locally, because the second line maps the name to refs/remotes/origin/master
.
What this means is that to get this to work, you must:
- contact the other Git
- get a complete list of all of their branch names
- write a new set of
remote.hehe_server.fetch
lines, one line per branch name, with the mapping you desire: everything except their master
maps as usual, and their master
maps weirdly.
Every time the set of branch names on their server changes, you must repeat this process.
1A new wire protocol allows server-side filtering of refs. Without this filtering, a repository with many tags and/or branches may spew multiple megabytes of unwanted data at your Git before your Git can get to having a useful conversation with it. But this—emitting all the refs—is what happens with the old protocol.