diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -5623,11 +5623,11 @@ def purge(ui, repo, *dirs, **opts):
         ),
     ]
     + remoteopts,
-    _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'),
+    _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
     helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
     helpbasic=True,
 )
-def push(ui, repo, dest=None, **opts):
+def push(ui, repo, *dests, **opts):
     """push changes to the specified destination
 
     Push changesets from the local repository to the specified
@@ -5663,6 +5663,9 @@ def push(ui, repo, dest=None, **opts):
     Please see :hg:`help urls` for important details about ``ssh://``
     URLs. If DESTINATION is omitted, a default path will be used.
 
+    When passed multiple destinations, push will process them one after the
+    other, but stop should an error occur.
+
     .. container:: verbose
 
         The --pushvars option sends strings to the server that become
@@ -5706,7 +5709,12 @@ def push(ui, repo, dest=None, **opts):
                 # if we try to push a deleted bookmark, translate it to null
                 # this lets simultaneous -r, -b options continue working
                 opts.setdefault(b'rev', []).append(b"null")
-    if True:
+
+    if not dests:
+        dests = [None]
+    some_pushed = False
+    result = 0
+    for dest in dests:
         path = ui.getpath(dest, default=(b'default-push', b'default'))
         if not path:
             raise error.ConfigError(
@@ -5753,9 +5761,9 @@ def push(ui, repo, dest=None, **opts):
                 c = repo[b'.']
                 subs = c.substate  # only repos that are committed
                 for s in sorted(subs):
-                    result = c.sub(s).push(opts)
-                    if result == 0:
-                        return not result
+                    sub_result = c.sub(s).push(opts)
+                    if sub_result == 0:
+                        return 1
             finally:
                 del repo._subtoppath
 
@@ -5775,15 +5783,24 @@ def push(ui, repo, dest=None, **opts):
                 opargs=opargs,
             )
 
-            result = not pushop.cgresult
+            if pushop.cgresult == 0:
+                result = 1
+            elif pushop.cgresult is not None:
+                some_pushed = True
 
             if pushop.bkresult is not None:
                 if pushop.bkresult == 2:
                     result = 2
                 elif not result and pushop.bkresult:
                     result = 2
+
+            if result:
+                break
+
         finally:
             other.close()
+    if result == 0 and not some_pushed:
+        result = 1
     return result
 
 
diff --git a/tests/test-exchange-multi-source.t b/tests/test-exchange-multi-source.t
--- a/tests/test-exchange-multi-source.t
+++ b/tests/test-exchange-multi-source.t
@@ -71,6 +71,9 @@ Various other repositories
 Test simple bare operation
 ==========================
 
+pull
+----
+
   $ hg clone main-repo test-repo-bare --rev 0 -U
   adding changesets
   adding manifests
@@ -121,9 +124,90 @@ Test simple bare operation
   o  A 0
   
 
+push
+----
+
+  $ cp -R ./branch-E ./branch-E-push
+  $ cp -R ./branch-G ./branch-G-push
+  $ cp -R ./branch-H ./branch-H-push
+  $ hg push --force -R test-repo-bare ./branch-E-push ./branch-G-push ./branch-H-push
+  pushing to ./branch-E-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 3 changesets with 3 changes to 3 files (+2 heads)
+  pushing to ./branch-G-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files (+2 heads)
+  pushing to ./branch-H-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 4 changesets with 4 changes to 4 files (+2 heads)
+  $ hg log -R ./branch-E-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 7
+  |
+  | o  E 4
+  | |
+  | o  D 3
+  |/
+  o  C 2
+  |
+  | o  G 6
+  | |
+  | o  F 5
+  |/
+  o  B 1
+  |
+  o  A 0
+  
+  $ hg log -R ./branch-G-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 7
+  |
+  | o  E 6
+  | |
+  | o  D 5
+  |/
+  o  C 4
+  |
+  | o  G 3
+  | |
+  | o  F 2
+  |/
+  o  B 1
+  |
+  o  A 0
+  
+  $ hg log -R ./branch-H-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  G 7
+  |
+  o  F 6
+  |
+  | o  E 5
+  | |
+  | o  D 4
+  | |
+  | | o  H 3
+  | |/
+  | o  C 2
+  |/
+  o  B 1
+  |
+  o  A 0
+  
+  $ rm -rf ./*-push
+
 Test operation with a target
 ============================
 
+pull
+----
+
   $ hg clone main-repo test-repo-rev --rev 0 -U
   adding changesets
   adding manifests
@@ -199,6 +283,125 @@ different repositories.
   o  A 0
   
 
+push
+----
+
+We only push a specific branch with --rev
+
+  $ cp -R ./branch-E ./branch-E-push
+  $ cp -R ./branch-G ./branch-G-push
+  $ cp -R ./branch-H ./branch-H-push
+  $ hg push --force -R test-repo-bare ./branch-E-push ./branch-G-push ./branch-H-push --rev default
+  pushing to ./branch-E-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  pushing to ./branch-G-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files (+1 heads)
+  pushing to ./branch-H-push
+  searching for changes
+  no changes found
+  $ hg log -R ./branch-E-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 5
+  |
+  | o  E 4
+  | |
+  | o  D 3
+  |/
+  o  C 2
+  |
+  o  B 1
+  |
+  o  A 0
+  
+  $ hg log -R ./branch-G-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 5
+  |
+  o  C 4
+  |
+  | o  G 3
+  | |
+  | o  F 2
+  |/
+  o  B 1
+  |
+  o  A 0
+  
+  $ hg log -R ./branch-H-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 3
+  |
+  o  C 2
+  |
+  o  B 1
+  |
+  o  A 0
+  
+  $ rm -rf ./*-push
+
+Same push, but the first one is a no-op
+
+  $ cp -R ./branch-E ./branch-E-push
+  $ cp -R ./branch-G ./branch-G-push
+  $ cp -R ./branch-H ./branch-H-push
+  $ hg push --force -R test-repo-bare ./branch-G-push ./branch-H-push ./branch-E-push --rev default
+  pushing to ./branch-G-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 2 changesets with 2 changes to 2 files (+1 heads)
+  pushing to ./branch-H-push
+  searching for changes
+  no changes found
+  pushing to ./branch-E-push
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+  $ hg log -R ./branch-E-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 5
+  |
+  | o  E 4
+  | |
+  | o  D 3
+  |/
+  o  C 2
+  |
+  o  B 1
+  |
+  o  A 0
+  
+  $ hg log -R ./branch-G-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 5
+  |
+  o  C 4
+  |
+  | o  G 3
+  | |
+  | o  F 2
+  |/
+  o  B 1
+  |
+  o  A 0
+  
+  $ hg log -R ./branch-H-push -T '{desc} {rev}\n' --rev 'sort(all(), "topo")' -G
+  o  H 3
+  |
+  o  C 2
+  |
+  o  B 1
+  |
+  o  A 0
+  
+  $ rm -rf ./*-push
+
 
 Test with --update
 ==================