Patchwork [2,of,2] push: introduce --readonly to make push behave more like pull

login
register
mail settings
Submitter Mads Kiilerich
Date Aug. 20, 2016, 11:33 p.m.
Message ID <48f4ae36a49929c2389b.1471735994@madski>
Download mbox | patch
Permalink /patch/16369/
State Deferred
Headers show

Comments

Mads Kiilerich - Aug. 20, 2016, 11:33 p.m.
# HG changeset patch
# User Mads Kiilerich <madski@unity3d.com>
# Date 1471735620 -7200
#      Sun Aug 21 01:27:00 2016 +0200
# Branch stable
# Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
# Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
push: introduce --readonly to make push behave more like pull

Push and pull are almost symmetric ... except when they are not.

For example, push makes it possible to use a revset to specify the revisions to
push. Push can also be more suitable for distributing changes to multiple
mirrors than pull is. Finally, push will by default publish draft changesets.

Often, it is correct and desirable that push tries to update phases and thus
locks the repository while pushing ... put that also means that there can't be
multiple concurrent pushes and that pushing from non-publishing repositories
depends on correct configuration of the target repository.

We thus introduce --readonly to prevent push from locking the source repository
and thus prevent it from publishing. Push with --readonly is thus more
symmetric to pull.
Augie Fackler - Aug. 22, 2016, 2:25 p.m.
On Sun, Aug 21, 2016 at 01:33:14AM +0200, Mads Kiilerich wrote:
> # HG changeset patch
> # User Mads Kiilerich <madski@unity3d.com>
> # Date 1471735620 -7200
> #      Sun Aug 21 01:27:00 2016 +0200
> # Branch stable
> # Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
> # Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
> push: introduce --readonly to make push behave more like pull

Hm, interesting. To clarify the use case, you've got two local
repositories, and you want to move some draft changes between them,
but because (for example) push allows a revset, it's easier to push
than to pull?

So the goal here is to "push without publishing", and if the
destination supports drafts, you want to push the draft changes as
drafts, otherwise fail? Is that right?

>
> Push and pull are almost symmetric ... except when they are not.
>
> For example, push makes it possible to use a revset to specify the revisions to
> push. Push can also be more suitable for distributing changes to multiple
> mirrors than pull is. Finally, push will by default publish draft changesets.
>
> Often, it is correct and desirable that push tries to update phases and thus
> locks the repository while pushing ... put that also means that there can't be
> multiple concurrent pushes and that pushing from non-publishing repositories
> depends on correct configuration of the target repository.
>
> We thus introduce --readonly to prevent push from locking the source repository
> and thus prevent it from publishing. Push with --readonly is thus more
> symmetric to pull.

I wonder if we should have --[no-]publish flags on push, wherein the
--no-publish form would make it easy to "push these draft changes but
only if it doesn't publish them" and the --publish form would make it
easier to "push and publish these, but don't make them public unless
the push succeeds." Thoughts?

(This particular strategy would be facilitated by my upcoming boolean
flag behavioral improvements.)
Gregory Szorc - Aug. 22, 2016, 4:02 p.m.
> On Aug 22, 2016, at 07:25, Augie Fackler <raf@durin42.com> wrote:
> 
>> On Sun, Aug 21, 2016 at 01:33:14AM +0200, Mads Kiilerich wrote:
>> # HG changeset patch
>> # User Mads Kiilerich <madski@unity3d.com>
>> # Date 1471735620 -7200
>> #      Sun Aug 21 01:27:00 2016 +0200
>> # Branch stable
>> # Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
>> # Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
>> push: introduce --readonly to make push behave more like pull
> 
> Hm, interesting. To clarify the use case, you've got two local
> repositories, and you want to move some draft changes between them,
> but because (for example) push allows a revset, it's easier to push
> than to pull?
> 
> So the goal here is to "push without publishing", and if the
> destination supports drafts, you want to push the draft changes as
> drafts, otherwise fail? Is that right?
> 
>> 
>> Push and pull are almost symmetric ... except when they are not.
>> 
>> For example, push makes it possible to use a revset to specify the revisions to
>> push. Push can also be more suitable for distributing changes to multiple
>> mirrors than pull is. Finally, push will by default publish draft changesets.
>> 
>> Often, it is correct and desirable that push tries to update phases and thus
>> locks the repository while pushing ... put that also means that there can't be
>> multiple concurrent pushes and that pushing from non-publishing repositories
>> depends on correct configuration of the target repository.
>> 
>> We thus introduce --readonly to prevent push from locking the source repository
>> and thus prevent it from publishing. Push with --readonly is thus more
>> symmetric to pull.
> 
> I wonder if we should have --[no-]publish flags on push, wherein the
> --no-publish form would make it easy to "push these draft changes but
> only if it doesn't publish them" and the --publish form would make it
> easier to "push and publish these, but don't make them public unless
> the push succeeds." Thoughts?

I proposed adding a path sub-option to disable publishing as well. I think the use case is marginal enough that it shouldn't be a command argument.
Mads Kiilerich - Aug. 23, 2016, 1:14 a.m.
On 08/22/2016 04:25 PM, Augie Fackler wrote:
> On Sun, Aug 21, 2016 at 01:33:14AM +0200, Mads Kiilerich wrote:
>> # HG changeset patch
>> # User Mads Kiilerich <madski@unity3d.com>
>> # Date 1471735620 -7200
>> #      Sun Aug 21 01:27:00 2016 +0200
>> # Branch stable
>> # Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
>> # Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
>> push: introduce --readonly to make push behave more like pull
> Hm, interesting. To clarify the use case, you've got two local
> repositories, and you want to move some draft changes between them,
> but because (for example) push allows a revset, it's easier to push
> than to pull?
>
> So the goal here is to "push without publishing", and if the
> destination supports drafts, you want to push the draft changes as
> drafts, otherwise fail? Is that right?

My primary use case is to have a push hook that will push to other 
repositories - some of them local, some of them remote.

Push has some advantages:
* I can use revsets to control what is pushed.
* Pushing is "the right model" for propagating changes - polling/pulling 
is bad.
* For a master server with "slaves" it also has some advantages that the 
master will call out to the slaves instead of allowing them to connect.
* It's so weird to creating a ssh connection to a remote server and pass 
the result of some local revset query, just to let it connect back and 
make a pull.

But the problems are:
* The primary problem is the write lock that prevents multiple 
simultaneous pushes. Sudoing to another user is a odd workaround.
* When I'm pushing I'm in charge. Then it is "wrong" that it is 
hardcoded that the remote server decides how the push modifies state on 
my server.

These problems can be addressed with this flag.

> I wonder if we should have --[no-]publish flags on push, wherein the
> --no-publish form would make it easy to "push these draft changes but
> only if it doesn't publish them" and the --publish form would make it
> easier to "push and publish these, but don't make them public unless
> the push succeeds." Thoughts?

Perhaps ... but that is also the opposite of what I'm asking for and 
would not solve my use case, right? I want to push as if the receiving 
repo was non-publishing. Also, I want it to not lock.

I don't know if this feature I'm asking for most correctly is described 
as non-locking and thus readonly and not modifying state or publishing, 
or if it non-publishing and thus readonly and with no need for locking. 
It could be described either way - I just want one of them ;-)

/Mads
Yuya Nishihara - Aug. 23, 2016, 1:45 p.m.
On Tue, 23 Aug 2016 03:14:59 +0200, Mads Kiilerich wrote:
> On 08/22/2016 04:25 PM, Augie Fackler wrote:
> > On Sun, Aug 21, 2016 at 01:33:14AM +0200, Mads Kiilerich wrote:
> >> # HG changeset patch
> >> # User Mads Kiilerich <madski@unity3d.com>
> >> # Date 1471735620 -7200
> >> #      Sun Aug 21 01:27:00 2016 +0200
> >> # Branch stable
> >> # Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
> >> # Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
> >> push: introduce --readonly to make push behave more like pull
> > Hm, interesting. To clarify the use case, you've got two local
> > repositories, and you want to move some draft changes between them,
> > but because (for example) push allows a revset, it's easier to push
> > than to pull?
> >
> > So the goal here is to "push without publishing", and if the
> > destination supports drafts, you want to push the draft changes as
> > drafts, otherwise fail? Is that right?
> 
> My primary use case is to have a push hook that will push to other 
> repositories - some of them local, some of them remote.
> 
> Push has some advantages:
> * I can use revsets to control what is pushed.
> * Pushing is "the right model" for propagating changes - polling/pulling 
> is bad.
> * For a master server with "slaves" it also has some advantages that the 
> master will call out to the slaves instead of allowing them to connect.
> * It's so weird to creating a ssh connection to a remote server and pass 
> the result of some local revset query, just to let it connect back and 
> make a pull.
> 
> But the problems are:
> * The primary problem is the write lock that prevents multiple 
> simultaneous pushes. Sudoing to another user is a odd workaround.
> * When I'm pushing I'm in charge. Then it is "wrong" that it is 
> hardcoded that the remote server decides how the push modifies state on 
> my server.
> 
> These problems can be addressed with this flag.

Just an idea. Locking issue could be addressed at vfs layer, using something
like a readonly localrepo, which would be instantiated by "-R readonly:<path>".
Augie Fackler - Aug. 23, 2016, 1:49 p.m.
On Mon, Aug 22, 2016 at 9:14 PM, Mads Kiilerich <mads@kiilerich.com> wrote:
> On 08/22/2016 04:25 PM, Augie Fackler wrote:
>>
>> On Sun, Aug 21, 2016 at 01:33:14AM +0200, Mads Kiilerich wrote:
>>>
>>> # HG changeset patch
>>> # User Mads Kiilerich <madski@unity3d.com>
>>> # Date 1471735620 -7200
>>> #      Sun Aug 21 01:27:00 2016 +0200
>>> # Branch stable
>>> # Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
>>> # Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
>>> push: introduce --readonly to make push behave more like pull
>>
>> Hm, interesting. To clarify the use case, you've got two local
>> repositories, and you want to move some draft changes between them,
>> but because (for example) push allows a revset, it's easier to push
>> than to pull?
>>
>> So the goal here is to "push without publishing", and if the
>> destination supports drafts, you want to push the draft changes as
>> drafts, otherwise fail? Is that right?
>
>
> My primary use case is to have a push hook that will push to other
> repositories - some of them local, some of them remote.
>
> Push has some advantages:
> * I can use revsets to control what is pushed.
> * Pushing is "the right model" for propagating changes - polling/pulling is
> bad.
> * For a master server with "slaves" it also has some advantages that the
> master will call out to the slaves instead of allowing them to connect.
> * It's so weird to creating a ssh connection to a remote server and pass the
> result of some local revset query, just to let it connect back and make a
> pull.
>
> But the problems are:
> * The primary problem is the write lock that prevents multiple simultaneous
> pushes. Sudoing to another user is a odd workaround.
> * When I'm pushing I'm in charge. Then it is "wrong" that it is hardcoded
> that the remote server decides how the push modifies state on my server.
>
> These problems can be addressed with this flag.
>
>> I wonder if we should have --[no-]publish flags on push, wherein the
>> --no-publish form would make it easy to "push these draft changes but
>> only if it doesn't publish them" and the --publish form would make it
>> easier to "push and publish these, but don't make them public unless
>> the push succeeds." Thoughts?
>
>
> Perhaps ... but that is also the opposite of what I'm asking for and would
> not solve my use case, right? I want to push as if the receiving repo was
> non-publishing. Also, I want it to not lock.
>
> I don't know if this feature I'm asking for most correctly is described as
> non-locking and thus readonly and not modifying state or publishing, or if
> it non-publishing and thus readonly and with no need for locking. It could
> be described either way - I just want one of them ;-)

I think the latter is more sensible from a user perspective - the user
doesn't (ideally) know about or care about our locking requirements,
so the visible side effect to them (no phase changes) seems like the
thing to focus on from a UI level.

>
> /Mads
Gregory Szorc - Aug. 23, 2016, 5:02 p.m.
On Mon, Aug 22, 2016 at 6:14 PM, Mads Kiilerich <mads@kiilerich.com> wrote:

> On 08/22/2016 04:25 PM, Augie Fackler wrote:
>
>> On Sun, Aug 21, 2016 at 01:33:14AM +0200, Mads Kiilerich wrote:
>>
>>> # HG changeset patch
>>> # User Mads Kiilerich <madski@unity3d.com>
>>> # Date 1471735620 -7200
>>> #      Sun Aug 21 01:27:00 2016 +0200
>>> # Branch stable
>>> # Node ID 48f4ae36a49929c2389bfb75064f96d16159b2d0
>>> # Parent  7880f56ca7495a2d0365a8280935eccd1e63f0c8
>>> push: introduce --readonly to make push behave more like pull
>>>
>> Hm, interesting. To clarify the use case, you've got two local
>> repositories, and you want to move some draft changes between them,
>> but because (for example) push allows a revset, it's easier to push
>> than to pull?
>>
>> So the goal here is to "push without publishing", and if the
>> destination supports drafts, you want to push the draft changes as
>> drafts, otherwise fail? Is that right?
>>
>
> My primary use case is to have a push hook that will push to other
> repositories - some of them local, some of them remote.
>
> Push has some advantages:
> * I can use revsets to control what is pushed.
> * Pushing is "the right model" for propagating changes - polling/pulling
> is bad.
> * For a master server with "slaves" it also has some advantages that the
> master will call out to the slaves instead of allowing them to connect.
> * It's so weird to creating a ssh connection to a remote server and pass
> the result of some local revset query, just to let it connect back and make
> a pull.
>

And I think a pull based model is more robust for replication. And I think
a pull based model where clients consume a log of "pull requests" that is
asynchronous to the push operation is even more robust.

Mozilla's first approach at replication was to have the master ssh into the
mirrors and effectively instruct them to perform a `hg pull`. Our current
solution has the master writing replication events into Kafka and daemons
on the mirrors reacting to said events. This is described in detail at
https://mozilla-version-control-tools.readthedocs.io/en/latest/hgmo/replication.html.
The solution works pretty well, especially if a mirror goes down or
connectivity drops. The biggest problem is inconsistency windows on
mirrors. And we have a mitigation for that
https://mozilla-version-control-tools.readthedocs.io/en/latest/hgmo/notifications.html
.




>
> But the problems are:
> * The primary problem is the write lock that prevents multiple
> simultaneous pushes. Sudoing to another user is a odd workaround.
> * When I'm pushing I'm in charge. Then it is "wrong" that it is hardcoded
> that the remote server decides how the push modifies state on my server.
>
> These problems can be addressed with this flag.
>
> I wonder if we should have --[no-]publish flags on push, wherein the
>> --no-publish form would make it easy to "push these draft changes but
>> only if it doesn't publish them" and the --publish form would make it
>> easier to "push and publish these, but don't make them public unless
>> the push succeeds." Thoughts?
>>
>
> Perhaps ... but that is also the opposite of what I'm asking for and would
> not solve my use case, right? I want to push as if the receiving repo was
> non-publishing. Also, I want it to not lock.
>
> I don't know if this feature I'm asking for most correctly is described as
> non-locking and thus readonly and not modifying state or publishing, or if
> it non-publishing and thus readonly and with no need for locking. It could
> be described either way - I just want one of them ;-)
>
> /Mads
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
Mads Kiilerich - Aug. 23, 2016, 9:52 p.m.
On 08/23/2016 03:49 PM, Augie Fackler wrote:
>> I don't know if this feature I'm asking for most correctly is described as
>> non-locking and thus readonly and not modifying state or publishing, or if
>> it non-publishing and thus readonly and with no need for locking. It could
>> be described either way - I just want one of them ;-)
> I think the latter is more sensible from a user perspective - the user
> doesn't (ideally) know about or care about our locking requirements,
> so the visible side effect to them (no phase changes) seems like the
> thing to focus on from a UI level.

It is however (in the situation) very visible and annoying for the user 
that push is locking the source repo.

For many use cases, phase change (publish on push) is really not 
relevant. For example when you are propagating a mainline / upstream 
repo to a mirror and everything already has been published.

/Mads
Mads Kiilerich - Aug. 24, 2016, 12:20 a.m.
On 08/23/2016 07:02 PM, Gregory Szorc wrote:
> On Mon, Aug 22, 2016 at 6:14 PM, Mads Kiilerich <mads@kiilerich.com 
> <mailto:mads@kiilerich.com>> wrote:
>
>
>     Push has some advantages:
>     * I can use revsets to control what is pushed.
>     * Pushing is "the right model" for propagating changes -
>     polling/pulling is bad.
>     * For a master server with "slaves" it also has some advantages
>     that the master will call out to the slaves instead of allowing
>     them to connect.
>     * It's so weird to creating a ssh connection to a remote server
>     and pass the result of some local revset query, just to let it
>     connect back and make a pull.
>
>
> And I think a pull based model is more robust for replication. And I 
> think a pull based model where clients consume a log of "pull 
> requests" that is asynchronous to the push operation is even more robust.

Right now, I would agree. Pull is more stable than push because of the 
hardcoded policy that pull is read only so there can be multiple 
parallel pulls from a repo, but only one locking push at a time from a 
repo. I want to fix that.

(Btw: AFAIK, that "scalability regression" came when phases were 
introduced. I want to give an option for working around that.)

If you are running async out of a queue anyway and have this read-only 
push, then I doubt there will be much significant difference in 
robustness between initiating the "exchange" from the master and from 
the remove server. So sure, if you want more logic than just the plain 
hgweb on the remote servers and already have a reliable distributed 
secure message queue, then pulling is just as fine as pushing (except 
for the missing revsets).

No single size fits all, and even though our setups are similar, they 
also have differences. I would love to look more into your solution. But 
now, I just need this missing piece to make the next step / improvement 
in our setup.

/Mads
Pierre-Yves David - Aug. 29, 2016, 1:17 p.m.
Various opinion/idea on this matters.

- Another way to solve Mads usecase is to allow for pushing multiple 
patch at the same time, an easy way to define this would be to allow to 
define a path as an alias for multiple other (call "combined path"?). 
Given than we have path attributes already this should not be too hard 
to implement

- If we go for a magic config, I think the use-case is rare enough that 
a path attribute is enough. I don't see the point of getting an extra 
command line flag for it,

- If we get such config/flag I don't think we should make it specific to 
phases. bundle2's pushback requires locking (eg: pushrebase) and we'll 
keep adding usecase.

- I really like the yuya idea to have a specific syntaxt to access a 
repository read only ("-R readonly:<path>"). This is a slight extension 
of existing syntax/concept, consequence are very clear to the user, 
implementation will be very solid as plug very cleanly at low level.

- Another solution for mads is to have 'hg pull' support revset. We 
already have ways to hgweb to support revset without too much fear for 
DOS, we can probably extend that to pull.

Cheers


On 08/24/2016 02:20 AM, Mads Kiilerich wrote:
> On 08/23/2016 07:02 PM, Gregory Szorc wrote:
>> On Mon, Aug 22, 2016 at 6:14 PM, Mads Kiilerich <mads@kiilerich.com
>> <mailto:mads@kiilerich.com>> wrote:
>>
>>
>>     Push has some advantages:
>>     * I can use revsets to control what is pushed.
>>     * Pushing is "the right model" for propagating changes -
>>     polling/pulling is bad.
>>     * For a master server with "slaves" it also has some advantages
>>     that the master will call out to the slaves instead of allowing
>>     them to connect.
>>     * It's so weird to creating a ssh connection to a remote server
>>     and pass the result of some local revset query, just to let it
>>     connect back and make a pull.
>>
>>
>> And I think a pull based model is more robust for replication. And I
>> think a pull based model where clients consume a log of "pull
>> requests" that is asynchronous to the push operation is even more robust.
>
> Right now, I would agree. Pull is more stable than push because of the
> hardcoded policy that pull is read only so there can be multiple
> parallel pulls from a repo, but only one locking push at a time from a
> repo. I want to fix that.
>
> (Btw: AFAIK, that "scalability regression" came when phases were
> introduced. I want to give an option for working around that.)
>
> If you are running async out of a queue anyway and have this read-only
> push, then I doubt there will be much significant difference in
> robustness between initiating the "exchange" from the master and from
> the remove server. So sure, if you want more logic than just the plain
> hgweb on the remote servers and already have a reliable distributed
> secure message queue, then pulling is just as fine as pushing (except
> for the missing revsets).
>
> No single size fits all, and even though our setups are similar, they
> also have differences. I would love to look more into your solution. But
> now, I just need this missing piece to make the next step / improvement
> in our setup.
>
> /Mads
>
>
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>
Augie Fackler - Aug. 29, 2016, 1:19 p.m.
> On Aug 29, 2016, at 09:17, Pierre-Yves David <pierre-yves.david@ens-lyon.org> wrote:
> 
> Various opinion/idea on this matters.
> 
> - Another way to solve Mads usecase is to allow for pushing multiple patch at the same time, an easy way to define this would be to allow to define a path as an alias for multiple other (call "combined path"?). Given than we have path attributes already this should not be too hard to implement
> 
> - If we go for a magic config, I think the use-case is rare enough that a path attribute is enough. I don't see the point of getting an extra command line flag for it,
> 
> - If we get such config/flag I don't think we should make it specific to phases. bundle2's pushback requires locking (eg: pushrebase) and we'll keep adding usecase.
> 
> - I really like the yuya idea to have a specific syntaxt to access a repository read only ("-R readonly:<path>"). This is a slight extension of existing syntax/concept, consequence are very clear to the user, implementation will be very solid as plug very cleanly at low level.
> 
> - Another solution for mads is to have 'hg pull' support revset. We already have ways to hgweb to support revset without too much fear for DOS, we can probably extend that to pull.

I like this last one - it seems like it might be generally useful.

> 
> Cheers
> 
> 
> On 08/24/2016 02:20 AM, Mads Kiilerich wrote:
>> On 08/23/2016 07:02 PM, Gregory Szorc wrote:
>>> On Mon, Aug 22, 2016 at 6:14 PM, Mads Kiilerich <mads@kiilerich.com
>>> <mailto:mads@kiilerich.com>> wrote:
>>> 
>>> 
>>>    Push has some advantages:
>>>    * I can use revsets to control what is pushed.
>>>    * Pushing is "the right model" for propagating changes -
>>>    polling/pulling is bad.
>>>    * For a master server with "slaves" it also has some advantages
>>>    that the master will call out to the slaves instead of allowing
>>>    them to connect.
>>>    * It's so weird to creating a ssh connection to a remote server
>>>    and pass the result of some local revset query, just to let it
>>>    connect back and make a pull.
>>> 
>>> 
>>> And I think a pull based model is more robust for replication. And I
>>> think a pull based model where clients consume a log of "pull
>>> requests" that is asynchronous to the push operation is even more robust.
>> 
>> Right now, I would agree. Pull is more stable than push because of the
>> hardcoded policy that pull is read only so there can be multiple
>> parallel pulls from a repo, but only one locking push at a time from a
>> repo. I want to fix that.
>> 
>> (Btw: AFAIK, that "scalability regression" came when phases were
>> introduced. I want to give an option for working around that.)
>> 
>> If you are running async out of a queue anyway and have this read-only
>> push, then I doubt there will be much significant difference in
>> robustness between initiating the "exchange" from the master and from
>> the remove server. So sure, if you want more logic than just the plain
>> hgweb on the remote servers and already have a reliable distributed
>> secure message queue, then pulling is just as fine as pushing (except
>> for the missing revsets).
>> 
>> No single size fits all, and even though our setups are similar, they
>> also have differences. I would love to look more into your solution. But
>> now, I just need this missing piece to make the next step / improvement
>> in our setup.
>> 
>> /Mads
>> 
>> 
>> 
>> _______________________________________________
>> Mercurial-devel mailing list
>> Mercurial-devel@mercurial-scm.org
>> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
>> 
> 
> -- 
> Pierre-Yves David

Patch

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -5878,6 +5878,7 @@  def pull(ui, repo, source="default", **o
     ('b', 'branch', [],
      _('a specific branch you would like to push'), _('BRANCH')),
     ('', 'new-branch', False, _('allow pushing a new branch')),
+    ('', 'readonly', False, _("no source repo locking or phase update")),
     ] + remoteopts,
     _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
 def push(ui, repo, dest=None, **opts):
@@ -5886,8 +5887,9 @@  def push(ui, repo, dest=None, **opts):
     Push changesets from the local repository to the specified
     destination.
 
-    This operation is symmetrical to pull: it is identical to a pull
-    in the destination repository from the current one.
+    This operation is almost symmetrical to pull: it is identical to a pull
+    in the destination repository from the current one, except that pushed
+    changesets in draft phase are made public unless --readonly is used.
 
     By default, push will not allow creation of new heads at the
     destination, since multiple heads would make it unclear which head
@@ -5969,7 +5971,8 @@  def push(ui, repo, dest=None, **opts):
     pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
                            newbranch=opts.get('new_branch'),
                            bookmarks=opts.get('bookmark', ()),
-                           opargs=opts.get('opargs'))
+                           opargs=opts.get('opargs'),
+                           readonly=opts.get('readonly'))
 
     result = not pushop.cgresult
 
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -275,7 +275,7 @@  class pushoperation(object):
     """
 
     def __init__(self, repo, remote, force=False, revs=None, newbranch=False,
-                 bookmarks=()):
+                 bookmarks=(), readonly=False):
         # repo we push from
         self.repo = repo
         self.ui = repo.ui
@@ -289,6 +289,8 @@  class pushoperation(object):
         self.bookmarks = bookmarks
         # allow push of new branch
         self.newbranch = newbranch
+        # no locking of source repo and no publishing phase update
+        self.readonly = readonly
         # did a local lock get acquired?
         self.locallocked = None
         # step already performed
@@ -379,7 +381,7 @@  bookmsgmap = {'update': (_("updating boo
 
 
 def push(repo, remote, force=False, revs=None, newbranch=False, bookmarks=(),
-         opargs=None):
+         opargs=None, readonly=False):
     '''Push outgoing changesets (limited by revs) from a local
     repository to remote. Return an integer:
       - None means nothing to push
@@ -391,7 +393,7 @@  def push(repo, remote, force=False, revs
     if opargs is None:
         opargs = {}
     pushop = pushoperation(repo, remote, force, revs, newbranch, bookmarks,
-                           **opargs)
+                           readonly, **opargs)
     if pushop.remote.local():
         missing = (set(pushop.repo.requirements)
                    - pushop.remote.local().supported)
@@ -413,23 +415,26 @@  def push(repo, remote, force=False, revs
         raise error.Abort(_("destination does not support push"))
     # get local lock as we might write phase data
     localwlock = locallock = None
-    try:
-        # bundle2 push may receive a reply bundle touching bookmarks or other
-        # things requiring the wlock. Take it now to ensure proper ordering.
-        maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback')
-        if _canusebundle2(pushop) and maypushback:
-            localwlock = pushop.repo.wlock()
-        locallock = pushop.repo.lock()
-        pushop.locallocked = True
-    except IOError as err:
-        pushop.locallocked = False
-        if err.errno != errno.EACCES:
-            raise
-        # source repo cannot be locked.
-        # We do not abort the push, but just disable the local phase
-        # synchronisation.
-        msg = 'cannot lock source repository: %s\n' % err
-        pushop.ui.debug(msg)
+    if not pushop.readonly:
+        try:
+            # bundle2 push may receive a reply bundle touching bookmarks or
+            # other things requiring the wlock. Take it now to ensure proper
+            # ordering.
+            maypushback = pushop.ui.configbool('experimental',
+                                               'bundle2.pushback')
+            if _canusebundle2(pushop) and maypushback:
+                localwlock = pushop.repo.wlock()
+            locallock = pushop.repo.lock()
+            pushop.locallocked = True
+        except IOError as err:
+            pushop.locallocked = False
+            if err.errno != errno.EACCES:
+                raise
+            # source repo cannot be locked.
+            # We do not abort the push, but just disable the local phase
+            # synchronisation.
+            msg = 'cannot lock source repository: %s\n' % err
+            pushop.ui.debug(msg)
     try:
         if pushop.locallocked:
             pushop.trmanager = transactionmanager(pushop.repo,
@@ -988,7 +993,7 @@  def _localphasemove(pushop, nodes, phase
                                pushop.trmanager.transaction(),
                                phase,
                                nodes)
-    else:
+    elif not pushop.readonly:
         # repo is not locked, do not change any phases!
         # Informs the user that phases should have been moved when
         # applicable.
diff --git a/mercurial/subrepo.py b/mercurial/subrepo.py
--- a/mercurial/subrepo.py
+++ b/mercurial/subrepo.py
@@ -898,6 +898,7 @@  class hgsubrepo(abstractsubrepo):
         force = opts.get('force')
         newbranch = opts.get('new_branch')
         ssh = opts.get('ssh')
+        readonly = opts.get('readonly')
 
         # push subrepos depth-first for coherent ordering
         c = self._repo['']
@@ -916,7 +917,8 @@  class hgsubrepo(abstractsubrepo):
         self.ui.status(_('pushing subrepo %s to %s\n') %
             (subrelpath(self), dsturl))
         other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
-        res = exchange.push(self._repo, other, force, newbranch=newbranch)
+        res = exchange.push(self._repo, other, force, newbranch=newbranch,
+                            readonly=readonly)
 
         # the repo is now clean
         self._cachestorehash(dsturl)
diff --git a/tests/test-completion.t b/tests/test-completion.t
--- a/tests/test-completion.t
+++ b/tests/test-completion.t
@@ -217,7 +217,7 @@  Show all commands + options
   log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
   merge: force, rev, preview, tool
   pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
-  push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
+  push: force, rev, bookmark, branch, new-branch, readonly, ssh, remotecmd, insecure
   remove: after, force, subrepos, include, exclude
   serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
   status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos, template
diff --git a/tests/test-phases-exchange.t b/tests/test-phases-exchange.t
--- a/tests/test-phases-exchange.t
+++ b/tests/test-phases-exchange.t
@@ -1194,7 +1194,7 @@  2. Test that failed phases movement are 
 
 #endif
 
-Test that clone behaves like pull and doesn't
+Test that clone and push --readonly behave like pull and don't
 publish changesets as plain push does
 
   $ hg -R Upsilon phase -q --force --draft 2
@@ -1214,7 +1214,7 @@  publish changesets as plain push does
   |
   ~
 
-  $ hg -R Upsilon push Pi -r 8
+  $ hg -R Upsilon push Pi -r 8 --readonly
   pushing to Pi
   searching for changes
   adding changesets
@@ -1223,6 +1223,6 @@  publish changesets as plain push does
   added 1 changesets with 1 changes to 1 files
 
   $ hgph Upsilon -r 'min(draft())'
-  o  9 draft a-G - 3e27b6f1eee1
+  o  8 draft a-F - b740e3e5c05d
   |
   ~