Perforce's Inter-File Branching mechanism allows any set of files to be copied within the depot. 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.
Branching is a method of keeping in sync 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 files would become 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.
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:
As described above, two separate actions comprise branching: first, a branch is created (e.g., files are copied); second, edits are copied from one codeline to the other as needed. This section describes the first of these actions.
The steps to creating a branched codeline are:
The first step is to create the branch view. Creating a branch view does four things:
A version of Elm is ready for release, and a potential problem is foreseen: the developers will be submitting code to the depot for the next version of Elm, but the release engineers will be submitting fixes to the released version. The two policies are clearly incompatible; so a branched codeline, with duplicate Elm files, needs to be created. Kurt, one of the release engineers, is assigned to create the branch for the release engineers.
The original code is stored in the depot under its elm_proj subtree; Kurt decides to call the branch elm_r1, and will store the branched codeline in the depot under an elm_release1 subdirectory. He types
and sees the following:
Branch: elm_r1
Date: 05/25/1997 17:43:28
Owner: kurtv
Description:
Created by kurtv.
View:
//depot/... //depot/...
The default View above would map the entire depot to itself in a branch, which is useless. The View needs to map the original codeline's files on the left to branch files on the right; Kurt changes the View field as follows:
Branch: elm_r1
Date: 05/25/1997 17:43:28
Owner: kurtv
Description:
Created by kurtv.
View:
//depot/elm_proj/... //depot/elm_release1/...
This maps all the files in the depot's elm_proj file tree to a new depot file tree called elm_release1. All files from the source subtree will eventually be copied to the branch subtree; these files will be the contents of the branch.
Kurt quits the editor; the branch is created.
The p4 branch command does not copy files into the branch; it simply specifies which original file will correspond to which branched file.
Exclusionary mappings may be used within a branch view.
In order to work with branched files, the branched files must be accessible through the client view.
Kurt will be working with the branched files. His client is kurtv_cli; he types p4 client, and adds a line to his client view:
Client: kurtv_cli
Date: 05/25/1997 18:34:58
Owner: kurtv
Description:
Created by kurtv.
Root: /usr/kurtv
View:
//depot/elm_release1/... //kurtv_cli/elm.r1/...
There might be other mappings within the client view; the only crucial factor is that the files in the depot's elm branch directory be mapped to some location in Kurt's client workspace. The mapping shown here accomplishes this.
To create the new branch files, use p4 integrate followed by p4 submit. When the branch files don't yet exist in the depot, integrate creates the branched files in the client workspace and tells the server that the branch files are to be copied from the original files described in the branch mapping. The integrate command, like add, edit, and 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 basic syntax of the integrate command is
If the filepatterns are left off, all the files in the branch are affected. When included, the filepatterns must describe the new files, not the original files, and these files must be visible through the client view.
Kurt has created the branch elm_r1 as above, and he's ready to create the branched copies in the depot. He types p4 integrate -b elm_r1, and sees
//depot/elm_release1/Changes#1 - branch/sync from //depot/elm_proj/Changes#6
//depot/elm_release1/NOTICE#1 - branch/sync from //depot/elm_proj/NOTICE#23
<etc.>
The branched files have been created in his client workspace, and instructions to branch these files have been added to his default changelist. He types p4 submit, and sees
Change: new
Client: kurtv_elm
User: kurtv
Status: new
Description:
<enter description here>
Files:
//depot/elm_release1/Changes # branch
//depot/elm_release1/Configure # branch
<etc.>
He changes the description and quits the editor; the branched files are created within the depot and are copied into the client workspace.
If Kurt wanted the files to be created in the depot but not synced to the client workspace, he could have used the -v flag with the integrate command. If he did this, the files would later need to be copied to the client workspace with p4 sync.
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, simply p4 edit the file.
Once a branch has been created and the files have been copied into the branched codeline with p4 integrate, the branched files are treated exactly like non-branched files, with the normal use of sync, edit, delete, submit, etc. Evolution of both codelines proceeds separately; additional Perforce commands are used only when changes to one codeline need to be propagated to the other.
It is worth repeating that two separate actions comprise branching: first, one set of files is copied from one location in the depot to another location, and second, changes made to one codeline can be copied to the branched codeline as needed. The steps needed to accomplish the first action have been described above; now we'll discuss how to accomplish the second action.
Edits to a file in either codeline can be propagated to the corresponding file in the other codeline with the resolve command. Only one additional step needs to be performed: before resolving, the integrate command is used to schedule the merge between the original files and the branched files. In its normal use with branched files, p4 integrate takes the form p4 integrate -b branchname files, where the specified files are the branched files rather than the original files.
A bug has been fixed in the original codeline's src/elm.c file. Kurt wants to propagate the same bug fix to the branched codeline he's been working on. He types
and sees
//depot/elm_release1/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/kurtv/elm.r1/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.
In Perforce terminology, changes are always propagated from donor files to target files. In the above example, the original codeline provided the donor files and the target files were located in the branched codeline, but changes can be propagated in the other direction as well.
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. You must tell Perforce that text in one file needs to be propagated to its branch 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.
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 donor files supplied as arguments to integrate need not be in the client view.
To run the p4 integrate command, write access is needed on the target files, and read access is required on the donor files. Protections are discussed in Chapter 9.
A change can be propagated in the reverse direction, from branched files to the original files, by supplying the -r flag to p4 integrate. In this case, the names of the original files are provided as arguments to p4 integrate -r.
Ed wants to integrate a change in Kurt's branched src/screen.c file to Ed's original version of the same file. He types p4 integrate -r -b elm_r1 //depot/elm_proj/src/screen.c; and then p4 resolve. The change in the branched file is propagated to his source file.
When the -r flag is used to propagate changes from branched donors to original targets, the original source files must be visible through the target view.Thus far, we have been describing the two actions that comprise branching: copying a set of files from one location in the depot to another, and propagating edits of one of the codelines to the other codeline. It is possible to use p4 integrate to perform both of these steps without ever having created a branch view. This is accomplished by calling p4 integrate with two file arguments and without the -b branch flag, as in
When p4 integrate is called this way, it will perform the integration between the two named files. The first file argument provides the donor; the second provides the target. The donor file must already exist; the target file needn't. There are three possible combinations of donor and target:
Deleting a branch deletes only the branch view description, making the branch inaccessible from any subsequent p4 integrate commands. If the files in the branched codeline are to be removed, they must be deleted with p4 delete.
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.
By default, the integrate command will integrate into the target all the revisions of the donor since the last donor 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 donor.
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 of his newinit.c file in his branched workspace, he types
The target file is given as an argument, but the file revisions are applied to the donor. 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.
Once a particular revision of a donor 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 donor have been integrated into a particular target, p4 integrate will give the error message All revisions already integrated. But integration of a particular donor 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.
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.
The values of yours, theirs, and base in a three-way merge are quite different when propagating changes between two codelines:
Yours, theirs, and base are first discussed in chapter 5.
p4 integrate performs the following steps:
The integrate command will take one of three actions, depending on particular characteristics of the donor and target files:
When the -r flag is not provided to p4 integrate, the original codeline provides the donor files, and the branched codeline provides the targets. When the -r flag is given, the branched codeline is the donor, and the original files are the targets.
The branching-related reporting commands are:
There is an ordering 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 fifth command provides a complete history of any file, and is incredibly useful.