|
|
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 synchronized two or more sets of similar, but not identical, files. Most software configuration management systems have some form of branching; we believe that Perforce's mechanism is unique in that 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 file 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 whenever two sets of code have different rules governing when code should be submitted, or whenever a set of 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 would be integrated into the release codeline. Patches and bug fixes would be made in the release code; later, these changes could be integrated into the development code.
- A company is writing a driver for a new multi-platform printer. They've written a UNIX device driver; they're now going to begin work on a Macintosh driver, using the UNIX code as their starting point. They create a branch from the existing UNIX code; they now have two copies of the same code, and these codelines can evolve separately. If bugs are found in either codeline, bug fixes can be propagated from one codeline to the other with the Perforce integrate command.
- At Perforce, we use branching to manage our releases. Development always proceeds in files located within //depot/main/... When a new release is ready, it's branched into another codeline, for example, the code for release 98.1 was copied from //depot/main/... into //depot/r98.1/... Bug fixes that affect both codelines will be made within //depot/main/..., and later integrated into the other codeline.
- Development of release 98.2 proceeded in //depot/main/..., when the new release was ready, it was branched into //depot/r98.2/..., and the process continues like this for all Perforce releases.
|
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 at the start. 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 need not be, as 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 will be 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 fromfiles will be copied into the client workspace to the target files tofiles.
- Run p4 submit. The new files will be 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 the correct location in his client workspace.
Finally, he runs p4 submit, which adds the newly created branched files to the depot.
Why Not Just Copy the Files?
It is certainly 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. But when you do this with p4 integrate, Perforce tracks the connections between related files in an integration record, allowing easy propagation of changes between one set of files and another.
Propagating Changes Between Branched Files
Once 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 via p4 resolve into the corresponding branch files. (You'll find a general discussion of the resolve process in chapter 5, and file resolution with branching is discussed starting at "How Integrate Works" on page 72).
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 will be 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.
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, etc. 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. 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
|
|
In "Branching and Merging, Method 1: Branching with File Specifications" on page 66, the user must manually track which source files go with which target files. A slightly different branching mechanism allows Perforce to track this for you; the target files are mapped to source files in a branch specification, and the name of the branch specification is provided as an argument to p4 integrate.The downside is that the initial setup is slightly more complicated; the benefit is that the user no longer needs to remember the exact mappings between target files and source files. 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 will use 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: 05/25/1997 17:43:28 Owner: edk Description: Created by edk. View: //depot/... //depot/...
|
The default View: above would branch the entire depot into itself, which is useless. The view should map the
original codeline's files on the left to branched files on the right. Ed changes the View: and Description:
fields as follows:
elm2.0 Date: 05/25/1997 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 the correct
location 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 target files to source 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#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.
|
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 will be 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: 05/25/1997 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 may be used within branch specifications.
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 the filepatterns are left off, all the files in the branch are affected. Except as noted below, when the tofiles argument is included, it must describe the original source files, not the branched files; and these files must be visible through the client view.
- 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 [sourcefiles]
- 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 will begin 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 will be 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 will reject 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 will use as base the most recent revision of source that was added to the depot. 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, Perforce write access is needed on the target files, and read access is required 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 will integrate 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 syntax here is a little strange: although the file provided as an 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
Once a particular revision of a source file has been integrated into a particular target, that particular 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 will give the error message All revisions already integrated. But integration of a particular source can be forced, even though integration has already been performed, by providing the -f flag to p4 integrate.
Similarly, a target file that has been resolved but not (yet) submitted can be re-resolved by providing the -f flag to p4 resolve, which forces re-resolution of already resolved files. When this flag is used, the original client target file will already have been replaced with the result file of the original resolve process; thus, when re-resolving, yours will already be the new client file, the result of the original resolve.
|
How Integrate Works
|
|
The preceding material in this chapter was written from a user's perspective. This section makes another pass at the same material, this time describing the mechanism behind the integration process.
p4 integrate's Definitions of yours, theirs, and base
We're already seen what yours, theirs, and base mean in a single-file three-way merge (in "Perforce Basics: Resolving File Conflicts" on page 43). The meanings of these terms are quite different when propagating changes between two codelines:
Term
|
Meaning
|
---|
yours
|
The file that changes are being propagated to (also known as the target file). This file is in the client workspace, and it is overwritten by the result once the resolve process is complete.
In a forward integrate, this is a file in the branched codeline. When the -r flag has been provided to integrate, this is a file in the original codeline.
|
theirs
|
The file revision that changes are being read from (also known as the source file). This file revision comes from the depot, and is unchanged by the resolve process.
In a forward integrate, this is a file revision from the original codeline. When the -r flag has been provided to integrate, this is a file in the branched codeline.
|
base
|
The last integrated revision of the source file. When a new branch is created and integrate is used 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:
- It applies 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. It notes individually each revision of each source file that is to be integrated.
- It discards any source/target pairs for which the source file revisions have been integrated in previous changes. Each revision of each file that has been integrated is remembered individually, in order to avoid making the user merge changes more than once.
- It discards any source/target pairs whose source file revisions have integrations pending in files that are already opened in the client.
- All remaining source/target pairs will be integrated. The target file is opened on the client for the appropriate action (see below), 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.
|
When the -r flag is not provided to p4 integrate, the original codeline provides the source files, and the branched codeline provides the targets. When the -r flag is given, the branched codeline is the source, and the original files are the targets.
|
Integration Reporting
|
|
The branching-related reporting commands are:
Command
|
Function
|
---|
p4 integrate -n [filepatterns]
|
Reports what integrate would do without actually doing it. Any of the usual parameters to integrate can be specified as well.
|
p4 resolve -n [filepatterns]
|
Reports files that have been scheduled for resolve by p4 integrate, but that have not yet been resolved. Reports what p4 resolve would do without actually doing it. Any of the usual parameters to resolve can be provided as well.
|
p4 resolved
|
Lists those files that have been resolved but not yet submitted.
|
p4 integrated [filepatterns]
|
Describes all integrated and submitted files that match the filepattern arguments.
|
p4 branches
|
Display a list of all branches known to the system.
|
p4 integrated [filepatterns]
|
Describes the revision history of the named files. For each revision of the named files, the change number, operation (add, edit, delete, branch, integrate), client name, user name, and changelist description are reported. If the operation was branch or integrate, the names and revisions of the corresponding branch files are reported as well.
|
p4 filelog -i [filepatterns]
|
Display the revision histories of the named files, including the integration histories of any files that the named files were branched from, up to the branch point.
|
There is an order to the first four of these commands: the first is performed before integrate; the second is done after integrate and before resolve; the third is given after resolve but before submit, and the fourth is performed after submit. The sixth and seventh commands provides a complete history of any file, and is incredibly useful.
|
For More Information
|
|
Although Perforce's branching mechanism is relatively simple, the theory of branching can be very thorny. 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
This paper is a work-in-progress, and is not meant for beginners!
|
|
|
Please send comments and questions about this manual to
[email protected].
Copyright 1997, 1998, 1999, 2000 Perforce Software. All rights reserved.
Last updated: 10/09/00
|
|
| |
|