In 41c21f2 (branch.c: Validate tracking branches with refspecs instead of
refs/remotes/*), we changed the rules for what is considered a valid tracking
branch (a.k.a. upstream branch). We now use the configured remotes and their
refspecs to determine whether a proposed tracking branch is in fact within
the domain of a remote, and we then use that information to deduce the
upstream configuration (branch.<name>.remote and branch.<name>.merge).

However, with that change, we also check that - in addition to a matching
refspec - the result of mapping the tracking branch through that refspec
(i.e. the corresponding ref name in the remote repo) happens to start with
"refs/heads/". In other words, we require that a tracking branch refers to
a _branch_ in the remote repo.

Now, consider that you are e.g. setting up an automated building/testing
infrastructure for a group of similar "source" repositories. The build/test
infrastructure consists of a central scheduler, and a number of build/test
"slave" machines that perform the actual build/test work. The scheduler
monitors the group of similar repos for changes (e.g. with a periodic
"git fetch"), and triggers builds/tests to be run on one or more slaves.
Graphically the changes flow between the repos like this:

  Source #1 -------v          ----> Slave #1
  Source #2 -----> Scheduler -----> Slave #2
  Source #3 -------^          ----> Slave #3

        ...                           ...

The scheduler maintains a single Git repo with each of the source repos set
up as distinct remotes. The slaves also need access to all the changes from
all of the source repos, so they pull from the scheduler repo, but using the
following custom refspec:

  remote.origin.fetch = "+refs/remotes/*:refs/remotes/*"

This makes all of the scheduler's remote-tracking branches automatically
available as identical remote-tracking branches in each of the slaves.

Now, consider what happens if a slave tries to create a local branch with
one of the remote-tracking branches as upstream:

  git branch local_branch --track refs/remotes/source-1/some_branch

Git now looks at the configured remotes (in this case there is only "origin",
pointing to the scheduler's repo) and sees refs/remotes/source-1/some_branch
matching origin's refspec. Mapping through that refspec we find that the
corresponding remote ref name is "refs/remotes/source-1/some_branch".
However, since this remote ref name does not start with "refs/heads/", we
discard it as a suitable upstream, and the whole command fails.

This patch adds a testcase demonstrating this failure by creating two
source repos ("a" and "b") that are forwarded through a scheduler ("c")
to a slave repo ("d"), that then tries create a local branch with an
upstream. See the next patch in this series for the exciting conclusion
to this story...

Reported-by: Per Cederqvist <>
Signed-off-by: Johan Herland <>

I was not sure where to place this, so I tacked it onto the end of t3200.

Also, I wouldn't mind dropping the whole build/test infrastructure story
from the commit message if the rationale for this kind of setup (and this
patch) can be expressed more concisely.


 t/ | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/t/ b/t/
index 8f6ab8e..4031693 100755
--- a/t/
+++ b/t/
@@ -871,4 +871,38 @@ test_expect_success '--merged catches invalid object 
names' '
        test_must_fail git branch --merged 

+test_expect_failure 'tracking with unexpected .fetch refspec' '
+       git init a &&
+       (
+               cd a &&
+               test_commit a
+       ) &&
+       git init b &&
+       (
+               cd b &&
+               test_commit b
+       ) &&
+       git init c &&
+       (
+               cd c &&
+               test_commit c &&
+               git remote add a ../a &&
+               git remote add b ../b &&
+               git fetch --all
+       ) &&
+       git init d &&
+       (
+               cd d &&
+               git remote add c ../c &&
+               git config remote.c.fetch "+refs/remotes/*:refs/remotes/*" &&
+               git fetch c &&
+               git branch --track local/a/master remotes/a/master &&
+               test "$(git config branch.local/a/master.remote)" = "c" &&
+               test "$(git config branch.local/a/master.merge)" = 
"refs/remotes/a/master" &&
+               git rev-parse --verify a >expect &&
+               git rev-parse --verify local/a/master >actual &&
+               test_cmp expect actual
+       )

To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to
More majordomo info at

Reply via email to