push: add --new-branch option to allow intial push of new branches
authorSune Foldager <cryo@cyanite.org>
Fri, 21 May 2010 15:22:29 +0200
changeset 11211 e43c23d189a5
parent 11210 0c0088881562
child 11212 4d8db9676171
push: add --new-branch option to allow intial push of new branches Compare this to --force which allows anything to be pushed. With --new-branch, only changesets to named branches not present on the and changesets not introducing additional heads on existing branches are allowed. Developed by Henrik Stuart <henrik.stuart@edlund.dk> Sune Foldager <cryo@cyanite.org>
hgext/mq.py
mercurial/commands.py
mercurial/localrepo.py
tests/test-debugcomplete.out
tests/test-push-warn
tests/test-push-warn.out
--- a/hgext/mq.py	Fri May 21 22:53:57 2010 +0900
+++ b/hgext/mq.py	Fri May 21 15:22:29 2010 +0200
@@ -2555,10 +2555,10 @@
             return super(mqrepo, self).commit(text, user, date, match, force,
                                               editor, extra)
 
-        def push(self, remote, force=False, revs=None):
+        def push(self, remote, force=False, revs=None, newbranch=False):
             if self.mq.applied and not force and not revs:
                 raise util.Abort(_('source has mq patches applied'))
-            return super(mqrepo, self).push(remote, force, revs)
+            return super(mqrepo, self).push(remote, force, revs, newbranch)
 
         def _findtags(self):
             '''augment tags from base class with patch tags'''
--- a/mercurial/commands.py	Fri May 21 22:53:57 2010 +0900
+++ b/mercurial/commands.py	Fri May 21 15:22:29 2010 +0200
@@ -2564,7 +2564,8 @@
         if not c.sub(s).push(opts.get('force')):
             return False
 
-    r = repo.push(other, opts.get('force'), revs=revs)
+    r = repo.push(other, opts.get('force'), revs=revs,
+                  newbranch=opts.get('new_branch'))
     return r == 0
 
 def recover(ui, repo):
@@ -3948,6 +3949,7 @@
            _('a changeset intended to be included in the destination')),
           ('b', 'branch', [],
            _('a specific branch you would like to push')),
+          ('', 'new-branch', False, _('allow pushing a new branch')),
          ] + remoteopts,
          _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
     "recover": (recover, []),
--- a/mercurial/localrepo.py	Fri May 21 22:53:57 2010 +0900
+++ b/mercurial/localrepo.py	Fri May 21 15:22:29 2010 +0200
@@ -1507,7 +1507,7 @@
         finally:
             lock.release()
 
-    def push(self, remote, force=False, revs=None):
+    def push(self, remote, force=False, revs=None, newbranch=False):
         '''Push outgoing changesets (limited by revs) from the current
         repository to remote. Return an integer:
           - 0 means HTTP error *or* nothing to push
@@ -1524,10 +1524,10 @@
         # servers, http servers).
 
         if remote.capable('unbundle'):
-            return self.push_unbundle(remote, force, revs)
-        return self.push_addchangegroup(remote, force, revs)
+            return self.push_unbundle(remote, force, revs, newbranch)
+        return self.push_addchangegroup(remote, force, revs, newbranch)
 
-    def prepush(self, remote, force, revs):
+    def prepush(self, remote, force, revs, newbranch):
         '''Analyze the local and remote repositories and determine which
         changesets need to be pushed to the remote. Return value depends
         on circumstances:
@@ -1586,13 +1586,15 @@
                 # 2. Check for new branches on the remote.
                 remotemap = remote.branchmap()
                 newbranches = branches - set(remotemap)
-                if newbranches: # new branch requires --force
+                if newbranches and not newbranch: # new branch requires --new-branch
                     branchnames = ', '.join("%s" % b for b in newbranches)
                     self.ui.warn(_("abort: push creates "
                                    "new remote branches: %s!\n")
                                  % branchnames)
-                    self.ui.status(_("(use 'hg push -f' to force)\n"))
+                    self.ui.status(_("(use 'hg push --new-branch' to create new "
+                                     "remote branches)\n"))
                     return None, 0
+                branches.difference_update(newbranches)
 
                 # 3. Construct the initial oldmap and newmap dicts.
                 # They contain information about the remote heads before and
@@ -1654,14 +1656,14 @@
             cg = self.changegroupsubset(update, revs, 'push')
         return cg, remote_heads
 
-    def push_addchangegroup(self, remote, force, revs):
+    def push_addchangegroup(self, remote, force, revs, newbranch):
         '''Push a changegroup by locking the remote and sending the
         addchangegroup command to it. Used for local and old SSH repos.
         Return an integer: see push().
         '''
         lock = remote.lock()
         try:
-            ret = self.prepush(remote, force, revs)
+            ret = self.prepush(remote, force, revs, newbranch)
             if ret[0] is not None:
                 cg, remote_heads = ret
                 # here, we return an integer indicating remote head count change
@@ -1672,7 +1674,7 @@
         finally:
             lock.release()
 
-    def push_unbundle(self, remote, force, revs):
+    def push_unbundle(self, remote, force, revs, newbranch):
         '''Push a changegroup by unbundling it on the remote.  Used for new
         SSH and HTTP repos. Return an integer: see push().'''
         # local repo finds heads on server, finds out what revs it
@@ -1680,7 +1682,7 @@
         # different heads (someone else won commit/push race), server
         # aborts.
 
-        ret = self.prepush(remote, force, revs)
+        ret = self.prepush(remote, force, revs, newbranch)
         if ret[0] is not None:
             cg, remote_heads = ret
             if force:
--- a/tests/test-debugcomplete.out	Fri May 21 22:53:57 2010 +0900
+++ b/tests/test-debugcomplete.out	Fri May 21 15:22:29 2010 +0200
@@ -174,7 +174,7 @@
 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, style, template, include, exclude
 merge: force, rev, preview
 pull: update, force, rev, branch, ssh, remotecmd
-push: force, rev, branch, ssh, remotecmd
+push: force, rev, branch, new-branch, ssh, remotecmd
 remove: after, force, include, exclude
 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, templates, style, ipv6, certificate
 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude
--- a/tests/test-push-warn	Fri May 21 22:53:57 2010 +0900
+++ b/tests/test-push-warn	Fri May 21 15:22:29 2010 +0200
@@ -135,6 +135,18 @@
 hg -q ci -d "1000000 0" -m 12
 hg push -r 11 -r 12 ../f; echo $?
 
+echo % failed push of new named branch
+echo 12 > foo
+hg -q ci -d "1000000 0" -m 12a
+hg -q up 11
+echo 13 > foo
+hg -q branch e
+hg -q ci -d "1000000 0" -m 13d
+hg push -r 12 -r 13 ../f; echo $?
+
+echo % using --new-branch to push new named branch
+hg push --new-branch -r 12 -r 13 ../f; echo $?
+
 echo % checking prepush logic does not allow silently pushing multiple new heads
 cd ..
 hg init h
--- a/tests/test-push-warn.out	Fri May 21 22:53:57 2010 +0900
+++ b/tests/test-push-warn.out	Fri May 21 15:22:29 2010 +0200
@@ -89,23 +89,23 @@
 pushing to ../f
 searching for changes
 abort: push creates new remote branches: c!
-(use 'hg push -f' to force)
+(use 'hg push --new-branch' to create new remote branches)
 1
 pushing to ../f
 searching for changes
 abort: push creates new remote branches: c!
-(use 'hg push -f' to force)
+(use 'hg push --new-branch' to create new remote branches)
 1
 % multiple new branches
 pushing to ../f
 searching for changes
 abort: push creates new remote branches: c, d!
-(use 'hg push -f' to force)
+(use 'hg push --new-branch' to create new remote branches)
 1
 pushing to ../f
 searching for changes
 abort: push creates new remote branches: c, d!
-(use 'hg push -f' to force)
+(use 'hg push --new-branch' to create new remote branches)
 1
 % fail on multiple head push
 pushing to ../f
@@ -144,6 +144,20 @@
 adding file changes
 added 2 changesets with 2 changes to 1 files
 0
+% failed push of new named branch
+pushing to ../f
+searching for changes
+abort: push creates new remote branches: e!
+(use 'hg push --new-branch' to create new remote branches)
+1
+% using --new-branch to push new named branch
+pushing to ../f
+searching for changes
+adding changesets
+adding manifests
+adding file changes
+added 1 changesets with 1 changes to 1 files
+0
 % checking prepush logic does not allow silently pushing multiple new heads
 adding init
 adding a
@@ -172,7 +186,7 @@
 pushing to j
 searching for changes
 abort: push creates new remote branches: b!
-(use 'hg push -f' to force)
+(use 'hg push --new-branch' to create new remote branches)
 
 % prepush -r should not allow you to sneak in new heads
 pushing to ../l