Chapter 9
Branching
Perforce's Inter-File BranchingTM mechanism allows any set of files to be copied within the depot, and allows changes made to one set of files to be copied, or integrated, into another. By default, the new file set (or codeline) evolves separately from the original files, but changes in either codeline can be propagated to the other with the p4 integrate command.
What is Branching?
Branching is a method of keeping two or more sets of similar (but not identical) files synchronized. Most software configuration management systems have some form of branching; we believe that Perforce's mechanism is unique because it mimics the style in which users create their own file copies when no branching mechanism is available.
Suppose for a moment that you're writing a program and are not using an SCM system. You're ready to release your program: what would you do with your code? Chances are that you'd copy all your files to a new location. One of your file sets would become your release codeline, and bug fixes to the release would be made to that file set; your other files would be your development file set, and new functionality to the code would be added to these files.
What would you do when you find a bug that's shared by both file sets? You'd fix it in one file set, and then copy the edits that you made into the other file set.
The only difference between this homegrown method of branching and Perforce's branching methodology is that Perforce manages the file copying and edit propagation for you. In Perforce's terminology, copying the files is called making a branch. Each file set is known as a codeline, and copying an edit from one file set to the other is called integration. The entire process is called branching.
When to Create a Branch
Create a branch when two sets of code files have different rules governing when code can be submitted, or whenever a set of code files needs to evolve along different paths. For example:
- The members of the development group want to submit code to the depot whenever their code changes, whether or not it compiles, but the release engineers don't want code to be submitted until it's been debugged, verified, and signed off on.
- They would branch the release codeline from the development codeline. When the development codeline is ready, it is integrated into the release codeline. Afterwards, patches and bug fixes are made in the release code, and at some point in the future, integrated back into the development code.
- A company is writing a driver for a new multi-platform printer. It has written a UNIX device driver, and is now going to begin work on a Macintosh driver using the UNIX code as their starting point.
- The developers create a branch from the existing UNIX code, and now have two copies of the same code. These two codelines can then evolve separately. If bugs are found in either codeline, bug fixes can be propagated from one codeline to the other with the Perforce p4 integrate command.
One basic strategy is to develop code in //depot/main/ and create branches for releases (for example, //depot/rel1.1/). Make bug fixes that affect both codelines in //depot/main/ and integrate them into the release codelines. Make release-specific bug fixes in the release branch and, if required, integrate them back into the //depot/main/ codeline.
Perforce's Branching Mechanisms: Introduction
Perforce provides two mechanisms for branching. One method requires no special setup, but requires the user to manually track the mappings between the two sets of files. The second method remembers the mappings between the two file sets, but requires some additional work to set up.
In the first method, the user specifies both the files that changes are being copied from and the files that the changes are being copied into. The command looks like this:
p4 integrate fromfiles tofiles
In the second method, Perforce stores a mapping that describes which set of files get branched to other files, and this mapping, or branch specification, is given a name. The command the user runs to copy changes from one set of files to the other looks like this:
p4 integrate -b branchname [tofiles]
These methods are described in the following two sections.
Branching and Merging, Method 1:
Branching with File Specifications
Use p4 integrate fromfiles tofiles to propagate changes from one set of files (the source files) to another set of files (the target files). The target files need to be contained within the current client workspace view. The source files do not need to be, so long as the source files are specified in depot syntax. If the target files do not yet exist, the entire contents of the source files are copied to the target files. If the target files have already been created, changes can be propagated from one set of files to the other with p4 resolve. In both cases, p4 submit must be used to store the new file changes in the depot. Examples and further details are provided below.
Creating branched files
To create a copy of a file that will be tracked for branching, use the following procedure:
- Determine where you want the copied (or branched) file(s) to reside within the depot and within the client workspace. Add the corresponding mapping specification to your client view.
- Run p4 integrate fromfiles tofiles. The source files are copied from the server to target files in the client workspace.
- Run p4 submit. The new files are created within the depot, and are now available for general use.
Example:
Creating a branched file.
Version 2.0 of Elm has just been released, and work on version 3.0 is about to commence.
Work on the current development release always proceeds in //depot/elm_proj/..., and
it is determined that maintenance of version 2.0 will take place in //depot/elm_r2.0/...
The files in //depot/elm_proj/... need to be branched into //depot/elm_r2.0/...,
so Ed does the following:
He decides that he'll want to work on the new //depot/elm_r2.0/... files within his
client workspace at /usr/edk/elm_proj/r2.0. He uses p4 client to add the following
mapping to his client view:
//depot/elm_r2.0/... //eds_elm/r2.0/...
p4 integrate //depot/elm_proj/... //depot/elm_r2.0/...
which copies all the files under //depot/elm_proj/... to //eds_elm/v2.0 in his client
workspace. Finally, he runs p4 submit, which adds the new branched files to the depot.
Why not just copy the files?
Although it is possible to accomplish everything that has been done thus far by copying the files within the client workspace and using p4 add to add the files to the depot, when you use p4 integrate, Perforce is able to track the connections between related files in an integration record, allowing easy propagation of changes between one set of files and another.
Branching not only enables you to more easily track changes, it creates less overhead on the server. When you copy files with p4 add, you create two copies of the same file on the server. When you use branching, Perforce performs a "lazy copy" of the file, so that the depot holds only one copy of the original file and a record that a branch was created.
Propagating changes between branched files
After a file has been branched from another with p4 integrate, Perforce can track changes that have been made in either set of files and merge them using p4 resolve into the corresponding branch files. (You'll find a general discussion of the resolve process in Chapter 5, Perforce Basics: Resolving File Conflicts. File resolution with branching is discussed in "How Integrate Works" on page 108).
The procedure is as follows:
- Run p4 integrate fromfiles tofiles to tell Perforce that changes in the source files need to be propagated to the target files.
- Use p4 resolve to copy changes from the source files to the target files. The changes are made to the target files in the client workspace.
- Run p4 submit to store the changed target files in the depot.
Example:
Propagating changes between branched files.
Ed has created a release 2.0 branch of the Elm source files as above, and has fixed a bug in the
original codeline's src/elm.c file. He wants to merge the same bug fix to the release 2.0
codeline. From his home directory, Ed types
p4 integrate elm_proj/src/elm.c //depot/elm_r2.0/src/elm.c
//depot/elm_r2.0/src/elm.c#1 - integrate from //depot/elm_proj/src/elm.c#9
The file has been scheduled for resolve. He types p4 resolve, and the standard merge dialog
appears on his screen.
/usr/edk/elm_r2.0/src/elm.c - merging //depot/elm_proj/src/elm.c#2
Diff chunks: 0 yours + 1 theirs + 0 both + 0 conflicting
Accept(a) Edit(e) Diff(d) Merge (m) Skip(s) Help(?) [at]:
He resolves the conflict with the standard use of p4 resolve. When he's done, the result file
overwrites the file in his branched client, and it still must be submitted to the depot.
There is one fundamental difference between resolving conflicts in two revisions of the same file, and resolving conflicts between the same file in two different codelines. The difference is that Perforce will detect conflicts between two revisions of the same file and then schedule a resolve, but there are always differences between two versions of the same file in two different codelines, and these differences usually don't need to be resolved manually. (In these cases, a p4 resolve -as or p4 resolve -am to accept the Perforce-recommended revision is usually sufficient. See "Using Flags with Resolve to Automatically Accept Particular Revisions" on page 71 for details.)
In their day-to-day use, there is no difference between branched files and non-branched files. The standard Perforce commands like sync, edit, delete, submit, and so on. are used with all files, and evolution of both codelines proceeds separately. When changes to one codeline need to be propagated to another, you must tell Perforce to do this with p4 integrate, but if the codelines evolve separately, and changes never need to be propagated, you'll never need to integrate or resolve the files in the two codelines.
Propagating changes from branched files to the original files
A change can be propagated in the reverse direction, from branched files to the original files, by supplying the branched files as the source files, and the original files as the target files.
Example:
Propagating changes from branched files to the original files.
Ed wants to integrate some changes in //depot/elm_r2.0/src/screen.c file to the original
version of the same file. He types
p4 integrate //depot/elm_r2.0/src/screen.c //depot/elm_proj/src/screen.c
and then runs p4 resolve. The changes in the branched file can now be merged into his
source file.
Branching and Merging, Method 2:
Branching with Branch Specifications
To map a set of source files to target files, you can create a branch specification and use it as an argument to p4 integrate. To create and use a branch specification, do the following:
- Use p4 branch branchname to create a view that indicates which target files map to which source files.
- Make sure that the new files and directories are included in the p4 client view of the client workspace that will hold the new files.
- Use p4 integrate -b branchname to create the new files.
- To propagate changes from source files to target files, use p4 integrate -b branchname [tofiles]. Perforce uses the branch specification to determine which files the merged changes come from
- Use p4 submit to submit the changes to the target files to the depot.
The following example demonstrates the same branching that was performed in the example above, this time using a branch specification.
Example:
Creating a branch.
Version 2.0 of Elm has just been released, and work on version 3.0 is about to commence.
Work on the current development release always proceeds in //depot/elm_proj/..., and
it is determined that maintenance of version 2.0 will take place in //depot/elm_r2.0/...
The files in //depot/elm_proj/... need to be branched into //depot/elm_r2.0/...,
so Ed does the following:
Ed creates a branch specification called elm2.0 by typing p4 branch elm2.0. The
following form is displayed:
Branch: elm2.0 Date: 1997/05/25 17:43:28 Owner: edk Description: Created by edk. View: //depot/... //depot/...
|
The view maps the original codeline's files (on the left) to branched files (on the right). Ed
changes the View: and Description: fields as follows:
Branch: elm2.0 Date: 1997/05/25 17:43:28 Owner: edk Description: Elm release 2.0 maintenance codeline View: //depot/elm_proj/... //depot/elm_r2.0/...
|
Ed wants to work on the new //depot/elm_r2.0/... files within his client workspace at
/usr/edk/elm_proj/r2.0. He uses p4 client to add the following mapping to his client
view: //depot/elm_r2.0/... //eds_elm/r2.0/...
He runs p4 integrate -b elm2.0, which copies all the files under
//depot/elm_proj/... to //eds_elm/r2.0/... in his client workspace; then he runs
p4 submit, which adds the newly branched files to the depot.
Once the branch has been created and the files have been copied into the branched codeline, changes can be propagated from the source files to the target files with p4 integrate -b branchname.
Example:
Propagating changes to files with p4 integrate.
A bug has been fixed in the original codeline's src/elm.c file. Ed wants to propagate the
same bug fix to the branched codeline he's been working on. He types
p4 integrate -b elm2.0 ~edk/elm_r2.0/src/elm.c
//depot/elm_r2.0/src/elm.c#1 - integrate from //depot/elm_proj/src/elm.c#9
The file has been scheduled for resolve. He types p4 resolve, and the standard merge dialog
appears on his screen.
/usr/edk/elm_r2.0/src/elm.c - merging //depot/elm_proj/src/elm.c#9
Diff chunks: 0 yours + 1 theirs + 0 both + 0 conflicting
Accept(a) Edit(e) Diff(d) Merge (m) Skip(s) Help(?) [at]:
He resolves the conflict with the standard use of p4 resolve. When he's done, the result file
overwrites the file in his branched client, and it still must be submitted to the depot.
Branch Specification Usage Notes
|
- Creating or altering a branch specification has absolutely no immediate effect on any files within the depot or client workspace. The branch specification merely specifies which files are affected by subsequent p4 integrate commands.
- Branch specifications may contain multiple mappings, just as client views can. For example, the following branch specification branches the Elm 2.0 source code and documents to two separate locations within the depot:
Branch: elm2.0 Date: 1997/05/25 17:43:28 Owner: edk Description: Elm release 2.0 maintenance codeline View: //depot/elm_proj/src/... //depot/elm_r2.0/src/... //depot/elm_proj/docs/... //depot/docs/2.0/...
|
- Exclusionary mappings can be used within branch specifications.
- To reverse the direction of an integration that uses a branch specification, use the -r flag.
Integration Usage Notes
|
- p4 integrate only acts on files that are the intersection of target files in the branch view and the client view. If file patterns are given on the command line, integrate further limits its actions to files matching the patterns. The source files supplied as arguments to integrate need not be in the client view.
- The basic syntax of the integrate command when using a branch specification is:
p4 integrate -b branchname [tofiles]
- If you omit the tofiles argument, all the files in the branch are affected.
- The direction of integration through a branch specification may be reversed with the -r flag. For example, to integrate changes from a branched file to the original source file, use p4 integrate -b branchname -r [tofiles]
- The p4 integrate command, like p4 add, p4 edit, and p4 delete, does not actually affect the depot immediately; instead, it adds the affected files to a changelist, which must be submitted with p4 submit. This keeps the integrate operation atomic: either all the named files are affected at once, or none of them are.
- The actual action performed by p4 integrate is determined by particular properties of the source files and the target files:
- If the target file doesn't exist, the source file is copied to target, target is opened for branch, and Perforce begins tracking the integration history between the two files. The next integration of the two files will treat this revision of source as base.
- If the target file exists, and was originally branched from the source file with p4 integrate, then a three-way merge is scheduled between target and source. The base revision is the previously integrated revision of source.
- If the target file exists, but was not branched from the source, then these two file revisions did not begin their lives at a common, older file revision, so there can be no base file, and p4 integrate rejects the integration. This is referred to as a baseless merge. To force the integration, use the -i flag; p4 integrate will use the first revision of source as base. (Actually, p4 integrate uses the most recent revision of source that was added to the depot as base. Since most files are only opened for add once, this will almost always be the first revision of source.)
Note
|
In previous versions of Perforce (99.1 and earlier), integration of a target that was not originally branched from the source would schedule a two-way merge, in which the only resolve choices were accept yours and accept theirs. As of Perforce 99.2, it is no longer possible to perform a two-way merge of a text file (even when possible, it was never desirable).
|
- By default, a file that has been newly created in a client workspace by p4 integrate cannot be edited before its first submission. To make a newly-branched file available for editing before submission, perform a p4 edit of the file after the resolve process is complete.
- To run the p4 integrate command, you need Perforce write access on the target files, and read access on the source files. (See the Perforce System Administrator's Guide for information on Perforce protections).
Deleting Branches
To delete a branch, use
Deleting a branch deletes only the branch specification, making the branch specification inaccessible from any subsequent p4 integrate commands. The files themselves can still be integrated with p4 integrate fromfiles tofiles, and the branch specification can always be redefined. If the files in the branched codeline are to be removed, they must be deleted with p4 delete.
Advanced Integration Functions
Perforce's branching mechanism also allows integration of specific file revisions, the re-integration and re-resolving of already integrated code, and merging of two files that were previously not related.
Integrating specific file revisions
By default, the integrate command integrates into the target all the revisions of the source since the last source revision that integrate was performed on. A revision range can be specified when integrating; this prevents unwanted revisions from having to be manually deleted from the merge while editing. In this case, the revision used as base is the first revision below the specified revision range.
The argument to p4 integrate is the target, the file revision specifier is applied to the source.
Example:
Integrating Specific File Revisions.
Ed has made two bug fixes to his file src/init.c, and Kurt wants to integrate the change
into his branched version, which is called newinit.c. Unfortunately, init.c has gone
through 20 revisions, and Kurt doesn't want to have to delete all the extra code from all 20
revisions while resolving.
Kurt knows that the bug fixes he wants were made to file revisions submitted in changelist 30.
From the directory containing his newinit.c file in his branched workspace, he types
p4 integrate -b elm_r1 newinit.c@30,@30
The target file is given as an argument, but the file revisions are applied to the source. When
Kurt runs p4 resolve, only the revision of Ed's file that was submitted in changelist 30 is
scheduled for resolve. That is, Kurt only sees the changes that Ed made to init.c in
changelist 30. The file revision that was present in the depot at changelist 29 is used as base.
Re-integrating and re-resolving files
After a revision of a source file has been integrated into a target, that revision is usually skipped in subsequent integrations with the same target. If all the revisions of a source have been integrated into a particular target, p4 integrate returns the error message All revisions already integrated. To force the integration of already-integrated files, specify the -f flag to p4 integrate.
Similarly, a target that has been resolved but not (yet) submitted can be re-resolved by specifying the -f flag to p4 resolve, which forces re-resolution of already resolved files. When this flag is used, the original client target file has been replaced with the result file by the original resolve process; when you re-resolve, yours is the new client file, the result of the original resolve.
How Integrate Works
The following sections describe the mechanism behind the integration process.
The yours, theirs, and base files
The following table explains the terminology yours, theirs, and base files.
Term
|
Meaning
|
---|
yours
|
The file to which changes are being propagated (also called the target file). This file in the client workspace is overwritten by the result when you resolve.
|
theirs
|
The file from which changes are read (also known as the source file). This file resides in the depot, and is not changed by the resolve process.
|
base
|
The last integrated revision of the source file. When you use integrate to create the branched copy of the file in the depot, the newly-branched copy is base.
|
The integration algorithm
p4 integrate performs the following steps:
- Apply the branch view to any target files provided on the command line to produce a list of source/target file pairs. If no files are provided on the command line, a list of all source/target file pairs is generated, including each revision of each source file that is to be integrated.
- Discard any source/target pairs for which the source file revisions have already been integrated. Each revision of each file that has been integrated is recorded, to avoid making you merge changes more than once.
- Discard any source/target pairs whose source file revisions have integrations pending in files that are already opened in the client.
- Integrate all remaining source/target pairs. The target file is opened on the client for the appropriate action and merging is scheduled.
Integrate's actions
The integrate command will take one of three actions, depending on particular characteristics of the source and target files:
Action
|
Meaning
|
---|
branch
|
If the target file does not exist, it is opened for branch. The branch action is a variant of add, but Perforce keeps a record of which source file the target file was branched from. This allows three-way merges to be performed between subsequent source and target revisions with the original source file revision as base.
|
integrate
|
If both the source and target files exist, the target is opened for integration, which is a variant of edit. Before a user can submit a file that has been opened for integration, the source and target must be merged with p4 resolve.
|
delete
|
When the target file exists but no corresponding source file is mapped through the branch view, the target is marked for deletion. This is consistent with integrate's semantics: it attempts to make the target tree reflect the source tree.
|
By default, when you integrate using a branch specification, the original codeline contains the source files, and the branched codeline is the target. However, if you reverse the direction of integration by specifying the -r flag, the branched codeline contains the source, and the original files are the targets.
Integration Reporting
The branching-related reporting commands are:
Command
|
Function
|
---|
p4 integrate -n [filepatterns]
|
Previews the results of the specified integration, but does not perform the integration. (To perform the integration, omit the -n flag.)
|
p4 resolve -n [filepatterns]
|
Displays files that are scheduled for resolve by p4 integrate, but does not perform the resolve. (To perform the resolve, omit the -n flag.)
|
p4 resolved
|
Displays files that have been resolved but not yet submitted.
|
p4 branches
|
Displays all branches.
|
p4 integrated filepatterns
|
Displays the integration history of the specified files.
|
p4 filelog -i [filepatterns]
|
Displays the revision histories of the specified files, including the integration histories of files from which the specified files were branched.
|
For More Information
Although Perforce's branching mechanism is relatively simple, the theory of branching can be very complex. When should a branch be created? At what point should code changes be propagated from one codeline to another? Who is responsible for performing merges? These questions will arise no matter what SCM system you're using, and the answers are not simple. Three on-line documents can provide some guidance in these matters.
A white paper on InterFile Branching, which describes Perforce's branching mechanism in technical detail, is available from:
Christopher Seiwald and Laura Wingerd's Best SCM Practices paper provides a discussion of many source configuration management issues, including an overview of basic branching techniques. This paper is available at:
Streamed Lines: Branching Patterns for Parallel Software Development is an extremely detailed paper on branching techniques. You'll find it at:
Please send comments and questions about this manual to
[email protected].
Copyright 1997-2003 Perforce Software. All rights reserved.
Last updated: 12/12/03
|
|