|
| 1 | +--- |
| 2 | +type: post |
| 3 | +categories: code |
| 4 | +tags: [ code, python, tip, git ] |
| 5 | +title: "A tip for git, discover multiple URL remote" |
| 6 | +date: 2016-12-30T20:28:42+02:00 |
| 7 | +summary: "While I was developing git-repo I stumbled upon a little known feature that |
| 8 | +has been introduced in git 2.7: the possibility to have a remote with multiple |
| 9 | +URLs. After the break I'll tell you what this is about…" |
| 10 | +lang: english |
| 11 | +logo: /img/gitlogo-black.png |
| 12 | +header_background: /img/gitdark-bg.png |
| 13 | +tweet: 814917694436868097 |
| 14 | +aliases: /code/on/git:_how_to_setup_one_url_with_multiple_remotes |
| 15 | +--- |
| 16 | + |
| 17 | +While I was developing [git-repo] I stumbled upon a little known feature that |
| 18 | +has been introduced in git 2.7: the possibility to have a remote with multiple |
| 19 | +URLs. |
| 20 | + |
| 21 | +# Why would I need that? |
| 22 | + |
| 23 | +What's great with git is that it's a decentralized version control system. What that |
| 24 | +means is that, even if most of the time you'll work with only one remote repository (in a |
| 25 | +very centralised way), sometimes you'll be happy to be able to have many remotes to |
| 26 | +work with. |
| 27 | + |
| 28 | +An use case I had in a former work setup was to have an internal repository (that will |
| 29 | +feature the open code, as well as some custom features in branches for our paying customers), |
| 30 | +an external repository (because opensource is ♥), a production repository (for deployment |
| 31 | +purposes, but it's not anymore a use case thanks to CI integrations that [does automatic |
| 32 | +deployment](https://about.gitlab.com/gitlab-ci/)). |
| 33 | + |
| 34 | +Another use case, which was the one I ran into when developing [git-repo], is working |
| 35 | +with several repository services at once. You've got gitlab, github, bitbucket, gogs… |
| 36 | +And when you're working with all of them you got to create a remote for each, and |
| 37 | +push your changesets to each of them everytime: |
| 38 | + |
| 39 | +``` bash |
| 40 | +git remote add github https://github.com/guyzmo/git-repo |
| 41 | +git remote add gitlab https://gitlab.com/guyzmo/git-repo |
| 42 | +git remote add bitbucket https://bitbucket.org/guyzmo/git-repo |
| 43 | +git remote add home https://gitlab.myserver.com/guyzmo/git-repo |
| 44 | +git remote add gogs https://gogs.myserver.com/guyzmo/git-repo |
| 45 | +``` |
| 46 | + |
| 47 | +it's a bit boring and redundant to do so, but at least that's a task you do only |
| 48 | +once (and you can do it faster with [a great editor](http://vim.org) on the `.git/config` file). |
| 49 | + |
| 50 | +But each time you want to push to your projects, you got to push to each of them: |
| 51 | + |
| 52 | +``` bash |
| 53 | +git push github master |
| 54 | +git push gitlab master |
| 55 | +git push bitbucket master |
| 56 | +git push home master |
| 57 | +git push gogs master |
| 58 | +``` |
| 59 | + |
| 60 | +And as you're as lazy as anybody, you'll end up only pushing to one, maybe two |
| 61 | +of the remotes, usually `github` and `home`… |
| 62 | + |
| 63 | +# The multiple URL remote comes to help |
| 64 | + |
| 65 | +…But you don't really have to! You can work with multiple remotes nicely, by creating |
| 66 | +a new remote that contains all the other remotes URLs: |
| 67 | + |
| 68 | +``` bash |
| 69 | +git remote add all https://github.com/guyzmo/git-repo |
| 70 | +git remote add all set-url --add https://gitlab.com/guyzmo/git-repo |
| 71 | +git remote add all set-url --add https://bitbucket.org/guyzmo/git-repo |
| 72 | +git remote add all set-url --add https://gitlab.myserver.com/guyzmo/git-repo |
| 73 | +git remote add all set-url --add https://gogs.myserver.com/guyzmo/git-repo |
| 74 | +``` |
| 75 | + |
| 76 | +then, when it's time to push your code, you just need to run: |
| 77 | + |
| 78 | +``` bash |
| 79 | +git push all master |
| 80 | +``` |
| 81 | + |
| 82 | +The only drawback is that it makes little sense to pull from a remote with multiple URL, |
| 83 | +and when looking at your index, you'll see that the `all/master` ref is updated, but |
| 84 | +not the other refs (`github/master`, `gitlab/master`…). So if you want to keep all the |
| 85 | +remotes updated you've got to run: |
| 86 | + |
| 87 | +``` bash |
| 88 | +git fetch --all |
| 89 | +``` |
| 90 | + |
| 91 | +which will sync back all the refs that have been updated when you pushed through the all |
| 92 | +remote. So instead of 4/5 or more commands to repeat, you end up with only two commands. |
| 93 | + |
| 94 | +And you can make it simpler using an alias such as: |
| 95 | + |
| 96 | +``` bash |
| 97 | +git config alias.pushall "!pushall() { git push all $* && git fetch --all ; }; pushall" |
| 98 | +``` |
| 99 | + |
| 100 | +# And how is it being used in [git-repo]? |
| 101 | + |
| 102 | +When I worked on [git-repo], I figured that's a great feature to take advantage of. So |
| 103 | +I've implemented a customized version of the `git remote add`, that's being used under |
| 104 | +the hood by `git <target> create`, `git <target> clone`, git <target> fork`… |
| 105 | + |
| 106 | +This is the `git <target> add` command, and it works as follows: |
| 107 | + |
| 108 | +``` bash |
| 109 | +git hub add <user>/<repo> [<name>] [--tracking=<branch>] [-a] |
| 110 | +``` |
| 111 | + |
| 112 | +there you can add to the list of remotes the repository as identified by the repo slug |
| 113 | +(`<user>/<repo>`, a full URL to the repo works as well, as long as it matches the targetted |
| 114 | +service provider). Then you can give a custom name to the repo and specify the branch you |
| 115 | +want to track (with `--tracking`). The `-a/--alone` parameter is to prevent from adding |
| 116 | +the URL to the `all` remote. |
| 117 | + |
| 118 | +``` bash |
| 119 | +% git hub add foobar/project foobar --tracking=devel |
| 120 | +Successfully added `foobar/project` as remote named `foobar` |
| 121 | +% git remote |
| 122 | +all |
| 123 | +foobar |
| 124 | +github |
| 125 | +% git remote get-url --all all |
| 126 | +git@github.com:guyzmo/clait |
| 127 | +git@github.com:foobar/project |
| 128 | +``` |
| 129 | + |
| 130 | +As a nice to have idea I'd like to implement would be to make the `add` command so it is possible |
| 131 | +to handle `remote add` with just any repository, something that's like: |
| 132 | + |
| 133 | +``` bash |
| 134 | +% git <target> add user@10.0.0.1:./repository.git local_machine --tracking=devel |
| 135 | +Successfully added `user@10.0.0.1:./repository.git` as remote named `local_machine` |
| 136 | +``` |
| 137 | + |
| 138 | +At the time of writing this article, the feature [has been implemented](https://github.com/guyzmo/git-repo/compare/devel...features/repo_add_cmd?expand=1), |
| 139 | +and will be merged to one of the future versions of [git-repo]. |
| 140 | + |
| 141 | +[git-repo]:https://github.com/guyzmo/git-repo |
0 commit comments