546

I have two remote git repositories. origin and github

I push my branch devel to both repositories.

git push -u origin devel
git push -u github devel

But then, when I do. git push It would only get pushed to github.

Is there anyway I can set up my two remotes, so that I can push changes to both repositories with one command ?

yasith
  • 8,981
  • 7
  • 27
  • 32
  • 1
    See also [Pro Git: 9.5 Git Internals - The Refspec - Pushing Refspecs](http://git-scm.com/book/en/Git-Internals-The-Refspec#Pushing-Refspecs). –  May 23 '14 at 20:20
  • 11
    I did vote to reopen this question. It's is not a duplicate of the one referenced in the close reason. This one asks specifically how to push to multiple repos. While the other question mentions both pull and push, the accepted answer doesn't cover pushing to multiple repos - one could improve it though. – jweyrich Mar 28 '15 at 19:53
  • 10
    My use case specifically needed it to be such that I only push to both repositories, but I only pull from one of them. So if I only had the answer from the question linked, I wouldn't have been able to solve my issue. – yasith Apr 03 '15 at 19:23

2 Answers2

867

In recent versions of Git you can add multiple pushurls for a given remote. Use the following to add two pushurls to your origin:

git remote set-url --add --push origin git://original/repo.git
git remote set-url --add --push origin git://another/repo.git

So when you push to origin, it will push to both repositories.

UPDATE 1: Git 1.8.0.1 and 1.8.1 (and possibly other versions) seem to have a bug that causes --add to replace the original URL the first time you use it, so you need to re-add the original URL using the same command. Doing git remote -v should reveal the current URLs for each remote.

UPDATE 2: Junio C. Hamano, the Git maintainer, explained it's how it was designed. Doing git remote set-url --add --push <remote_name> <url> adds a pushurl for a given remote, which overrides the default URL for pushes. However, you may add multiple pushurls for a given remote, which then allows you to push to multiple remotes using a single git push. You can verify this behavior below:

$ git clone git://original/repo.git
$ git remote -v
origin  git://original/repo.git (fetch)
origin  git://original/repo.git (push)
$ git config -l | grep '^remote\.'
remote.origin.url=git://original/repo.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*

Now, if you want to push to two or more repositories using a single command, you may create a new remote named all (as suggested by @Adam Nelson in comments), or keep using the origin, though the latter name is less descriptive for this purpose. If you still want to use origin, skip the following step, and use origin instead of all in all other steps.

So let's add a new remote called all that we'll reference later when pushing to multiple repositories:

$ git remote add all git://original/repo.git
$ git remote -v
all git://original/repo.git (fetch)               <-- ADDED
all git://original/repo.git (push)                <-- ADDED
origin  git://original/repo.git (fetch)
origin  git://original/repo.git (push)
$ git config -l | grep '^remote\.all'
remote.all.url=git://original/repo.git            <-- ADDED
remote.all.fetch=+refs/heads/*:refs/remotes/all/* <-- ADDED

Then let's add a pushurl to the all remote, pointing to another repository:

$ git remote set-url --add --push all git://another/repo.git
$ git remote -v
all git://original/repo.git (fetch)
all git://another/repo.git (push)                 <-- CHANGED
origin  git://original/repo.git (fetch)
origin  git://original/repo.git (push)
$ git config -l | grep '^remote\.all'
remote.all.url=git://original/repo.git
remote.all.fetch=+refs/heads/*:refs/remotes/all/*
remote.all.pushurl=git://another/repo.git         <-- ADDED

Here git remote -v shows the new pushurl for push, so if you do git push all master, it will push the master branch to git://another/repo.git only. This shows how pushurl overrides the default url (remote.all.url).

Now let's add another pushurl pointing to the original repository:

$ git remote set-url --add --push all git://original/repo.git
$ git remote -v
all git://original/repo.git (fetch)
all git://another/repo.git (push)
all git://original/repo.git (push)                <-- ADDED
origin  git://original/repo.git (fetch)
origin  git://original/repo.git (push)
$ git config -l | grep '^remote\.all'
remote.all.url=git://original/repo.git
remote.all.fetch=+refs/heads/*:refs/remotes/all/*
remote.all.pushurl=git://another/repo.git
remote.all.pushurl=git://original/repo.git        <-- ADDED

You see both pushurls we added are kept. Now a single git push all master will push the master branch to both git://another/repo.git and git://original/repo.git.

IMPORTANT NOTE: If your remotes have distinct rules (hooks) to accept/reject a push, one remote may accept it while the other doesn't. Therefore, if you want them to have the exact same history, you'll need to fix your commits locally to make them acceptable by both remotes and push again, or you might end up in a situation where you can only fix it by rewriting history (using push -f), and that could cause problems for people that have already pulled your previous changes from the repo.

jweyrich
  • 31,198
  • 5
  • 66
  • 97
  • 10
    It seems like `git remote set-url --add --push all git://another/repo.git` might be more sane. That way you can still push to upstream and origin but 'all' is a special remote used for both remotes. – Adam Nelson Nov 11 '14 at 12:42
  • 4
    This is insane. Why would anyone ever want functionality like this? First add the Master, then override the Master with the Backup, then add the Master in again? Crazy-pants. A lesson in not overriding verbs. `--add` means ADD, not REPLACE. – AJB Jan 21 '15 at 20:43
  • 7
    @AJB: It's in fact adding a new pushurl. The thing that confuses us is that by having a pushurl, the default URL is no longer used for pushes - which I sincerely disagree with. – jweyrich Mar 13 '15 at 19:21
  • @AdamNelson oh, I agree! Just updated the answer to include your suggestion. Thank you! :-) – jweyrich Mar 27 '15 at 13:40
  • I have a problem that 2 repos that I am pushing have different proxy settings. So when I have proxy defined for remote, it will fail, because there are now 2 push repos in my single remote. Any suggestions? – Jaanus Feb 17 '16 at 10:14
  • @Jaanus Sure - see http://stackoverflow.com/a/35456245/298054 - Let me know if it doesn't work. – jweyrich Feb 17 '16 at 12:04
  • Awesome guide. I used this to setup 2 push urls as I wanted to push my code to 2 different origins. – hygull Feb 19 '18 at 10:36
  • 2
    I'm running git version 2.15.1 currently, and this didn't work for me until I omitted the `--add` flag. It seemed to then get past any errors, and once a push url was in my config as a result of this, I could use the command above to my heart's content (with `--add` and `--push`) – Phil Gibbins Feb 25 '18 at 19:21
  • 1
    How do i remove one of them again? – BelgoCanadian Aug 13 '18 at 21:30
  • Hi @jweyrich, I tried and set up everything but I still can only push to the first repo. For the second I am getting the error: ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@gitlab.com – Roxana Dec 08 '18 at 09:23
  • @Roxana are you trying to push -f (or —force)? Looks like the 2nd repo has a protected branch, which rejects overwriting history, or existing commits. – jweyrich Dec 08 '18 at 15:28
  • Many thanks @jweyrich, you are right. The 2nd repo had an additional commit. – Roxana Dec 09 '18 at 06:03
  • if i have azure and GitLab's how i can do pull and push code both repo on local single repo ? – Mr Coder Apr 16 '20 at 06:15
  • [@CodeWizard](https://stackoverflow.com/users/1755598/codewizard) your edit to the example seems to be a mistake. The paragraph above it says `you can add multiple pushurls for a given remote`. If you change the remote name, you're adding a push URL to a different remote, while my example was showing how to add multiple push URLs to the same remote. – jweyrich Feb 22 '23 at 17:59
106

To send to both remote with one command, you can create a alias for it:

git config alias.pushall '!git push origin devel && git push github devel'

With this, when you use the command git pushall, it will update both repositories.

William Seiti Mizuta
  • 7,669
  • 3
  • 31
  • 23
  • 4
    Is there anyway to make the branch an argument in that command ? – yasith Jan 12 '13 at 04:21
  • 16
    You can pass using bash function: `git config alias.pushall '!f() { git push origin $1 && git push github $1; }; f'` – William Seiti Mizuta Jan 12 '13 at 04:25
  • 8
    Even though this works, it feels a bit hacky. [This](http://stackoverflow.com/questions/14290113/git-pushing-code-to-two-remotes#answer-14290145) seems like the nicer solution. – Matijs Jul 13 '14 at 21:20
  • 1
    *pros:* easier, a bit more portable; *cons:* does not work with `--tags`, less scalable (say 4 repos) – Alois Mahdal Feb 10 '16 at 18:19
  • 1
    One alias for multiple projects doesn't work well if you push different projects to different places. – cp.engr Feb 19 '16 at 16:15
  • @WilliamSeitiMizuta With your command I get "Syntax error: ";" unexpected". – donquixote Dec 29 '16 at 02:45
  • 1
    I had to manually edit .git/config and write `pushall = "!git push origin $1; git push github $1 #"` in the `[alias]` section. The `#` was to ignore the rest of the line. – donquixote Dec 29 '16 at 03:10
  • this is a much better solution – Kamel Labiad May 31 '20 at 18:37
  • Or just add an alias to your .zshrc or .bashrc with the custom command. Something like direnv is also nice as you can add aliases based on the repo. – fengelhardt Oct 20 '21 at 19:23
  • Example `export GIT_GPA_REMOTES=("origin" "bb"); gpa(){ for i in "${GIT_GPA_REMOTES[@]}"; do; git push $i $@; done };` – fengelhardt Oct 20 '21 at 19:41
  • While I liked the idea of configuring a remote to have multiple pushurl entries, I found that in practice only one remote's tracking branch (call it the the primary) was updated when I pushed to that multi-url remote and I had to `git fetch secondary` for `refs/remotes/secondary/` to be updated, making this approach the only real "one-command push" for me. – Shi Nov 25 '21 at 01:22
  • Update: I was surprised that nobody else had mentioned the above issue, then I encountered [this comment](https://stackoverflow.com/questions/5785549/able-to-push-to-all-git-remotes-with-the-one-command#comment22163951_5785618), which does. – Shi Nov 25 '21 at 01:47