Background
Merging is not yet part of the Vesta core tools. However, there is a prototype merge script that has been in development for a while and is actively used by a variety of people. This page gives some advice on how best to plan your work around merging and how to avoid potential problems.
In order to understand this page, you should already know the basics of Vesta and the repository tools. If you don't know:
The difference between a session directory and a working directory,
The difference between advancing a snapshot and checking in a new version,
Then you should probably back up and start with LearningVesta, or at least make sure you understand the diagrams on the man pages for vcreate, vbranch, vcheckout, vadvance, and vcheckin.
For even more information on merging in Vesta, you may also want to look at MergingFuture.
For ideas about ways to use branching and merging that are independent of the version control system, you might find Streamed Lines to be useful.
Merging Advice
This section lists some guidelines which can help you avoid trouble. They're not hard and fast rules, by they are useful rules of thumb.
Resolve Conflicts Right Away
When you perform a merge operation, the merge tool may report conflicts. These are cases where it felt it could not determine the right choice without human intervention. Whenever a merge operation results in conflicts, you should resolve them right away. You should always resolve conflicts before you:
- Check in a new version
- Perform another merge
- Perform a build
Many merge tools (and certainly the vmerge script) handle conflicts in text files by placing conflict markers around sections of the files. Conflict markers usually look like this:
<<<<<<< changes made on one side ======= mofidications performed in another branch >>>>>>>
This allows you to use you favorite text editor to examine the conflicts and figure out how choose between, combine, or otherwise reconcile them.
The main reason to resolve conflicts right away is to avoid confusion for humans.
- If you perform a build using files containing conflicts, you will probably get strange errors as conflict markers are usually not syntactically valid.
- If you check in a version containing conflicts, you are probably checking in a version that could be considered broken, non-functional, or otherwise wrong. Most people would consider this poor version control practice.
- If you perform another merge without first resolving conflicts, you may get interleaved or overlapping conflict regions.
- The merge algorithm will not treat the conflict markers specially. They will appear to it like any other edit a human might make: as lines of text added to the file. The merge algorithm will not be confused by them. It will happily produce conflicts over and through the middle of existing conflicts. This will probably make it much harder for a human to figure out what happened and resolve the conflicts.
Merge With Checked-In Versions
When choosing the version to merge into your working directory, you should use a checked in version if at all possible rather than a snapshot in a session directory. Merging with checked in versions will help avoid conflicts.
Snapshots in session directories are not part of the normal progression of version history. To a merge tool, they appear to be separate changes off to the side. Version N of a package is based on version N-1 of the package. A snapshot in a session directory is based on a version chosen at checkout time, but usually nothing is based on such snapshots.
The following diagram illustrates what the version history looks like to a merge tool. Main-line versions (which are good to merge with) are shown in blue, and snapshots in session directories (which you should avoid merging with) are shown in red.
The important thing to remember about this difference is that merging with snapshots in session directories makes conflicts on future merges more likely. See SessionHistoryMentalModel for more about this.
The obvious exception to this is if you are merging in changes made in a non-exclusive checkout (aka scribble). This is fine as long as you only merge from a single snapshot in the session. Normally, you would merge the last snapshot in the session and you would delete the working directory after performing the merge operation.
Either Merge or Edit During Each Checkout, but not Both
It's easier for everyone to understand the version history if each checkin contains either a new change made by the user or a merge operation. When you mix the two, it can be difficult to make sense of what happened.
If you have an active checkout with edits you've been making and you want to perform a merge, check in your changes first and then check out again before merging.
If you've just done a merge but you're not 100% satisfied with the merged version (maybe you spotted a typo or a logical/semantic error), check in before making further edits. Of course if the merge produced any conflicts you should resolve those first before checking in. Also, if the merged version fails to compile or pass tests, you should probably make edits to resolve those problems before checking in. (In some cases it may be a bit of a judgment call whether an edit is resolving a conflict or other post-merge problem or making a new edit.)
Limit to One Merge per Checkout
It's easier to understand the version history if you perform and most one merge in each checkout operation. It is possible to merge multiple merges between checking out and checking in, but it's best to avoid doing so.
Suppose you have two separate changes in two separate branches and you want to merge both of them into the main-line of your package. This is what you should do:
- Check out the package main line
- Merge in the changes from first branch
- Resolve any merge conflicts
- Check in the merged version (creating a new version on the main line)
- Check out the package main line again
- Merge in the changes from second branch
- Resolve any merge conflicts
- Check in the merged version (creating a new version on the main line)
Another reasonable approach would be:
- Check out the first branch
- Merge in the changes from second branch
- Resolve any merge conflicts
- Check in the merged version (creating a new version on the first branch)
- Check out the package main line
- Merge in the changes from first branch (which now includes the changes from the second branch)
- Resolve any merge conflicts
- Check in the merged version (creating a new version on the main line)
Never Merge From Multiple Sanpshots in a Single Checkout Session
If you must merge a snapshot from a checkout session into your working copy, never merge with any other snapshot from that session directory. Merging with multiple snapshots from the same session directory will make conflicts very likely.
This is why we recommend that if you are merging in a change from a non-exclusive checkout (sometimes called a "scribble"), that you only merge in the last snapshot in the session. It might even be a good idea to remove the working directory before you merge.
It's tempting to think of a non-exclusive checkout as a micro-branch, but there are important differences between branches and non-exclusive checkouts. If your change will go take some time and go through multiple revisions, you may want to use an actual branch (created with vbranch). Again, see SessionHistoryMentalModel for more about this.
Use Throw-away Merges When Violating These Guidelines
There may be times when you have a legitimate need to violate these guidelines. Perhaps you want to try combining several changes that are still works in progress to see how many conflicts they will cause or even to try compiling and testing a preliminary combination. If you want to do that, do it in a non-exclusive checkout that you use temporarily and then discard.
As an example, suppose we have several users working on different changes:
Alice is working on the next main-line version of a package. Her latest snapshot is pkg/checkout/15/26.
Bob has a change he's been working on in a branch. His latest version is pkg/12.bugfix/3.
Charlie has a small change he's started working on in a non-exclusive checkout (since Alice already had version 15 reserved). His latest snapshot is pkg/checkout/14.csmith_example.com.1/32.
Another user, Dave, is going to be responsible for integrating these changes when the three users finish with their work. He wants to find out how much trouble he's going to have by merging the current state of these three changes. He performs a non-exclusive checkout:
% vchekcout -N /vesta/example.com/foo/pkg Creating session /vesta/example.com/foo/pkg/checkout/14.djones_example.com.1 Making working directory /vesta-work/djones/pkg
He then merges in each of the three versions:
Merging pkg/checkout/15/26 produces no conflicts (since Dave made no changes and the basis version is the same)
Merging pkg/12.bugfix/3 may produce some conflicts, which Dave must resolve
Merging pkg/checkout/14.csmith_example.com.1/32 may produce some conflicts, which Dave must resolve
After these merges Dave builds and tests the changes, possibly going through more edits. He notes his final snapshot (pkg/checkout/14.djones_example.com.1/6) which may help remind him later how he dealt with certain differences which may come up when he does the real merges. Then he deletes /vesta-work/djones/pkg.
Specific Usage Plans
This section suggests a couple ways to use merging that help you follow the above guidelines and keep out of trouble.
Work in Branch, Then Merge Up
When you start working on a change, create a branch. Check it out, make changes, and check it in. Build and test as appropriate. Repeat until you (and anyone else involved) are satisfied with the change.
When you're satisfied with your branch, check out the main-line of your package, merge in the latest version from your branch, resolve any conflicts, and check in a new main-line version.
In the following illustration of this method, the branch is shown in turquoise, and the merge operation is shown as a dashed red arrow.
This is the pattern we follow for development of Vesta itself.
Merge Forward to New Branch
If the main-line gets significantly ahead of the point where your branch started, you can merge newer main-line versions down into your branch.
However you might find that it's easier to understand the version history if you create a new branch and merge the changes from your older branch forward.
A Note About Sub-Branches
Since you can create branches inside branches, you could use this to further sub-divide really complex changes and merge them up just one level (from a sub-branch into a branch).
Work in Non-Exclusive Checkout, Then Merge Up
When you start working on a change, do it in a new non-exclusive checkout (aka scribble). When you're satisfied with your changes, take a final snapshot of your working directory and remove it. Then exclusively check out the main-line of your package, merge in the latest snapshot from your non-exclusive checkout, resolve any conflicts, and check in the new main-line version.
This model is essentially the way CVS works.
Merge Forward to New Non-Exclusive Checkout
If the main-line gets significantly ahead of the point where your non-exclusive checkout started, you may want to create a new non-exclusive checkout and merge your changes forward into it.
(Note that in the above diagram every snapshot in the session directory of the second non-exclusive checkout is shown as being merged with the last snapshot in the first. That's how the merges would be recorded, and that's how the merge algorithm would perceive the history. Again, see SessionHistoryMentalModel for more about this.)
You can also merge newer main-line versions into your non-exclusive checkout, but you will probably find it easier to understand the version history if you merge your changes forwared into a new non-exclusive checkout.
A Small Cautionary Note
Automatic merging is an attempt to implement a "do what I mean" algorithm. We endeavor to make such tools involve the user with merge conflicts when appropriate. However, there's an inherent tension between the desire to automatically handle as much as possible without involving the user and the desire to avoid mistakes by asking the user to resolve conflicts.
While the results of a conflict-free merge are often considered correct by users, you probably shouldn't trust them implicitly. You should use at least some method for testing their correctness such as:
- Compiling or otherwise processing the merge result through software which will reject invalid inputs
- Performing other tests on the merge result, such as running test cases using merged source code
Of course, it never hurts to read the diff of the changes made by a merge before checking in.