Git - What is "Refspec"

user4832990 picture user4832990 · Jun 2, 2017 · Viewed 26.7k times · Source

I've been following this guide on configuring GitLab continuous integration with Jenkins.

As part of the process, it is necessary to set the respec as follows

+refs/heads/*:refs/remotes/origin/* +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*

Why this is necessary is not explained in the post, so I began looking online for an explanation and looked at the official documentation as well as some related stack overflow questions like this one.

In spite of this, I'm still confused -

What exactly is refspec?

And why is the above refspec necessary - what does it do?

Answer

Mark Adelsberger picture Mark Adelsberger · Jun 2, 2017

A refspec tells git how to map references from a remote to the local repo.

The value you listed was +refs/heads/*:refs/remotes/origin/* +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*; so let's break that down.

You have two patterns with a space between them; this just means you're giving multiple rules. (The pro git book refers to this as two refspecs; which is probably technically more correct. However, you just about always have the ability to list multiple refspecs if you need to, so in day to day life it likely makes little difference.)

The first pattern, then, is +refs/heads/*:refs/remotes/origin/* which has three parts:

  1. The + means to apply the rule without failure even if doing so would move a target ref in a non-fast-forward manner. I'll come back to that.
  2. The part before the : (but after the + if there is one) is the "source" pattern. That's refs/heads/*, meaning this rule applies to any remote reference under refs/heads (meaning, branches).
  3. The part after the : is the "destination" pattern. That's refs/remotes/origin/*.

So if the origin has a branch master, represented as refs/heads/master, this will create a remote branch reference origin/master represented as refs/remotes/origin/master. And so on for any branch name (*).

So back to that +... suppose the origin has

A --- B <--(master)

You fetch and, applying that refspec you get

A --- B <--(origin/master)

(If you applied typical tracking rules and did a pull you also have master pointed at B.)

A --- B <--(origin/master)(master)

Now some things happen on the remote. Someone maybe did a reset that erased B, then committed C, then forced a push. So the remote says

A --- C <--(master)

When you fetch, you get

A --- B
 \
  C

and git must decide whether to allow the move of origin/master from B to C. By default it wouldn't allow this because it's not a fast-forward (it would tell you it rejected the pull for that ref), but because the rule starts with + it will accept it.

A --- B <--(master)
 \
  C <--(origin/master)

(A pull will in this case result in a merge commit.)

The second pattern is similar, but for merge-requests refs (which I assume is related to your server's implementation of PR's; I'm not familiar with it).

More about refspecs: https://git-scm.com/book/en/v2/Git-Internals-The-Refspec