Set gitlab repository to have more than 1 remote

145 Views Asked by At

I am using a repository in Gitlab. When I clone the repository, it has its usual remote origin which points to Gitlab. However, I also need to push the commits to a second repo and for that reason I set a second remote as soon as I clone the repo:

git remote add second <url_of_secondary_repo>

My question is, is there a way to store that second remote in the git config of the Gitlab repo so that I do not need to add it every time I clone the repo, but instead as soon as I clone it and query with git remote -v, I get e.g.

origin  [email protected]:<user>/<repo>.git (fetch)
origin  [email protected]:<user>/<repo>.git (push)
second  <url_of_secondary_repo> (fetch)
second  <url_of_secondary_repo> (push)
2

There are 2 best solutions below

0
Vivek Tikar On

I don't see a way to commit and share git config across team, Although there is a workaround to share it through git hooks but I don't think it's worth it given your use case. Although I am still jotting it down incase.

In GIT, As soon as a repository is cloned post-checkout hook gets called. You can put a shell command (git remote add second <url_of_secondary_repo>) in this hook file (.git/hooks/post-checkout). Git doesn't allow you to commit the hooks, But there are plenty of workaround available on internet on how to share the git hooks across your team. Now as soon as your team mate clone this repo, post-checkout hook will get executed automatically and second remote will get added to their config.

Please note that post-checkout hook gets called every time you run git checkout command, So you will have to put a check in post-checkout hook to only run add remote command if second remote doesn't exist in config already.

OR

More sophisticated and better way of doing same is -

  1. Add a new file named gitconfig at top level in the repo.
  2. Put below lastly mentioned config in this gitconfig file.
  3. Paste this command git config --local include.path ../gitconfig in .git/hooks/post-checkout file. It's okay if this command get's executed every time post-checkout hook runs.
  4. Make your hooks sharable.
  5. Commit gitconfig

Now once someone clones the repository the post-checkout hook will get executed and second remote will be added automatically, Instead of cloning the repo if they download it then second remote will be added as soon as they run checkout command for first time.

contents of gitconfig file -

[remote "second"]
    url = <url_of_secondary_repo>
    fetch = +refs/heads/*:refs/remotes/second/*```
0
ElpieKay On

Git config values are not tracked, and they are not designed to be transferred between repositories. It would be nice if git clone had options like --extra-remote-name=second --extra-remote-url=<url_of_secondary_repo>. It doesn't so far as I know.

The most straight-forward method I think is to write a script or a function. Here is a very simple example that considers only git clone [email protected]:<user>/<repo>.git.

#!/bin/bash

url=$1
worktree=$(echo $url | awk -F/ '{print $NF}' | awk -F. '{print $1}')

git clone $1
git -C $worktree remote add second <url_of_secondary_repo>

# and an optional config value
git -C $worktree config remote.second.fetch +refs/heads/*:refs/remotes/second/* 

Among them a special case is a custom git command. If the script is named git-foo and it's executable and under a path in $PATH, you can use the command git foo [email protected]:<user>/<repo>.git to invoke it. foo should be different from any of the existing git commands like add, commit, etc. Suppose it's git-mclone and you can use it to clone the repository and add the second remote.

git mclone [email protected]:<user>/<repo>.git

You could also make use of template directory or post-checkout.

The default template directory is /usr/share/git-core/templates and on git-bash it's /mingw64/share/git-core/templates or <git-bash-installation-path>/mingw64/share/git-core/templates. Everything under it will be copied to .git/ of the repository on git init or git clone by default.

Copy the default template directory.

mkdir -p /path/to/foo_templates
cp -r /usr/share/git-core/templates/* /path/to/foo_templates

Add a default config file /path/to/foo_templates/config that has these lines.

[remote "second"]
    url = <url_of_secondary_repo>
    fetch = +refs/heads/*:refs/remotes/second/*

These lines will be in .git/config of the created local repository if you specify the template directory.

git clone --template=/path/to/foo_templates [email protected]:<user>/<repo>.git

The default template directory has a folder hooks with sample hooks under it. You can also add an executable post-checkout in it.

#!/bin/bash

# post-checkout to add the remote "second"
git remote add second <url_of_secondary_repo>
git config remote.second.fetch +refs/heads/*:refs/remotes/second/*

If the template directory is specified in git clone, the hooks will be copied to .git/hooks of the repository. After the default checkout of main or master, .git/hooks/post-checkout is invoked and the second remote is added.

Like the template directory, you can also create a hook directory.

mkdir -p /path/to/foo_hooks
# copy or create the above post-checkout under it

Specify the hook directory in git clone.

# The absolute path to the hook directory is needed here
git clone -c core.hookspath=/path/to/foo_hooks [email protected]:<user>/<repo>.git

This way after the default checkout, /path/to/foo_hooks/post-checkout is invoked and the second remote is added.

If you don't want to use a different git command or type extra options like --template and -c core.hookspath, you can also consider the conditional includes of git config.

There are 2 possible condition keywords, gitdir and hasconfig:remote.*.url:. If we add these condition keywords in system or global config, the config can include another config file that contains remote.second.url and remote.second.fetch.

hasconfig:remote.*.url: looks promising. With this system or global config value,

; NOT ALLOWED AND IT WOULD RAISE AN ERROR
[includeIf "remote.origin.url:[email protected]:<user>/<repo>.git"]
    path = /path/to/foo_templates/config

if the repository has [email protected]:<user>/<repo>.git, remote.second.url and remote.second.fetch can be included from /path/to/foo_templates/config. However, remote.second.url is not allowed.

Files included by this option (directly or indirectly) are not allowed to contain remote URLs.

It would raise an error:

fatal: remote URLs cannot be configured in file directly or indirectly included by includeIf.hasconfig:remote.*.url

Luckily, gitdir allows it, although you'll have to clone the repository under a specified folder.

; include for all repositories inside /path/to/group
[includeIf "gitdir:/path/to/group/"]
    path = /path/to/foo_templates/config