Hg push tries to push data to repo without any changes

Matt Mackall mpm at selenic.com
Fri Jan 25 22:33:28 UTC 2013


On Fri, 2013-01-25 at 12:33 +0100, Pierre-Yves David wrote:
> On Wed, Jan 23, 2013 at 01:41:04PM -0600, Matt Mackall wrote:
> > On Tue, 2013-01-22 at 19:40 -0800, Roman Kamyk jr wrote:
> > > Hi,
> > > I have a 'dotfiles' repo with couple subrepos, and have been using this
> > > fine for long time. Recently I migrated to a new machine, installed new
> > > Ubuntu 12.10, cloned the repo fine, but I am not able to push, because hg
> > > tries to push to one of the subrepos - that I didn't change, and I have no
> > > authorization to push on bitbucket.
> > > The problematic repo is https://bitbucket.org/tksoh/hgshelve, even without
> > > any subrepos there is a problem as ilustrated by new clone & push:
> > > 
> > > rkj at rkjunior:~/tmp$ hg clone https://bitbucket.org/tksoh/hgshelve
> > 
> > I've reproduced this here. Please file a bug on the BTS:
> > 
> > http://mercurial.selenic.com/wiki/BugTracker
> > 
> > This repo appears to be in a state of sin:
> > 
> > $ hg debugpushkey https://bitbucket.org/tksoh/hgshelve phases
> > 1155713d3222ebee34b59900df8abaf59cea64a1	1
> > publishing	True
> > 
> > It's simultaneously advertising that some changesets are draft but that
> > all of them are public. The client thus tries to tell it "uh, this thing
> > you think is draft is public now". Both the client and server side are
> > buggy.
> 
> That is the expected behavior. The public server serves a repository also used for local operation.

Let's try a different approach: how is this not a regression?

If Alice has a publishing repo libalice (on Bitbucket!!!) that Bob
decided to use as a subrepo and Alice decides to start using phases, Bob
is going to lose, even though everything in Alice's repo is _supposed to
be seen as public by clients_.

In short, all users of third-party subrepos are now exposed to random,
pointless breakage that they have no control of and they will be forced
to downgrade to a pre-phase Mercurial from a year ago to get back to
work. If that downgrade means they lose _other_ functionality that they
depend on, they are seriously screwed. 

This is deeply, deeply not ok. Let me quote myself from three days ago:

"Breaking people's builds and making them wish they'd never upgraded is
about the worst thing a tool can do."

http://markmail.org/message/hlwxpm63vh32tnkj


We are going to change something here to make this work again for 2.5,
the only question is what. Here are the options I see:

a) fix subrepos not to do empty pushes
b) fix clients and servers to treat published changesets as public, full
stop
c) quietly skip phase push whenever authentication is requested
d) something else

You should note that (a) presents a paradox because you've defined
things such that this isn't actually an empty push any more, which means
that now we can NEVER try to optimize away.

The absurd thing here is that the whole point of "publishing servers"
was to not have this class of issue at all. When something is on a
publishing server, it is supposed to be considered public by all
clients.

Option (c) is obviously both hard and hacky. I'm not even sure any
option (d) exists, because we run into trouble before the server even
sees the pushkey request.


In my view, we should do (b) for 2.5. Breaking the relatively obscure
corner case this failing push is meant to help with is a very small
price to pay in my mind. And we need to do it for both clients and
servers because earlier clients and servers will persist for a while.

Probably something like this:

diff -r 692cbda1eb50 mercurial/localrepo.py
--- a/mercurial/localrepo.py	Wed Jan 23 22:52:55 2013 +0900
+++ b/mercurial/localrepo.py	Fri Jan 25 16:32:26 2013 -0600
@@ -1870,20 +1870,17 @@
                     cheads.extend(c.node() for c in revset)
                 # even when we don't push, exchanging phase data is useful
                 remotephases = remote.listkeys('phases')
-                if not remotephases: # old server or public only repo
+                if not remotephases or remotephases.get('publishing'):
+                    # old server or public only repo
                     phases.advanceboundary(self, phases.public, cheads)
                     # don't push any phase data as there is nothing to push
                 else:
                     ana = phases.analyzeremotephases(self, cheads, remotephases)
                     pheads, droots = ana
                     ### Apply remote phase on local
-                    if remotephases.get('publishing', False):
-                        phases.advanceboundary(self, phases.public, cheads)
-                    else: # publish = False
-                        phases.advanceboundary(self, phases.public, pheads)
-                        phases.advanceboundary(self, phases.draft, cheads)
+                    phases.advanceboundary(self, phases.public, pheads)
+                    phases.advanceboundary(self, phases.draft, cheads)
                     ### Apply local phase on remote
-
                     # Get the list of all revs draft on remote by public here.
                     # XXX Beware that revset break if droots is not strictly
                     # XXX root we may want to ensure it is but it is costly
diff -r 692cbda1eb50 mercurial/phases.py
--- a/mercurial/phases.py	Wed Jan 23 22:52:55 2013 +0900
+++ b/mercurial/phases.py	Fri Jan 25 16:32:26 2013 -0600
@@ -294,28 +294,12 @@
 def listphases(repo):
     """List phases root for serialization over pushkey"""
     keys = {}
-    value = '%i' % draft
-    for root in repo._phasecache.phaseroots[draft]:
-        keys[hex(root)] = value
-
     if repo.ui.configbool('phases', 'publish', True):
-        # Add an extra data to let remote know we are a publishing
-        # repo. Publishing repo can't just pretend they are old repo.
-        # When pushing to a publishing repo, the client still need to
-        # push phase boundary
-        #
-        # Push do not only push changeset. It also push phase data.
-        # New phase data may apply to common changeset which won't be
-        # push (as they are common). Here is a very simple example:
-        #
-        # 1) repo A push changeset X as draft to repo B
-        # 2) repo B make changeset X public
-        # 3) repo B push to repo A. X is not pushed but the data that
-        #    X as now public should
-        #
-        # The server can't handle it on it's own as it has no idea of
-        # client phase data.
         keys['publishing'] = 'True'
+    else:
+        value = '%i' % draft
+        for root in repo._phasecache.phaseroots[draft]:
+            keys[hex(root)] = value
     return keys
 
 def pushphase(repo, nhex, oldphasestr, newphasestr):


-- 
Mathematics is the supreme nostalgia of our time.





More information about the Mercurial mailing list