= CVS-like Working Practice = Mercurial allows multiple [[WorkingPractices|working practices]]. In a CVS-like world, you will typically have one central repository; let's call it the "main line". This corresponds to CVS's notion of the "trunk". {{{#!dot digraph G { trunk [label="main line"]; subgraph { alice [label="Alice's clone"]; bob [label="Bob's clone"]; carlos [label="Carlos' clone"]; rank=same } trunk -> alice [label="pull"]; alice -> trunk [label="push"]; trunk -> bob [label="pull"]; bob -> trunk [label="push"]; trunk -> carlos [label="pull"]; carlos -> trunk [label="push"]; } }}} Repositories tend to be long-lived, and the "authoritative branches" are clones of the central repository. == Working environment == Developers may have access to the central repositories on a server via SSH (whether that is through "normal" SSH access with each developer having their own login, or through [[SharedSSH|shared SSH logins]], but note that they do not actually log in to the server to get a shell prompt), or they may have access via [[PublishingRepositories|HTTP]] (including [[PublishingRepositories#push|push privileges]]), and push their changes directly from their local clones into the appropriate repositories. == Workflow == With CVS, the practice would be as follows: {{{ # Check for work done... cvs update # Do work, consider changes... cvs diff # Commit changes... cvs commit }}} {{{#!dot digraph G { subgraph { node [shape="box"] rank=same start -> committed; } start -> checkout:w [label="update"]; checkout:e -> committed [label="commit"]; } }}} This doesn't always work out... {{{ cvs commit: Examining . cvs commit: Up-to-date check failed for `test.txt' cvs [commit aborted]: correct above errors first! }}} ...and so the following workflow is typically required if `cvs commit` complains: {{{ # Obtain new changes... cvs update # CVS may complain about "conflicts during merge". # Edit conflicts, then commit... cvs commit }}} {{{#!dot digraph G { subgraph { node [shape="box"] rank=same competing [color="red"]; start -> competing -> committed; } subgraph { rank=same checkout; checkout2 [label="..."; color="red"]; } start -> checkout:w [label="update"]; checkout:e -> checkout2:w; competing -> checkout2:n [label="update"]; checkout2:e -> committed [label="commit"]; } }}} Note that CVS won't let you commit anything new until you deal with any conflicts. === The basic workflow in Mercurial === The corresponding practice would be as follows with Mercurial: {{{ # Check for work done (part of the update operation in CVS)... hg pull # Start working with the latest changes (part of the update operation in CVS)... hg update # Do work, consider changes... hg diff # Commit changes (part of the commit operation in CVS)... hg commit # Push changes (part of the commit operation in CVS)... hg push }}} {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; committed1 [label="committed"]; } subgraph { node [shape="box"] rank=same label2 [label="local clone",shape="none"]; start2 [label="start"]; committed2 [label="committed"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; checkout [label="working files"]; start1 -> committed1; start2 -> committed2; start1 -> start2 [label="pull"]; start2 -> checkout:w [label="update"]; checkout:e -> committed2 [label="commit"]; committed2 -> committed1 [label="push"]; } }}} The single command equivalent of `cvs update` is really... {{{ hg pull -u }}} When you invoke a [[Cmd:pull|pull]] operation, Mercurial will invite you to [[Update|update]] to the pulled [[Revision|revision]]. If you have uncommitted changes to some files and someone starting from the same revision has already committed changes to those files, Mercurial will try and get you to [[Merge|merge]] both sets of changes. This is more or less what CVS does in this situation. {{{#!wiki tip You don't usually want to do `hg pull -u` or `hg update` unless you want to work with other people's changes. Mercurial gives you the choice of integrating your own work with other people's or just obtaining that other work for separate perusal. See [[#Working independently|Working independently]] for more details. }}} === Conflicts and merging in Mercurial === When pulling and [[Cmd:push|pushing]], `hg pull` and `hg push` may respectively complain about multiple [[Head|heads]] being created: {{{ abort: push creates new remote heads! (did you forget to merge? use push -f to force) }}} This means that the content of a repository has been edited in different ways, committed to various repository clones, and these different changes have not yet been reconciled. In the diagram below, an attempt is being made to push changes to a remote repository where some "competing" changes have been committed: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; cc [shape="none"; label=<
competing
committed
>]; } subgraph { node [shape="box"] rank=same label2 [label="local clone",shape="none"]; start2 [label="start"]; committed2 [label="committed"]; } subgraph { rank=same checkout [label="working files"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; start1 -> cc:competing1; start1 -> cc:committed1 [color="#bbbbbb"]; start1 -> start2 [label="pull"]; start2 -> committed2; start2 -> checkout:w [label="update"]; checkout:e -> committed2 [label="commit"]; committed2 -> cc:committed1 [label="push"]; } }}} The warning about multiple heads revolves around the following situation: {{{#!dot digraph G { rankdir=LR node [shape="box"] start; subgraph { rank=same competing [color="red"]; committed; } start -> competing; start -> committed; } }}} [[MultipleHeads|Multiple heads]] can exist in a repository, either after `hg pull` in a local clone or after `hg push -f` to a remote clone, but this leaves any work for reconciling the competing changes for later, which may not be desirable. Nevertheless, Mercurial provides the option of deferring such work, whereas CVS will confront you with the conflict situation immediately. If the remote clone is being used as a CVS-style "central repository" inside which nobody works directly, having multiple heads can be inconvenient because they cannot be merged without cloning or pulling from that repository, doing the necessary merges, and then pushing the work back. To reconcile competing changes, the following workflow becomes necessary: {{{ # Get the remote changes... hg pull # Heads were created, so merge them... hg merge # Edit any conflicts, then commit... hg commit # Push merged changes... hg push }}} Here, we number the operations to make their order a bit more obvious: {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; cc1 [shape="none"; label=<
committed
competing
>]; committed1 [label="merged and committed"]; } subgraph { node [shape="box"] rank=same label2 [label="local clone",shape="none"]; start2 [label="start"]; cc2 [shape="none"; label=<
competing
committed
>]; committed3 [label="merged and committed"]; } subgraph { rank=same checkout [label="working files"]; checkout2 [label="..."; color="red"]; } label1 -> label2 [penwidth="0",arrowsize="0"]; start1 -> cc1:competing1 -> committed1; start1 -> cc1:committed1 -> committed1 [color="#bbbbbb"]; start1 -> start2 [label="pull (1)"]; start2 -> cc2:competing2 -> committed3; start2 -> cc2:committed2 -> committed3; start2 -> checkout:w [label="update (2)"]; checkout:n -> cc2:committed2 [label="commit (3)"]; cc1:competing1 -> cc2:competing2 [label="pull (4)"]; checkout:e -> checkout2:w; cc2:competing2 -> checkout2 [label="merge (5)"]; checkout2:e -> committed3 [label="commit (6)"]; committed3 -> committed1 [label="push (7)"]; } }}} Again, you can defer pushing changes until later. The principal advantage of Mercurial here is that you do not need to be confronted with merging others' changes on every commit. === A note about file-specific commits === (See also the [[CvsConcepts#The_Unit_of_Work|notes on the "unit of work"]] in CVS and Mercurial.) In CVS, it is possible to commit specific files, and if your work on these files does not conflict with other people's work - they have perhaps been working on other files instead - then your changes will be accepted by the repository without any further action: {{{ # Change a specific file, then commit only that file... cvs commit test.txt }}} This will succeed, despite the repository having updates available on other files. {{{#!dot digraph G { subgraph { rank=same cc1 [shape="none"; label=<
other.txt
test.txt
>]; cc1new [shape="none"; label=<
other.txt (changed)
test.txt (changed)
>]; } subgraph { rank=same cc2 [shape="none"; label=<
other.txt
test.txt
>]; cc2new [shape="none"; label=<
other.txt
test.txt (changed)
>]; } cc1:test -> cc2:test:w [label="update"]; cc2:test:e -> cc2new:test:w [label="(edit)"]; cc2new:test:e -> cc1new:test [label="commit"]; cc1:other -> cc1new:other; cc1:test -> cc1new:test; } }}} However, Mercurial does not version files independently: the repository as a whole is versioned. Consequently, if someone has changed a file in the repository, even if such changes are independent of changes you have made to another file, a conflict situation can arise. {{{ # Change a specific file, then commit only that file... hg commit test.txt # Now try and push the commit... hg push }}} This will cause a complaint about multiple heads. {{{#!dot digraph G { subgraph { rank=same cc1 [shape="box"; label=<
other.txt
test.txt
>]; cc1new [shape="box"; label=<
other.txt (changed)
test.txt
>]; } subgraph { rank=same label1 [label="remote repository",shape="none"]; cc1new2 [shape="box"; label=<
other.txt
test.txt (changed)
>]; } subgraph { rank=same label2 [label="local clone",shape="none"]; cc2 [shape="box"; label=<
other.txt
test.txt
>]; cc2new [shape="box"; label=<
other.txt
test.txt (changed)
>]; } label1 -> label2 [penwidth="0",arrowsize="0"]; cc1new:s -> cc1new2:n [penwidth="0",arrowsize="0"]; working [label="working files"]; cc1 -> cc2 [label="pull"]; cc2 -> working:w [label="update"]; working:n -> cc2new [label="commit"]; cc2 -> cc2new; cc1 -> cc1new; cc1 -> cc1new2 [color="#bbbbbb"]; cc2new:n -> cc1new2:s [label="push"]; } }}} In order to have the changed files co-exist in a single repository revision, a pull and merge is necessary, as described above. === Working independently === With CVS it is not possible to perform various version control operations without getting the remote repository involved, but with Mercurial you can keep working and versioning your own work without needing to pay any attention to what others might be doing. If you perform an `hg pull` but not an `hg update` you can check what other people have been doing, but your own work will not be affected by their changes. {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; before1 [label="..."]; start1 [label="start"]; } subgraph { node [shape="box"] rank=same label2 [label="local clone",shape="none"]; before2 [label="..."]; sc [shape="none"; label=<
start
committed
>]; } label1 -> label2 [penwidth="0",arrowsize="0"]; checkout [label="working files"]; before1 -> start1; before2 -> sc:start2; before2 -> sc:committed2; start1 -> sc:start2 [label="pull"]; before2 -> checkout:w [label="(previous) update"]; checkout:e -> sc:committed2 [label="commit"]; } }}} Ultimately you are likely to want to combine your own work with theirs, so an [[Update|update]] will need to happen at some stage. If you do not perform an `hg push` after committing changes, you remain free to keep committing as many [[ChangeSet|changesets]] as you like without being affected by people working elsewhere. {{{#!dot digraph G { subgraph { node [shape="box"] rank=same label1 [label="remote repository",shape="none"]; start1 [label="start"]; competing1 [label="competing"; color="red"]; } subgraph { node [shape="box"] rank=same label2 [label="local clone",shape="none"]; start2 [label="start"]; committed2 [label="committed"]; committed3 [label="..."]; } subgraph { rank=same checkout [label="working files"]; checkout2 [label="..."]; } label1 -> label2 [penwidth="0",arrowsize="0"]; start1 -> competing1; start2 -> committed2; committed2 -> committed3; start1 -> start2 [label="pull"]; start2 -> checkout:w [label="update"]; checkout:n -> committed2 [label="commit"]; checkout:e -> checkout2:w; checkout2:e -> committed3 [label="commit"]; } }}} == Branches and merging == To set up a branch, you can clone a repository and treat one of the clones as the main line and the other as a branch: {{{ # Outside the project directory. hg clone project project_maintenance # Or inside the project directory. hg clone . ../project_maintenance }}} {{{#!dot digraph G { rankdir=LR project -> project_maintenance [label="clone"]; } }}} Here, the `project` clone might act as the main line and the `project_maintenance` clone as a branch. Someone may be responsible for "backporting" changes from a branch to the main line. They do this by pulling changes from the branch and the main line into a local repository, merging appropriately, then pushing back to the main line. {{{#!dot digraph G { subgraph { trunk [label="main line"]; branch [label="branch"]; rank=same } subgraph { carlos [label="Carlos' clone"]; rank=same } branch -> carlos [label="pull"]; trunk -> carlos [label="pull"]; carlos -> trunk [label="push"]; } }}} When the main line reaches a release point, someone creates a clone on the server at the appropriate revision, and people who need to work on that branch clone it, then start pushing their changes back. {{{#!dot digraph G { subgraph { trunk [label="main line"]; branch [label="branch"]; rank=same } subgraph { david [label="David's clone"]; edward [label="Edward's clone"]; rank=same } trunk -> branch [label="clone"]; branch -> david [label="clone"]; branch -> david [label="pull"]; david -> branch [label="push"]; branch -> edward [label="clone"]; branch -> edward [label="pull"]; edward -> branch [label="push"]; } }}} == See also == * [[CvsCommands|CVS commands]] and their equivalents in Mercurial * [[CvsInfo|Information for CVS users]]