Chapter 6
Codelines and Branching
This chapter describes the tasks required to maintain groups of files in your depot. The following specific issues are addressed:
- Depot directory structure and how to best organize your repository
- Moving files and file changes among codeline and project directories
- Identifying specific sets of files using either labels or changelists
This chapter focuses on maintaining a software code base, but many of the tasks are relevant to managing other groups of files, such as a web site. For advice about best practices, see the white papers on the Perforce web site.
Basic terminology
To enable you to understand the following sections, here are definitions of some relevant terms as they are used in Perforce.
Term
|
Definition
|
---|
branch
|
(noun) A set of related files created by copying files, as opposed to adding files. A group of related files is often referred to as a codeline.
(verb) To create a branch.
|
integrate
|
To create new files from existing files, preserving their ancestry (branching), or to propagate changes from one set of files to another (merging).
|
merge
|
The process of combining the contents of two conflicting file revisions into a single file, typically using a merge tool like P4Merge.
|
resolve
|
The process you use to reconcile the differences between two revisions of a file. You can choose to resolve conflicts by selecting a file to be submitted or by merging the contents of conflicting files.
|
Organizing the depot
You can think of a depot as a top-level directory. Consider the following factors as you decide how to organize your depot:
- Type of content: create depots or mainline directories according to the nature of your projects and their relationships (for example, applications with multiple components developed on separate schedules).
- Release requirements: within a project, create branches for each release and integrate changes between branches to control the introduction of features and bug fixes.
- Build management: use labels and changelists to control the file revisions that are built; use client specifications and views to ensure clean build areas.
A basic and logical way to organize the depot is to create one subdirectory (codeline) for each project. For example, if your company is working on Jam, you might devote one codeline to the release presently in development, another to already-released software, and perhaps one to your corporate web site. Your developers can modify their client views to map the files in their project, excluding other projects that are not of interest. For example, if Earl maintains the web site, his client view might look like this:
//depot/www/dev/... //earl-web-catalpa/www/development/... //depot/www/review/... //earl-web-catalpa/www/review/... //depot/www/live/... //earl-web-catalpa/www/live/...
|
And Gale, who's working on Jam, sets up her client view as:
//depot/dev/main/jam/... //gale-jam-oak/jam/...
|
You can organize according to projects or according to the purpose of a codeline. For example, to organize the depot according to projects, you can use a structure like the following:
//depot/project1/main/ //depot/project1/release 1.0/ //depot/project1/release 1.1/
|
Or, to organize the depot according to the purpose of each codeline, you can use a structure like the following:
//depot/main/project1/ //depot/main/project2/ //depot/release1.0/project1/ //depot/release1.0/project2/ //depot/release2.0/project1/ //depot/release2.0/project2/
|
Another approach is to create one depot for each project. Choose a structure that makes branching and integrating as simple as possible, so that the history of your activities makes sense to you.
Branching
Branching is a method of maintaining the relationship between sets of related files. Branches can evolve separately from their ancestors and descendants, and you can propagate (integrate) changes from one branch to another as desired. Perforce's Inter-File BranchingTM mechanism preserves the relationship between files and their ancestors while consuming minimal server resources.
To create a branch, use the p4 integrate command. The p4 integrate command is also used to propagate changes between existing sets of files. For details about integrating changes, refer to "Integrating changes" on page 75.
When to branch
Create a branch when two sets of files have different submission policies or need to evolve separately. For example:
- Problem: the development group wants to submit code to the depot whenever their code changes, regardless of whether it compiles, but the release engineers don't want code to be submitted until it's been debugged, verified, and approved.
- Solution: create a release branch by branching the development codeline. When the development codeline is ready, it is integrated into the release codeline. Patches and bug fixes are made in the release code and integrated back into the development code.
- Problem: a company is writing a driver for a new multiplatform printer. The UNIX device driver is done and they are beginning work on a Macintosh driver, using the UNIX code as their starting point.
- Solution: create a Macintosh branch from the existing UNIX code. These two codelines can evolve separately. If bugs are found in one codeline, fixes can be integrated to the other.
One basic strategy is to develop code in //depot/main/ and create branches for releases (for example, //depot/rel1.1/). Make release-specific bug fixes in the release branches and, if required, integrate them back into the //depot/main/ codeline.
Creating branches
To create a branch, use the p4 integrate command. When you create a branch, the Perforce server records the relationships between the branched files and their ancestors.
You can create branches using file specifications or branch specifications. For simple branches, use file specifications. For branches that are based on complex sets of files or to ensure that you have a record of the way you defined the branch, use branch specifications. Branch specifications can also be used in subsequent integrations. Branch specifications also can serve as a record of codeline policy.
Using branch specifications
To map a set of files from source to target, you can create a branch specification and use it as an argument when you issue the p4 integrate command. To create a branch specification, issue the p4 branch branchname command and specify the desired mapping in the View: field, with source files on the left and target files on the right. Make sure that the target files and directories are in your client view. Creating or altering a branch specification has no effect on any files in the depot or client workspace. The branch specification merely maps source files to target files.
To use the branch specification to create a branch, issue the p4 integrate -b branchname command; then use p4 submit to submit the target files to the depot.
Branch specifications can contain multiple mappings and exclusionary mappings, just as client views can. For example, the following branch specification branches the Jam 1.0 source code, excluding test scripts, from the main codeline.
Branch: jamgraph-1.0-dev2release
View: //depot/dev/main/jamgraph/... //depot/release/jamgraph/1.0/... -//depot/dev/main/jamgraph/test/... //depot/release/jamgraph/1.0/test/... //depot/dev/main/bin/glut32.dll //depot/release/jamgraph/1.0/bin/glut32.dll
|
To create a branch using the preceding branch specification, issue the following command:
p4 integrate -b jamgraph-1.0-dev2release
To delete a branch specification, issue the p4 branch -d branchname command. Deleting a branch specification has no effect on existing files or branches.
As with workspace views, if a filename or path in a branch view contains spaces, make sure to quote the path:
//depot/dev/main/jamgraph/... "//depot/release/Jamgraph 1.0/..."
Using file specifications
To branch using file specifications, issue the p4 integrate command, specifying the source files and target files. The target files must be in the client view. If the source files are not in your client view, specify them using depot syntax.
To create a branch using file specifications, perform the following steps:
- Determine where you want the branch to reside in the depot and the client workspace. Add the corresponding mapping specification to your client view.
- Issue the p4 integrate source_files target_files command.
- Submit the changelist containing the branched files. The branch containing the target files is created in the depot.
Example:
Creating a branch using a file specification
Version 2.2 of Jam has just been released, and work on version 3.0 is starting. Version 2.2
must be branched to //depot/release/jam/2.2/... for maintenance.
Bruno uses p4 client to add the following mapping to his client view:
//depot/release/jam/2.2/... //bruno_ws/release/jam/2.2/...
He issues the following command to create the branch:
p4 integrate //depot/dev/main/jam/... //bruno_ws/release/jam/2.2/...
Finally, he issues the p4 submit command, which adds the newly branched files to the depot.
Integrating changes
After you create branches, you might need to propagate changes between them. For example, if you fix a bug in a release branch, you probably want to incorporate the fix back into your main codeline. To propagate selected changes between branched files, you use the p4 integrate command, as follows:
- Issue the p4 integrate command to schedule the files for resolve.
- Issue the p4 resolve command to propagate changes from the source files to the target files.
To propagate individual changes, edit the merge file or use a merge program. The changes are made to the target files in the client workspace.
- Submit the changelist containing the resolved files.
Example:
Propagating changes between branched files
Bruno has fixed a bug in the release 2.2 branch of the Jam project and needs to integrate it
back to the main codeline. From his home directory, Bruno types
p4 integrate //depot/release/jam/2.2/src/Jambase //depot/dev/main/jam/Jambase
and sees the following message:
//depot/dev/main/jam/Jambase#134 - integrate from //depot/release/jam/2.2/src/Jambase#9
The file has been scheduled for resolve. He types p4 resolve, and the standard merge dialog
appears on his screen.
//depot/dev/main/jam/Jambase - merging //depot/release/jam/2.2/src/Jambase#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. When he's done, the result file overwrites the file in his workspace.
The changelist containing the file must be submitted to the depot.
To run the p4 integrate command, you must have Perforce write permission on the target files, and read access on the source files. (See the Perforce System Administrator's Guide for information on Perforce permissions.)
By default, a file that has been newly created in a client workspace by p4 integrate cannot be edited before being submitted. To edit a newly integrated file before submission, resolve it, then issue the p4 edit command.
If the range of revisions being integrated includes deleted revisions (for example, a file was deleted from the depot, then re-added), you can specify how deleted revisions are integrated using the -d or -D flags. For details, refer to the Perforce Command Reference.
Integrating using branch specifications
To integrate changes from one set of files and directories to another, you can use a branch specification when you issue the p4 integrate command. The basic syntax of the integrate command when using a branch specification is:
p4 integrate -b branchname [tofiles]
Target files must be mapped in both the branch view and the client view. The source files need not be in the client view. If you omit the tofiles argument, all the files in the branch are affected.
To reverse the direction of integration using a branch specification, specify the -r flag. This flag enables you to integrate in either direction between two branches without requiring you to create a branch specification for each direction.
Example:
Integrating changes to a single file in a branch
A feature has been added in the main Jam codeline and Bruno wants to propagate the feature
to release 1.0 He types:
p4 integrate -b jamgraph-1.0-dev2release *.c
//depot/release/jam/1.0/src/command.c#10 - integrate from //depot/dev/main/jam/command.c#97
The file has been scheduled for resolve. He types p4 resolve, and the standard merge dialog
appears on his screen.
//depot/release/jam/1.0/src/command.c - merging //depot/dev/main/jam/command.c#97
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. When he's done, the result file overwrites the file in his branched
client workspace; the file must then be submitted to the depot.
Integrating between unrelated files
If the target file was not branched from the source, there is no base (common ancestor) revision. To integrate between unrelated files, specify the -i flag. Perforce uses the first (most recently added) revision of the source file as its base revision. This operation is referred to as a baseless merge.
Integrating specific file revisions
By default, the integrate command integrates all the revisions following the last-integrated source revision into the target. To avoid having to manually delete unwanted revisions from the merge file while editing, you can specify a range of revisions to be integrated. The base file is the common ancestor.
Example:
Integrating specific file revisions
Bruno has made two bug fixes to //depot/dev/main/jam/scan.c in the main codeline,
and Earl wants to integrate the change into the release 1.0 branch. Although scan.c has
gone through 20 revisions since the fixes were submitted, Earl knows that the bug fixes he
wants were made to file revisions submitted in changelist 30. He types
p4 integrate -b jamgraph-1.0-dev2release //depot/release/jam/
1.0/scan.c@30,@30
The target file (//depot/release/jam/1.0/scan.c) is given as an argument, but the file
revisions are applied to the source. When Earl runs p4 resolve, only the revision of Bruno's
file that was submitted in changelist 30 is scheduled for resolve. That is, Earl sees only the
changes that Bruno made to scan.c in changelist 30. The file revision that was present in the
depot at changelist 29 is used as the base file.
Reintegrating and reresolving 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. To force the integration of already-integrated files, specify the -f flag when you issue the p4 integrate command.
A target that has been resolved but not submitted can be resolved again by specifying the -f flag to p4 resolve. When you reresolve a file, yours is the new client file, the result of the original resolve.
Integration reporting
The following reporting commands provide useful information about the status of files being branched and integrated. Note the use of the preview flag (-n) for reporting purposes.
To display this information
|
Use this command
|
---|
Preview of the results of an integration
|
p4 integrate -n [filepatterns]
|
Files that are scheduled for resolve
|
p4 resolve -n [filepatterns]
|
Files that have been resolved but not yet submitted.
|
p4 resolved
|
List of branch specifications
|
p4 branches
|
The integration history of the specified files.
|
p4 integrated filepatterns
|
The revision histories of the specified files, including the integration histories of files from which the specified files were branched.
|
p4 filelog -i [filepatterns]
|
Using labels
A Perforce label is a set of tagged file revisions. For example, you might want to tag the file revisions that compose a particular release with the label release2.0.1. In general, you can use labels to:
- Keep track of all the file revisions contained in a particular release of software.
- Distribute a particular set of file revisions to other users (for example, a standard configuration).
- Populate a clean build workspace.
- Specify a set of file revisions to be branched for development purposes.
- Sync the revisions as a group to a client workspace.
Labels and changelist numbers both refer to particular sets of file revisions but differ as follows:
- A label can refer to any set of file revisions. A changelist number refers to the contents of all the files in the depot at the time the changelist was submitted. If you need to refer to a group of file revisions from different points in time, use a label. If there is a point in time at which the files are consistent for your purposes, use a changelist number.
- You can change the contents of a label. You cannot change the contents of a submitted changelist.
- You can assign your own names to labels. Changelist numbers are assigned by the Perforce server.
Changelists are suitable for many applications that traditionally use labels. Unlike labels, changelists represent the state of a set of files at a specific time. Before you assume that a label is required, consider whether simply referring to a changelist number might fulfill your requirements.
Tagging files with a label
To tag a set of file revisions (in addition to any revisions that have already been tagged), use p4 tag, specifying a label name and the desired file revisions.
For example, to tag the head revisions of files that reside under //depot/release/jam/2.1/src/ with the label jam-2.1.0, issue the following command:
p4 tag -l jam-2.1.0 //depot/release/jam/2.1/src/...
To tag revisions other than the head revision, specify a changelist number in the file pattern:
p4 tag -l jam-2.1.0 //depot/release/jam/2.1/src/...@1234
Only one revision of a given file can be tagged with a given label, but the same file revision can be tagged by multiple labels.
Untagging files
You can untag revisions with:
p4 tag -d -l labelname filepattern
This command removes the association between the specified label and the file revisions tagged by it. For example, if you have tagged all revisions under //depot/release/jam/2.1/src/... with jam-2.1.0, you can untag only the header files with:
p4 tag -d -l jam-2.1.0 //depot/release/jam/2.1/src/*.h
Previewing tagging results
You can preview the results of p4 tag with p4 tag -n. This command lists the revisions that would be tagged, untagged, or retagged by the tag command without actually performing the operation.
Listing files tagged by a label
To list the revisions tagged with labelname, use p4 files, specifying the label name as follows:
All revisions tagged with labelname are listed, with their file type, change action, and changelist number. (This command is equivalent to p4 files //...@labelname).
Listing labels that have been applied to files
To list all labels that have been applied to files, use the command:
Using a label to specify file revisions
You can use a label name anywhere you can refer to files by revision (#1, #head), changelist number (@7381), or date (@2003/07/01).
If you omit file arguments when you issue the p4 sync @labelname command, all files in the client workspace view that are tagged by the label are synced to the revision specified in the label. All files in the workspace that do not have revisions tagged by the label are deleted from the workspace. Open files or files not under Perforce control are unaffected. This command is equivalent to p4 sync //...@labelname.
If you specify file arguments when you issue the p4 sync command (p4 sync files@labelname), files that are in your workspace and tagged by the label are synced to the tagged revision.
Example:
Retrieving files tagged by a label into a client workspace
To retrieve the files tagged by Earl's jam-2.1.0 label into his client workspace, Bruno issues
the following command:
//depot/dev/main/jam/Build.com#5 - updating c:\bruno_ws\dev\main\jam\Build.com //depot/dev/main/jam/command.c#5 - updating c:\bruno_ws\dev\main\jam\command.c //depot/dev/main/jam/command.h#3 - added as c:\bruno_ws\dev\main\jam\command.h //depot/dev/main/jam/compile.c#12 - updating c:\bruno_ws\dev\main\jam\compile.c //depot/dev/main/jam/compile.h#2 - updating c:\bruno_ws\dev\main\jam\compile.h <etc>
|
Deleting labels
To delete a label, use the following command:
Deleting a label has no effect on the tagged file revisions (though, of course, the revisions are no longer tagged).
Creating a label for future use
To create a label without tagging any file revisions, issue the p4 label labelname command. This command displays a form in which you describe and specify the label. After you have created a label, you can use p4 tag or p4 labelsync to apply the label to file revisions.
Label names cannot be the same as client workspace, branch, or depot names.
For example, to create jam-2.1.0, issue the following command:
The following form is displayed:
Label: jam-2.1.0 Update: 2005/03/07 13:07:39 Access: 2005/03/07 13:13:35 Owner: earl Description: Created by earl. Options: unlocked View: //depot/...
|
Enter a description for the label and save the form. (You do not need to change the View: field.)
After you create the label, you are able to use the p4 tag and p4 labelsync commands to apply the label to file revisions.
Restricting files that can be tagged
The View: field in the p4 label form limits the files that can be tagged with a label. The default label view includes the entire depot (//depot/...). To prevent yourself from inadvertently tagging every file in your depot, set the label's View: field to the files and directories to be taggable, using depot syntax.
Example:
Using a label view to control which files can be tagged
Earl wants to tag the revisions of source code in the release 2.1 branch, which he knows can be
successfully compiled. He types p4 label jam-2.1.0 and uses the label's View: field to
restrict the scope of the label as follows:
Label: jam-2.1.0 Update: 2005/03/07 13:07:39 Access: 2005/03/07 13:13:35 Owner: earl Description: Created by earl. Options: unlocked View: //depot/release/jam/2.1/src/...
|
This label can tag only files in the release 2.1 source code directory.
Using static labels to archive workspace configurations
You can use static labels to archive the state of your client workspace (meaning the currently synced file revisions) by issuing the p4 labelsync command. The label you specify must have the same view as your client workspace.
For example, to record the configuration of your current client workspace using the existing ws_config label, use the following command:
p4 labelsync -l ws_config
All file revisions that are synced to your current workspace and visible through both the client view and the label view (if any) are tagged with the ws_config label. Files that were previously tagged with ws_config, then subsequently removed from your workspace (p4 sync #none), are untagged.
To sync the files tagged by the ws_config label (thereby recreating the workspace configuration), issue the following command:
Using automatic labels as aliases for changelists or other revisions
You can use automatic labels to specify files at certain revisions without having to issue the p4 labelsync command.
To create an automatic label, fill in the Revision: field of the p4 label form with a revision specifier. When you sync a workspace to an automatic label, the contents of the Revision: field are applied to every file in the View: field.
Example:
Using an automatic label as an alias for a changelist number
Earl is running a nightly build process, and has successfully built a product as of changelist
1234. Rather than having to remember the specific changelist for every night's build, he types
p4 label nightly20061201 and uses the label's Revision: field to automatically tag all
files as of changelist 1234 with the nightly20061201 label:
Label: nightly20061201 Owner: earl Description: Nightly build process. Options: unlocked View: //depot/... Revision: @1234
|
The advantage to this approach is that it is highly amenable to scripting, takes up very little
space in the label table, and provides a way to easily refer to a nightly build without
remembering which changelist number was associated with the night's build process.
Example:
Referring specifically to the set of files submitted in a single changelist.
A bug was fixed by means of changelist 1238, and requires a patch label that refers to only
those files associated with the fix. Earl types p4 label patch20061201 and uses the label's
Revision: field to automatically tag only those files submitted in changelist 1238 with the
patch20061201 label:
Label: patch20061201 Owner: earl Description: Patch to 2006/12/01 nightly build. Options: unlocked View: //depot/... Revision: @1238,1238
|
This automatic label refers only to those files submitted in changelist 1238.
Example:
Referring to the first revision of every file over multiple changelists.
You can use revision specifiers other than changelist specifiers; in this example, Earl is
referring to the first revision (#1) of every file in a branch. Depending on how the branch was
populated, these files could have been created through multiple changelists over a long period
of time:
Label: first2.2 Owner: earl Description: The first revision in the 2.2 branch Options: unlocked View: //depot/release/jam/2.2/src/... Revision: "#1"
|
Because Perforce forms use the # character as a comment indicator, Earl has placed quotation
marks around the # to ensure that it is parsed as a revision specifier.
Preventing inadvertent tagging and untagging of files
To tag the files that are in your client workspace and label view (if set) and untag all other files, issue the p4 labelsync command with no arguments. To prevent the inadvertent tagging and untagging of files, issue the p4 label labelname command and lock the label by setting the Options: field of the label form to locked. To prevent other users from unlocking the label, set the Owner: field. For details about Perforce privileges, refer to the Perforce System Administrator's Guide.
Please send comments and questions about this manual to
[email protected].
Copyright 2005-2008 Perforce Software. All rights reserved.
Last updated: 12/05/08