Differences between revisions 16 and 18 (spanning 2 versions)
Revision 16 as of 2011-02-08 18:30:15
Size: 8483
Editor: PaulBoddie
Comment: Added diagrams and headings.
Revision 18 as of 2011-02-10 18:15:35
Size: 10520
Editor: PaulBoddie
Comment: Added notes about updating and merging plus a branching diagram.
Deletions are marked like this. Additions are marked like this.
Line 23: Line 23:
== Working environment ==

Developers have direct SSH access (whether that is through "normal" SSH access with each developer having their own login, or through [[SharedSSH|shared SSH logins]]) to the central repositories on a server, and push their changes directly from their local clones into the appropriate repositories.
Line 25: Line 29:
Developers have direct SSH access (whether that is through "normal" SSH access with each developer having their own login, or through [[SharedSSH|shared SSH logins]]) to the central repositories on a server, and push their changes directly from their local clones into the appropriate repositories. Thus, the usual CVS practice would be as follows: With CVS, the practice would be as follows:
Line 48: Line 52:
This doesn't always work out, and so the following workflow is typically required if `cvs commit` complains:

{{{
# Rectify conflicts, then...
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...
Line 53: Line 65:
# CVS may complain about "conflicts during merge".
Line 65: Line 78:
  subgraph {
    rank=same
    checkout; checkout2 [label="..."; color="red"];
  }
Line 66: Line 83:
  competing -> checkout:n [label="update"];
  checkout:e -> committed [label="commit"];
  checkout:e -> checkout2:w;
competing -> checkout2:n [label="update"];
  checkout2:e -> committed [label="commit"];
Line 118: Line 136:
When you invoke a [[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.

=== Conflicts and merging in Mercurial ===

When pulling and [[Push|pushing]], `hg pull` and `hg push` may respectively complain about multiple [[Head|heads]] being created. 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.

{{{#!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` in a remote clone, but this leaves any work for reconciling the competing changes for later, which may not be desirable. If the remote clone is being used as a CVS-style "central repository" with 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
}}}

{{{#!dot
digraph G {
  subgraph {
    node [shape="box"]
    rank=same
    start1 [label="start"];
    competing1 [label="competing"; color="red"];
    committed1 [label="committed"];
  }
  subgraph {
    node [shape="box"]
    rank=same
    start2 [label="start"];
    cc [shape="none"; label=<
<table border='0' cellspacing='10' cellpadding='10'><tr><td port='competing2' border='1' color='red'>competing</td></tr><tr><td port='committed2' border='1'>committed</td></tr></table>
        >];
    committed3 [label="committed"];
  }
  subgraph {
    rank=same
    checkout [label="working files"];
    checkout2 [label="..."; color="red"];
  }
  start1 -> competing1 -> committed1;
  start1 -> start2 [label="pull"];
  start2 -> cc:competing2 -> committed3;
  start2 -> cc:committed2 -> committed3;
  start2 -> checkout:w [label="update"];
  checkout:n -> cc:committed2 [label="commit"];
  competing1 -> cc:competing2 [label="pull"];
  checkout:e -> checkout2:w;
  cc:competing2 -> checkout2 [label="merge"];
  checkout2:e -> committed3 [label="commit"];
  committed3 -> committed1 [label="push"];
}
}}}

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.
Line 120: Line 213:
If you perform the `hg pull` but not the `hg update` you can keep working and ignore other people's work, but you will be able to see what they did. 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.
Line 150: Line 245:
Note that before pushing changes, the developer is free to commit as many [[ChangeSet|changesets]] as they like without being affected by people working elsewhere. 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.
Line 183: Line 278:
=== Conflicts and merging in Mercurial ===

When pushing, `hg push` may complain about multiple heads being created which means that the content of the repository has been edited in different ways and has not been reconciled. Multiple heads can exist in the remote repository, but this leaves any work for reconciling the competing changes for later, which may not be desirable.
== 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
}}}
Line 190: Line 292:
  node [shape="box"]
  start;
  subgraph {
    rank=same
    competing [color="red"];
    committed;
  }
  start -> competing;
  start -> committed;
}
}}}

To reconcile competing changes, the following workflow becomes necessary:

{{{
# Get the remote changes and merge them...
hg pull
hg merge
# Edit any conflicts, then commit...
hg commit
# Push merged changes...
hg push
}}}

{{{#!dot
digraph G {
  subgraph {
    node [shape="box"]
    rank=same
    start1 [label="start"];
    competing1 [label="competing"; color="red"];
    committed1 [label="committed"];
  }
  subgraph {
    node [shape="box"]
    rank=same
    start2 [label="start"];
    cc [shape="none"; label=<
<table border='0' cellspacing='10' cellpadding='10'><tr><td port='competing2' border='1' color='red'>competing</td></tr><tr><td port='committed2' border='1'>committed</td></tr></table>
        >];
    committed3 [label="committed"];
  }
  subgraph {
    rank=same
    checkout [label="working files"];
    checkout2 [label="..."];
  }
  start1 -> competing1 -> committed1;
  start1 -> start2 [label="pull"];
  start2 -> cc:competing2 -> committed3;
  start2 -> cc:committed2 -> committed3;
  start2 -> checkout:w [label="update"];
  checkout:n -> cc:committed2 [label="commit"];
  competing1 -> cc:competing2 [label="pull"];
  checkout:e -> checkout2:w;
  cc:competing2 -> checkout2 [label="update"];
  checkout2:e -> committed3 [label="commit"];
  committed3 -> committed1 [label="push"];
}
}}}

Again, the developer can defer pushing changes until later. The principal advantage of Mercurial here is that the developer need not be confronted with merging others' changes on every commit.

== Branches and merging ==
  project -> project_maintenance [label="clone"];
}
}}}

Here, the `project` clone might act as the main line and the `project_maintenance` clone as a branch.

CVS-like Working Practice

Mercurial allows multiple 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".

Repositories tend to be long-lived, and the "authoritative branches" are clones of the central repository.

Working environment

Developers have direct SSH access (whether that is through "normal" SSH access with each developer having their own login, or through shared SSH logins) to the central repositories on a server, 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

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

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

The single command equivalent of cvs update is really...

hg pull -u

When you invoke a pull operation, Mercurial will invite you to update to the pulled 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 both sets of changes. This is more or less what CVS does in this situation.

Conflicts and merging in Mercurial

When pulling and pushing, hg pull and hg push may respectively complain about multiple heads being created. 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.

Multiple heads can exist in a repository, either after hg pull in a local clone or after hg push -f in a remote clone, but this leaves any work for reconciling the competing changes for later, which may not be desirable. If the remote clone is being used as a CVS-style "central repository" with 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

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.

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.

Ultimately you are likely to want to combine your own work with theirs, so an 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 changesets as you like without being affected by people working elsewhere.

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

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.

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.

See also

CvsLikePractice (last edited 2012-06-20 16:49:32 by PaulBoddie)