Merging branches

Merging branches is a complex operation in any version control system. The merge operation in Bazaar combines the changes of another branch into the current branch, and copies all the revision metadata including the historical ordering information as well.

Performing a three-way merge

The default merge algorithm used by Bazaar is the so-called three-way merge. The first step in this algorithm is identifying the base revision of the two branches; that is, the point after which the branches have diverged. For example, given the following two branches, the base revision is 2, which is also the common ancestor of the two branches:

Performing a three-way merge

The general logic of the three-way merge algorithm is to take the base revision as the starting state, then automatically apply the non-conflicting changes that took place in the two branches. For example, if a file was edited only by the first branch or the second branch but not by both, the changed file will be included in the result regardless of which branch it was edited in.

In case of text files that were edited by both the branches, the same logic can be applied by evaluating the changed blocks within the file. For example, if we take the original line Lorem ipsum at revision 2 and the same line at the latest revisions in the two branches, then the three-way merge algorithm works out the correct action as follows:

Line in base revision

Line in the first branch

Line in the second branch

Result

Comment

Lorem ipsum

Lorem ipsum

Lorem ipsum

Lorem ipsum

No change

Lorem ipsum

Dolor sit amet

Lorem ipsum

Dolor sit amet

Use line from first

Lorem ipsum

Lorem ipsum

Ante ipsum

Ante ipsum

Use line from second

Lorem ipsum

Est ipsum

Est ipsum

Est ipsum

Same change in both

Lorem ipsum

Dolor sit amet

Ante ipsum

?

Conflict

That is, if none of the branches changed the line, then the line is kept unchanged. If only one branch changed the line, the algorithm simply takes the changed line, regardless of the branch that changed it. If both the branches changed the line the same way, use the changed line.

However, if both the branches changed the same line in a different way, then the algorithm cannot decide what is the right thing to do. So, it marks the file as conflicted, which must be resolved manually by editing the file and correcting it.

The same is true for other kinds of changes; for example, the renaming of a file. If only one of the branches renamed a file, then the file will be renamed in the result. If both the branches renamed the file differently, then the merge operation will mark that as a conflict that must be resolved manually by choosing the correct name.

Completing the merge

As a result of the merge, the changes in the unique revisions of the other branch are applied to the working tree, leaving it in a changed state. You can use the usual Bazaar commands to view what has changed, such as bzr status, bzr diff, bzr log, and their equivalents in Bazaar Explorer.

As nothing is committed yet, you have at least two options:

  • Commit the changes to record a new revision
  • Abort the merge and restore the working tree

Committing the merge

If the merge operation was successful and there were no conflicts, you should review the changes carefully. Even though there were no conflicts during the merge operation, the changes in the other branch might have important implications for the current branch, warranting adjustments. For this reason, it is important to understand the changes introduced by the other branch.

After you have reviewed the changes and tested the new state of the project, you can commit the changes as a new revision in the same way as always. In addition to the changes, Bazaar will also record all the meta information about the merged revisions in the other branch.

As a result, the current branch will have all the information of both the branches combined, and you will be able to reconstruct both the original branches as they were before the merge operation, if necessary in the future.

Aborting the merge

If the merge operation was not successful, for example if it resulted in too many conflicts, or you are simply not happy with the new state of the project, then you can abort the merge by reverting the changes. As always, you can revert the changes selectively by specifying files and directories, or revert all the changes at once.

Reverting all the changes will completely abort the merge and restore the working tree to the state of the last revision in the current branch.

If you revert the files selectively, keep in mind that during a merge operation Bazaar keeps a pending merge marker with the meta information of the merged revisions. If you commit in such a state, Bazaar will still consider that as a merge. You can revert the pending merge marker using the --forget-merges flag.

Resolving conflicts

When two branches have made contradicting changes, it is impossible to determine automatically which change should take precedence, or to combine the changes in a meaningful way. In such situations, Bazaar marks the involved files as conflicted, and you must resolve them manually.

It is easy to think of contradictory changes. For example:

  • BranchA and BranchB changed the same line of the same file differently
  • BranchA and BranchB renamed the same file differently
  • BranchA changed something in a file, but BranchB deleted that file
  • BranchA added a file to a directory, but the directory was deleted in BranchB
  • BranchA added a new file, but BranchB also added a file with the same name but with different content

These are just a few examples of contradictory changes that can happen in two branches and cannot be resolved automatically. You must investigate why such contradicting changes took place in the branches, and decide the right resolution that is appropriate and logical for the project.

When Bazaar detects a conflict, it marks the files involved in the change as conflicted. You can view the list of conflicts by using the bzr conflicts command. As long as there are unresolved conflicts in the working tree, Bazaar will not let you commit a new revision.

Bazaar categorizes conflicts into conflict types, as documented in bzr help conflict-types. Regardless of the conflict type, resolving a conflict involves two main steps:

  1. Make the necessary changes in the working tree to fix its state, typically by combining the changes of the two branches in a meaningful way, or sometimes by taking the changes of only one branch and ignoring the other.
  2. Tell Bazaar that the conflict is resolved in order to clear the conflict marker.

Let's demonstrate how this works by using two common conflict types as examples—text conflicts and content conflicts.

Resolving text conflicts

When both the branches changed the same line of the same text file in a different way, it is called a text conflict. Let's create such a scenario using the following steps:

  1. Create two new branches, namely text1 and text2, based on the trunk.
  2. In text1, edit the hello.c file, change Hello World to Hi World, and commit the change.
  3. In text2, edit the hello.c file, change Hello World to HELLO World, and commit the change.
  4. In text2, merge from the text1 branch.

Using the command line as follows:

$ bzr branch trunk/ text1
Branched 7 revisions.                                                          
$ bzr branch trunk/ text2
Branched 7 revisions.
$ cd text1
$ vim hello.c  # change Hello World to Hi World
$ bzr commit -m 'say Hi World'
Committing to: /sandbox/hello/text1/                                           
modified hello.c
Committed revision 8.
$ cd ../text2
$ vim hello.c  # change Hello World to HELLO World
$ bzr commit -m 'say HELLO World'
Committing to: /sandbox/hello/text2/                                           
modified hello.c
Committed revision 8.
$ bzr merge ../text1
 M  hello.c                                                                    
Text conflict in hello.c
1 conflicts encountered.

If we take a look at the status of the working tree, it will look similar to the following:

$ bzr status
modified:
  hello.c
unknown:
  hello.c.BASE
  hello.c.OTHER
  hello.c.THIS
conflicts:
  Text conflict in hello.c
pending merge tips: (use -v to see all merge revisions)
  Janos Gyerik 2013-03-10 say Hi World

If we use Bazaar Explorer to see the output, it will look as follows:

Resolving text conflicts

In addition to modifications in the hello.c file, Bazaar adds three new files with .BASE, .OTHER, and .THIS suffixes, and reports a text conflict. These three new files are different versions of hello.c that help us sort out the conflict:

  • hello.c.BASE: This is a copy of the file as of the base revision
  • hello.c.OTHER: This is a copy of the file as of the revision in the other branch
  • hello.c.THIS: This is a copy of the file as of the revision in the current branch

Let's look at the modifications in hello.c using Bazaar Explorer:

Resolving text conflicts

The markers "<<<<<<<" and ">>>>>>>" are called herringbone markers, and have been added by Bazaar to help compare the conflicting changes in two branches. The lines between <<<<<<< TREE and ======= are as they were in the current branch (text2), and the lines between ======= and >>>>>>> MERGE-SOURCE are as they were in the merge source (the other branch, text1). That is, the affected area in the file is changed following this general format:

<<<<<<< TREE
ZERO OR MORE LINES AS THEY EXIST
IN THE REVISION IN THE CURRENT BRANCH
=======
ZERO OR MORE LINES AS THEY EXIST
IN THE REVISION IN THE OTHER BRANCH (THE MERGE SOURSE)
>>>>>>> MERGE-SOURCE

One solution is to edit the file, remove the herringbone markers, and make the line look like the way it should, which depends upon the situation and cannot be decided automatically.

A graphical merge tool can help in reviewing and resolving the conflicts. To launch the graphical merge tool, you first need to open the Conflicts view in Bazaar Explorer. You can either click on the small red triangle next to the list of conflicts, or click on the large Work button in the toolbar and select Resolve Conflicts..., or with the bzr qresolve command:

Resolving text conflicts

The Conflicts view shows the list of conflicts. Click on a conflict to select it, use the Merge tool: combo box to choose a merge tool and click on Launch... to start it. Using the merge tool you can review the conflicting blocks inside the selected file. Depending upon the tool of your choice, you may be able to choose the changes from one of the branches, or freely edit the file using the .THIS, .OTHER, and .BASE versions as references; the available options and features depend on the tool.

Tip

If you made a mistake while editing the working tree to resolve the conflicts, you can always restart from the beginning by reverting and restarting the merge.

After correcting the file manually or with the help of a merge tool, you must tell Bazaar that the conflict is resolved using the bzr resolve command. For example:

$ bzr resolve hello.c
1 conflict resolved, 0 remaining

With this step, Bazaar clears the conflict marker, and it also removes the temporary .THIS, .OTHER, and .BASE files it created earlier.

Instead of resolving the conflicting changes one by one, sometimes the solution is to take the changes of one branch and ignore the conflicting changes of the other. The bzr resolve command provides convenient shortcuts for such situations by using the --take-this and --take-other flags:

  • --take-this: This resolves the conflict by using the version of the current branch, ignoring the conflicting changes by the other branch
  • --take-other: This resolves the conflict by using the version of the other branch, ignoring the conflicting changes by the current branch

Resolving content conflicts

When both the branches changed the same file in a conflicting way that is not a text conflict, it is called a content conflict. This can happen, for example, if a binary file was changed by both branches, or if a file was changed by one branch and deleted or renamed by the other.

Let's create such a scenario using the following steps:

  1. Create a new branch named content1 based on the trunk.
  2. Delete the hello.c file and commit the change.
  3. Merge the text1 branch we created in the previous example, which modified the hello.c file.

Using the command line:

$ bzr branch trunk/ content1
Branched 7 revisions.                                                          
$ cd content1/
$ bzr rm hello.c
deleted hello.c
$ bzr ci -m 'removed the c impl'
Committing to: /sandbox/hello/content1/                                        
deleted hello.c
Committed revision 8.
$ bzr merge ../text1
+N  hello.c.OTHER                                                              
Contents conflict in hello.c
1 conflicts encountered.

If we take a look at the status of the working tree, it will looks similar to the following:

$ bzr status
added:
  hello.c.OTHER
unknown:
  hello.c.BASE
conflicts:
  Contents conflict in hello.c
pending merge tips: (use -v to see all merge revisions)
  Janos Gyerik 2013-03-10 say Hi World

In this case, Bazaar did not add hello.c.THIS, as it would not make sense since we deliberately deleted the file in this branch. The hello.c.OTHER file is marked to be added. Perhaps you must rename this file and add it back under version control, otherwise you might lose the work on it that was done by the other branch. Again, hello.c.BASE is also created, so that you can compare hello.c.OTHER with it, and see what the other branch has changed in it.

This is a good example where the correct resolution is probably simply taking the changes of one branch and ignoring the other:

  • If it was a mistake to remove hello.c in our branch, then we can resolve the conflict by using bzr resolve --take-other, which will add hello.c back in the current branch, including the changes by the other branch.
  • If hello.c is to be considered obsolete and the other branch should not have worked on it, then we can resolve the conflict by using bzr resolve --take-this, which will simply ignore the change by the other branch.

Redoing the merge

Before you even begin trying to resolve the conflicts, it is a good idea to retry the merge using a different algorithm. One way to do this is to abort the merge and repeat the same merge command but with an option to specify a different merge algorithm. For example, --diff3, --lca, --weave, or --merge-type=ARG.

An easier way is to use the bzr remerge command. This command accepts the same options to select a merge algorithm as bzr merge, but it has a great advantage that you can specify a subset of the files to run on. In this way, you can try different merge algorithms depending upon what is best for a given file or a set of files. By running only for a subset of files, remerge is more efficient than aborting and re-applying a merge on the entire working tree, as it only needs to work with the selected files.

The merge algorithm that produces the best result varies from case to case. The weave algorithm is known to produce better results when two branches frequently merge from each other.

Another useful option is --reprocess, which tries to do additional processing to reduce the size of the conflict regions. It makes the merge command run slower, but usually it is worth a try.

Resolving other types of conflicts

The documentation in bzr help conflict-types explains all the conflict types you may see in Bazaar and how they could happen, and it provides hints to resolving them. The general logic is always the same:

  1. Check the status of the project, review the content of the files that are in conflict.
  2. Use the additional temporary files created by Bazaar to compare the files in different states—base revision, last state in this branch, or state in the other branch.
  3. Make the necessary changes in the working tree to fix the project.
  4. Use the --take-this and --take-other shortcuts to resolve the conflicts by accepting the changes from one branch and ignoring from others, if this makes sense in the given situation.
  5. Inform Bazaar about the conflicts that have been resolved.

If there are too many conflicts, you may want to re-do the merge using a different algorithm. At any time, you may abort the merge to restore the working tree to the last revision of the current branch and postpone the merge. Or, after resolving all conflicts, you may commit the merge to record a new revision.

Merging a subset of revisions

By default, the merge command tries to merge all the revisions of the specified source branch. However, in some cases, it can be useful to merge only a subset of the missing revisions.

Merging up to a specific revision

To merge only up to and including a particular revision of the source branch, specify that revision by using the -r or --revision options.

As an example, let's merge into the trunk from the say-hello-to-x branch at a particular revision. First, let's confirm the missing revisions in order to find a suitable revision:

$ cd /sandbox/hello/trunk
$ bzr missing ../say-hello-to-x/ --line --other
You are missing 2 revisions:
8: Janos Gyerik 2013-03-03 python impl can say hello to X
7: Janos Gyerik 2013-03-03 bash impl can say hello to X

Revisions 7 and 8 of the say-hello-to-x branch are missing in the trunk. Use the following command to merge only up to revision 7 and thus ignore revision 8:

$ bzr merge ../say-hello-to-x/ -r7                       
 M  README.md                                                                  
 M  hello.sh
All changes applied successfully.

Using the status command with the --verbose or -v flag, we can confirm that only revision 7 has been merged:

$ bzr status -v
modified:
  README.md
  hello.sh
pending merges:
  Janos Gyerik 2013-03-03 bash impl can say hello to X

Merging a range of revisions

If you specify a range of revisions by using -r BASE..OTHER or --revision BASE..OTHER, only the revisions through BASE to OTHER will be merged, excluding BASE but including OTHER. In the previous example, merging up to revision 7 is equivalent to specifying the range 6..7.

If BASE is a revision that does not exist in the current branch, then the historical relationship between the current branch and the specified branch segment cannot be determined. In such a case, the merge will be treated as "cherry-picking".

Cherry-picking

Cherry-picking is a merge operation, where the historical relationship between the target and the specified branches segment does not exist, or cannot be determined. This can happen, for example, when merging changes from an unrelated branch, or when merging from a related branch but specifying a range of revisions that does not include a revision that already exists in the destination branch.

Bazaar does not track the revisions of a cherry-pick merge; in other words, the individual revisions of the specified range will not be preserved, and the changes will be applied as an independent change-set.

Understanding revision numbers

The integer revision numbers are unique per branch, but not unique globally in the project. For example, after creating a branch at revision 100, the next revision in the original branch will be 101, and the next revision in the other branch (with completely different content) will also be 101:

Understanding revision numbers

After a branch is merged into the current branch, its revision history is preserved, but to keep revision numbers unique in the current branch, the merged revisions are renumbered using a dotted notation:

Understanding revision numbers

The format of revisions in the dotted notation is BASE.BRANCH.REV:

  • BASE: This is the base revision where the branch started from.
  • BRANCH: This is the sequence number of the branch, starting from 1, and incremented by 1 every time another branch is merged with the same base revision.
  • REV: This is the sequence number starting from 1 and incremented by 1 for each merged revision.

In the preceding example, the original revision numbers of the second branch were renamed as follows:

  • 101 → 100.1.1
  • 102 → 100.1.2
  • 103 → 100.1.3

This is because the base revision of the merged branch is 100, and since this is the first branch merged with this base revision, BASE = 100, BRANCH = 1, and the revision numbers are incremented as usual starting from 1.

If we were to merge another branch that also started from base revision 100, then the BRANCH number component in the renamed revisions will be 2, as shown in the following example:

Understanding revision numbers

Note that the merge operation does not affect the source branch. As illustrated on the graph, even after the merge operation, the second branch still exists, completely unaffected. Since its unique revisions have been merged into the first branch, we can safely delete the second branch.

If needed, you can recreate the deleted second branch by branching from the first branch at revision 100.1.3. The result will be a perfect replica of the second branch, with the revisions 100.1.1, 100.1.2, and 100.1.3 renamed back to 101, 102, and 103, respectively.

Merging from multiple branches

The merge operation in Bazaar works only with two branches at a time. However, you can merge multiple branches by merging them one by one, as follows:

$ cd /path/to/target/branch
$ bzr merge /path/to/source1
$ bzr merge /path/to/source2 --force
$ bzr commit

Notice that when merging the second branch, you must use the --force flag, because by default Bazaar refuses to merge when there are uncommitted changes in the working tree. Since the first merge leaves the working tree in a changed state, the --force flag is necessary to push through the second merge.

It is usually better to merge branches one by one in separate commits, as in this way the history will be cleaner, with the merge commits appearing as distinct larger steps in the evolution in the project.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.130.199