Subject: Re: How to mirror and augment a git repository

From: Sebastian Tennant <[email-masked]>
Date: Wed, 08 Mar 2023 16:54:39 +0000

Hello Jeff,

Thanks for your explanation.  It's been really helpful.

Quoth Jeff King <[email-masked]>
on Mon, 6 Mar 2023 03:08:50 -0500:
> On Sat, Mar 04, 2023 at 12:19:16PM +0000, Sebastian Tennant wrote:
>
>>  Augmented mirror:
>>
>>    $ git clone --mirror <upstream> upstream
>>    $ cd upstream
>>    $ git remote update  # regular cron job
>
> The problem here is that your refspec for "origin" in the mirror
> will be "+refs/*:refs/*". So it claims to have responsibility for
> the whole refs namespace. And because of the "+", it will happily
> overwrite local refs when fetching, including branches pushed up by
> the client. You noticed it most with "prune", because that deletes
> local branches not present in upstream repo. But a similar problem
> would happen if both a client and the upstream had a branch named
> "foo".

Understood.

>> I've tried running the augmented mirror as a plain bare repo, i.e.
>>
>>    $ git config --unset remote.origin.fetch
>>    $ git config --unset remote.origin.mirror
>>
>> but then the cron job (git remote update) is no longer sufficient in
>> making all upstream activity available downstream.
>
> Right. If you drop the fetch refspec entirely, then it will only fetch
> HEAD during your cron jobs, which is not what you want.

Precisely.

> You want a refspec that tells Git to fetch everything, but you need
> to divide up the "refs/" namespace into local stuff and mirrored
> stuff.
>
> You could use the normal "+refs/heads/*:refs/remotes/origin/*" refspec,
> but it's awkward for the clients to access "refs/remotes/" on the
> mirror.

Indeed.  To fetch a known ref, a client (also with the normal fetch
refspec) would have to do something like this, for example:

 $ git fetch origin\
       refs/remotes/origin/<ref>:refs/remotes/upstream/<ref>

Alternatively, they could add an additional fetch refspec to their
config:

[remote="origin"]
 ...
 fetch = +refs/heads/*:refs/remotes/origin/*             # normal
 fetch = +refs/remotes/origin/*:refs/remotes/upstream/*  # additional

This would have the advantage of fetching all the upstream refs on the
next update giving them a better idea of what's happening upstream.

Is my understanding more or less correct?

> So you probably want to keep the upstream branches in "refs/heads/",
> but mark a special part of the namespace. Like:
>
>   cd augmented-mirror
>   git config remote.origin.fetch '+refs/heads/*:refs/heads/upstream/*'
>
> And then "git fetch" will pull all of the remote branches into the
> "upstream/" namespace.

I see.  And creating the 'upstream' namespace under 'heads' (instead
of under 'remotes') is crucial, since any client (with a normal fetch
refspec) will then receive those refs automatically, i.e. without the
additional fetch refspec I've described above.

> And it's safe to prune, because it will only delete branches in
> refs/heads/upstream/ (and you may want to just "git fetch --prune"
> as you fetch via cron, which is a little more efficient than a
> separate "git remote prune").

Understood and noted.

> Clients can name their branches whatever they like, as long as they
> don't start with "upstream/", and that won't interfere with the
> mirror.

Got it.  Thanks again.

Sebastian