[Top] [Prev] [Next]

Branching


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.

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:

Creating a Branch

The steps to creating a branched codeline are:

  1. Create the new branch with p4 branch branchname. Use the view in the resulting form to indicate which files are to be included in the branch, and where the branched codeline will be stored within the depot's file tree.
  2. 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.
  3. Use p4 integrate to open the new files for branching. The new files are listed in a changelist; the associated operation is branch.
  4. Use p4 submit to submit the changelist to the perforce server. This creates the new files in the depot.
  5. Retrieve the new codeline into the client workspace with p4 get.
The following example demonstrates each of these steps.

Step 1: Create the branch

The first step is to create the branch. Creating a branch does four things:

  1. Assigns the branched codeline a name;
  2. Describes which files will be copied from;
  3. For each original file, describes where the new copy will be stored within the depot;
  4. Maintains a mapping between each original and branch file, so that changes to one can be easily propagated to the other.
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_project 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

p4 branch elm_r1
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.

Step 2: Include the Branched Files in the Client 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.

Steps 3 & 4:
Use `p4 integrate' and `p4 submit'
to Create the Target Files

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 simply 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 do the operation it describes; 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

p4 integrate -b branchname [filepatterns] 
If the filepatterns are left off, all the files in the branch are affected. When included, the filepatterns must describe branch 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 from //depot/elm_proj/Changes#6
//depot/elm_release1/NOTICE#1 - branch from //depot/elm_proj/NOTICE#23
      <etc.>

The 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.

Step 5:
Use `p4 get' to Retrieve the Files
into the Client Workspace

The final step is to retrieve the branched files into the client workspace so that they can be worked with. This is done, as always, with p4 get.

Kurt wants to retrieve the branched copies of the Elm source into his workspace so he can work with them. He types

p4 get //depot/elm_release1/...
and sees
//depot/elm_release1/Changes#1 - added as /usr/kurtv/elm.r1/Changes
//depot/elm_release1/Configure#1 - added as /usr/kurtv/elm.r1/Configure
      <etc.>

All the branched files are copied into his workspace, he can proceed to work with them as he normally would.

Working With Branched Files

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 get, edit, delete, submit, etc. Evolution of both codelines proceeds separately; new commands come into play only when changes to one codeline needs to be propagated to the other.

Propagating Changes from One Codeline
to the Other

Changes 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.

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

p4 integrate -b elm_r1 ~kurtv/elm.r1/src/elm.c
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. 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.

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 -r flag to p4 integrate.

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.

Deleting Branches

To delete a branch, use

p4 branch -d branchname

Advanced Integration Functions

Perforce's branching mechanism also allows integration of specific file revisions, the re-resolving 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 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 in revisions 31 and 32 of Ed's init.c file. From the directory of his newinit.c file in his branched workspace, he types

p4 integrate -b elm_r1 src/newinit.c#31,32
The target file is given as an argument, but the file revisions are applied to the donor. When Kurt runs p4 resolve, only revisions 31 and 32 of Ed's file are scheduled for resolve, that is, Kurt only sees the changes that Ed made to init.c between revision 31 and revision 32. The 30th revision is used as base.

Re-Integrating and Re-Resolving Files

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 when integration has already been performed, by providing the -f flag to p4 integrate.

A target file that has been resolved, but not yet submitted, can be re-resolved with p4 reresolve. By the time reresolve is called, the original client target file has been replaced with the result file of the resolve process; thus, when re-resolving, yours will be the new client file, which is the result of the original resolve.

Merging Previously Unrelated Files

It is possible to perform an integration between two files that were never previously related! For example, suppose you're working on a source file, and want to add some functionality from a file from a completely different project. It would obviously be useful to be able to perform a merge between the two unrelated files, and Perforce permits this.

There are two methods by which this can be done. The easiest way is to call p4 integrate with two file arguments and without the -b branch flag, as in

p4 integrate donor_file target_file
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.

A second method would be to set up a new branch that includes the donor and target files on each side of the same mapping within the branch view, and then integrate on this branch with the -b flag.

This process may be surprising when first seen, but it is a natural by-product of Perforce's branching mechanism. Creating a branched codeline merely makes copies of the files in the original codeline; these files evolve entirely separately from each other, and are not considered `related' until p4 integrate is called upon with the -b branch flag to find the common bloodline.

The integration process is slightly different when integrating two previously unrelated files. Since the two file revisions did not begin their lives at a common, older file revision, there can be no base file, so p4 resolve can't do a three-way merge. In this case, p4 resolve will do a two-way merge, in which yours is also used as base. In a two-way merge, all changes appear as theirs, and there can be no conflicts.

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

The values of yours, mine, and base in a three-way merge are quite different when propagating changes between two codelines:
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 straightforward 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 donor file). This file revision comes from the depot, and is unchanged by the resolve process. In a straightforward 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 original codeline.
base The last integrated revision of the donor 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:

  1. It applies the branch view to any target files provided on the command line to produce a list of donor/target file pairs. If no files are provided on the command line, a list of all donor/target file pairs is generated. It notes individually each revision of each donor file that is to be integrated.
  2. It discards any donor/target pairs for which the donor 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.
  3. It discards any donor/target pairs whose donor file revisions have integrations pending in files that are already opened in the client.
  4. All remaining donor/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 donor 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 there are two differences:
integrate If both the donor 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 donor and target must be merged with p4 resolve.
delete When the target file exists but no corresponding donor 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 donor tree.

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.

Integration Reporting

There are five branching-related reporting commands:

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 have not yet been submitted.
p4 integrated
[filepatterns]
Describes all integrated and submitted files that match the filepattern arguments.
p4 filelog
[filepatterns]
Describes the revision history of the named files. For each revision of the named files, the following is reported: · change number; · operation (edit, add, delete, branch, integrate) · client name; · user name; and · description. If the operation was branch or integrate, the names and revisions of the corresponding branch files are reported as well.

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.



[Top] [Prev] [Next]

[email protected]
Copyright © 1997, Perforce Software. All rights reserved.