= Staging Changes = {{{#!wiki caution This page is currently a work in progress. It also does not reflect the [[ContributingChanges|contribution practices]] of the Mercurial project itself. }}} Although Mercurial encourages the sharing of changes between repositories by default, and in many environments it is natural to clone, [[Pull|pull]] and [[Push|push]] with the result that all work is replicated, some environments attempt to maintain a set of "clean" repositories containing only changes regarded as essential to a particular work, not the changes made by contributors in private within their own repository clones. The practice of [[CommunicatingChanges|communicating changes]] by exporting them in some way or other is employed by various projects including [[ContributingChanges|Mercurial itself]], but in some environments contributors may be expected to push "clean" changes directly to a project repository, potentially a central repository in connection with a [[CvsLikePractice|CVS-like working practice]]. In order to support the activity of making and pushing "clean" or "collapsed" changes selectively, it can be useful to maintain a staging area in the form of an additional clone of an upstream repository. Consider a contributor pulling and pushing changes in a centralised model, having cloned the upstream repository: {{{#!dot digraph G { remote [label="remote repository"]; local [label="local clone"]; remote -> local [label="pull"]; local:e -> remote:e [label="push"]; } }}} It may demand a fair amount of care to only push acceptable changes back to the upstream repository, so we might suggest an additional clone as follows: {{{#!dot digraph G { remote [label="remote repository"]; staging [label="staging clone"]; local [label="local clone"]; remote -> staging [label="pull"]; staging -> local [label="pull"]; local:e -> staging:e [label="push"]; staging:e -> remote:e [label="push"]; } }}} This process might be done as follows: {{{ hg clone upstream staging hg clone staging local }}} This may not be technically necessary, but the idea is to have a local clone into which we will contribute changes acceptable for the upstream repository. If we manage to make a mistake, we will not have polluted the upstream repository, and can recover by, in the worst case, cloning the upstream repository anew. == Preparing clean changes == As a contributor, you can now do work within the local clone, committing freely in order to version your changes. However, as already discussed, the upstream project may not want all your changesets as part of their repository's history. Consider the following situation: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; } subgraph { node [shape="box"] rank=same label2 [label="staging clone",shape="none"]; start2 [label="start"]; } subgraph { node [shape="record"] rank=same label3 [label="local clone",shape="none"]; start3 [label="1|start"]; change1 [label="2|change #1"]; change2 [label="3|change #2"]; change3 [label="4|change #3"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; label2 -> label3 [penwidth="0",arrowsize="0"]; checkout [label="working files"]; start3 -> change1 -> change2 -> change3; start1 -> start2 [label="pull"]; start2 -> start3 [label="pull"]; start3 -> checkout:w [label="update"]; checkout -> change1 [label="commit"]; checkout -> change2 [label="commit"]; checkout -> change3 [label="commit"]; } }}} It may be undesirable to replicate each of the committed changes to the upstream repository whose maintainers would prefer a single changeset incorporating the work done. To prepare such a changeset, one can produce a diff summarising the work as follows: {{{ hg diff -r 1 }}} Here, the convenient numeric changeset identifier has been used for simplicity. This diff can be stored in a file as follows: {{{ hg diff -r 1 > changes.diff }}} Now, in order to communicate this change, we need to pretend that the work was done in a single collapsed changeset starting from the same place in the repository history. We can navigate to that place as follows: {{{ hg update -r 1 }}} The working directory for our local clone will have the following state indicated by the red node in the graph: {{{#!dot digraph G { subgraph { node [shape="record"] rank=same start3 [label="1|start",color="red"]; change1 [label="2|change #1"]; change2 [label="3|change #2"]; change3 [label="4|change #3"]; } start3 -> change1 -> change2 -> change3; } }}} We may now apply our collapsed changeset to the working files: {{{ patch -p1 < changes.diff }}} This changeset may have added or removed files, and we should check the repository status to remind ourselves which files have been affected, using `hg add` and `hg remove` to ensure that they are taken into consideration when committing the changes. Committing the result will cause the local clone to change: {{{#!dot digraph G { rankdir=LR; node [shape="record"]; start3 [label="{1|start}"]; changes [label="{5|changes}",color="red"]; subgraph { change1 [label="{2|change #1}"]; change2 [label="{3|change #2}"]; change3 [label="{4|change #3}"]; } start3 -> change1 -> change2 -> change3; start3 -> changes; } }}} Mercurial will report that it has created a new head in the repository. At this point, we have a changeset in our clone which should be acceptable for contribution. == Pushing changes for staging == Now if we were to merely perform a standard [[Push|push]] operation, all changesets from the local clone would be pushed to the staging clone; this is not what we want. Instead, we should merely push the collapsed change; this is done by specifying the revision of the collapsed changeset: {{{ hg push -r 5 ../staging }}} Here, we assume that the `staging` clone resides in the same directory as the `local` clone. As a result of pushing, the repositories will now look like this: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; } subgraph { node [shape="box"] rank=same label2 [label="staging clone",shape="none"]; start2 [label="start"]; changes2 [label="changes"]; } subgraph { node [shape="record"] rank=same; label3 [label="local clone",shape="none"]; start3 [label="1|start"]; changes3 [label="5|changes",color="red"]; } subgraph { node [shape="record"] rank=same; change1 [label="2|change #1"]; change2 [label="3|change #2"]; change3 [label="4|change #3"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; label2 -> label3 [penwidth="0",arrowsize="0"]; start1 -> start2 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start2 -> start3 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start2 -> changes2; start3 -> change1 -> change2 -> change3; start3 -> changes3; changes3 -> change1 [penwidth="0",arrowsize="0"]; changes3 -> changes2 [label="push"]; } }}} You can now push freely to the upstream repository from the staging clone: {{{ cd ../staging hg push # should push to upstream }}} This should have the following effect: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; changes1 [label="changes"]; } subgraph { node [shape="box"] rank=same label2 [label="staging clone",shape="none"]; start2 [label="start"]; changes2 [label="changes"]; } subgraph { node [shape="record"] rank=same; label3 [label="local clone",shape="none"]; start3 [label="1|start"]; changes3 [label="5|changes",color="red"]; } subgraph { node [shape="record"] rank=same; change1 [label="2|change #1"]; change2 [label="3|change #2"]; change3 [label="4|change #3"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; label2 -> label3 [penwidth="0",arrowsize="0"]; start1 -> start2 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start2 -> start3 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start1 -> changes1; start2 -> changes2; start3 -> change1 -> change2 -> change3; start3 -> changes3; changes3 -> change1 [penwidth="0",arrowsize="0"]; changes3 -> changes2 [label="push",color="#bbbbbb",fontcolor="#bbbbbb"]; changes2 -> changes1 [label="push"]; } }}} == Dealing with new upstream changes == It may be the case that changes have been made upstream while your own work was being done, and pushing to the upstream repository causes Mercurial to complain about new remote heads: {{{ abort: push creates new remote heads! }}} This can be visualised as follows: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; cc1 [shape="none"; label=<
competing
changes
>]; } subgraph { node [shape="box"] rank=same label2 [label="staging clone",shape="none"]; start2 [label="start"]; changes2 [label="changes"]; } subgraph { node [shape="record"] rank=same; label3 [label="local clone",shape="none"]; start3 [label="1|start"]; changes3 [label="5|changes"]; } subgraph { node [shape="record"] rank=same; change1 [label="2|change #1"]; change2 [label="3|change #2"]; change3 [label="4|change #3"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; label2 -> label3 [penwidth="0",arrowsize="0"]; start1 -> start2 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start2 -> start3 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start1 -> cc1:competing; start1 -> cc1:changes [color="#bbbbbb"]; start2 -> changes2; start3 -> change1 -> change2 -> change3; start3 -> changes3; changes3 -> change1 [penwidth="0",arrowsize="0"]; changes3 -> changes2 [label="push",color="#bbbbbb",fontcolor="#bbbbbb"]; changes2 -> cc1:changes [label="push"]; } }}} Such "competing" upstream changes can be pulled and merged in the staging repository. First, [[Update|update]] to the point at which your work was done: {{{ hg update tip }}} This assumes that your work is the most recent revision. If this is not the case for some reason, use the following to find the appropriate revision number: {{{ hg heads }}} Then pull and merge the competing changes: {{{ hg pull hg merge }}} If all went well, the result can be committed and then pushed upstream: {{{ hg commit hg push }}} The result should look like this: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; cc1 [shape="none"; label=<
changes
competing
>]; merged1 [label="merged"]; } subgraph { node [shape="box"] rank=same label2 [label="staging clone",shape="none"]; start2 [label="start"]; cc2 [shape="none"; label=<
competing
changes
>]; merged2 [label="merged"]; } subgraph { rank=same checkout [label="working files"]; edited [label="..."]; } label1 -> label2 [penwidth="0",arrowsize="0"]; start1 -> start2 [label="pull",color="#bbbbbb",fontcolor="#bbbbbb"]; start1 -> cc1:competing -> merged1; start1 -> cc1:changes -> merged1 [color="#bbbbbb"]; start2 -> cc2:competing -> merged2; start2 -> cc2:changes -> merged2; cc1:competing -> cc2:competing [label="pull"]; merged2 -> merged1 [label="push"]; checkout:e -> edited; cc2:competing -> edited [label="merge"]; cc2:changes -> checkout [label="update"]; edited -> merged2 [label="commit"]; } }}} Now, the staging repository should reflect the state of the upstream repository. == Returning to work == Updating your local clone should be as easy as performing a pull operation: {{{ hg pull }}} This will add the details of the merge you performed to the local clone, but your previous work will be unaffected (as nothing gets thrown away): {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; cc1 [shape="none"; label=<
changes
competing
>]; merged1 [label="merged"]; } subgraph { node [shape="box"] rank=same label2 [label="staging clone",shape="none"]; start2 [label="start"]; cc2 [shape="none"; label=<
competing
changes
>]; merged2 [label="merged"]; } subgraph { node [shape="record"] rank=same; label3 [label="local clone",shape="none"]; start3 [label="1|start"]; competing3 [label="6|competing",color="#bbbbbb"]; merged3 [label="7|merged"]; } subgraph { node [shape="record"] rank=same; changes3 [label="5|changes"]; } subgraph { node [shape="record"] rank=same; change1 [label="2|change #1"]; change2 [label="3|change #2"]; change3 [label="4|change #3"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; label2 -> label3 [penwidth="0",arrowsize="0"]; start1 -> cc1:competing -> merged1; start1 -> cc1:changes -> merged1; start2 -> cc2:competing -> merged2; start2 -> cc2:changes -> merged2; start3 -> change1 -> change2 -> change3; start3 -> changes3 -> merged3; start3 -> competing3 -> merged3 [color="#bbbbbb"]; competing3 -> changes3 -> change1 [penwidth="0",arrowsize="0"]; merged3 -> change2 [penwidth="0",arrowsize="0"]; merged2 -> merged3 [label="pull"]; } }}} You should now update to a revision known to the upstream repository, such as the tip which has just been retrieved in the pull operation: {{{ hg update tip }}} And from this point, it should be possible to repeat the whole contribution process again. As a side-effect of having an unshared "branch" of changes that we didn't push previously, it is now not possible to perform a default push operation without Mercurial complaining: {{{ hg push # causes an "abort: push creates new remote heads!" error }}} This forces us to consider how we attempt to share our contributions more carefully. However, it is necessary to exercise caution: if you decide to merge from one of your unshared revisions, for whatever reason, you run the risk of pushing your unshared changes by accident. That said, in the event of having to revisit any work previously shared, it is most likely that the starting point of that work would be the shared, collapsed changeset, not any of the component changesets comprising the eventually shared work. ---- CategoryHowTo