If you're reading this, then there's a good chance that your organization has recently migrated (or is considering migrating) to Perforce from some other SCM system (or lack thereof). You may be wondering why you should bother.
The main advantages of Perforce over other SCM systems are the improved speed of operations (up to 1000× in some cases), and the relative safety of branching. In my experience, these capabilities increase overall productivity by 10%-20%, so the cost of the transition to Perforce is usually overcome within a few weeks of use.
So if Perforce is so great, why do we need to use A4 too?
The general answer is that Perforce is too powerful. This leads to conflicts in terms of where data should be stored, when it should be submitted, how configurations should be reproduced, when it is safe to branch, what should be merged when and where, and so forth. It also burdens developers with routinely specifying things (such as the workspace view) that ought to be determined automatically by default.
A4 addresses these issues by setting certain basic standards, and providing utilities to support those standards. However, A4 does not supplant the direct use of p4 for routine operations.
We'll assume that the p4(1) and a4(1) have already been installed, and the Perforce server (p4d) and the A4 server (a4d) have already been set up by your SCM administrator. (If you don't know who your SCM administrator is, then it's probably you. You'll need to install p4 according to the instructions at Perforce, and install a4 according to the README file.)
Most importantly, you need to set the P4CONFIG environment variable in your .cshrc file (or equivalent) thusly:
setenv P4CONFIG .p4env
If the p4d server is not listening on the default port of perforce:1666 and a4 is not configured to set the P4PORT environment variable for you, then you'll need to set P4PORT in your .cshrc file thusly:
setenv P4PORT <host>:<port>
Your SCM administrator can tell you what <host> and <port> should be. Unless configured otherwise, a4 will assume that a4d is listening on the numerically consecutive port after P4PORT, even if <port> is a service name.
In order to submit to branches that have a built-in regression, you'll need to grant permission for the user running p4d to rsh to the client machine and run the regression in your client as you. The following command sets up an SUID file in a directory called .a4 below your home directory:
% a4 setup
In order to find the correct reference builds when using a4 clone, you need to set the A4PLATFORM environment variable in your .cshrc thusly:
setenv A4PLATFORM "`uname -m`-`uname -s``uname -r`"
That's about it. You can also set things like P4EDITOR, P4PAGER, P4MERGE and P4DIFF if you're so inclined.
A4 and all of its subcommands have on-line help. For example, try this:
% a4 help % a4 --help=topics % a4 newclient --help=long
You could instead view the a4(1) man page if you prefer. If you're looking for detailed information regarding the A4 system as a whole, then view the a4intro(1) man page.
Before you can submit any changes to the source database, you need to create a workspace (a.k.a. client) in which to make and test those changes. In order to create a client, you must first determine on which branch (a.k.a. codeline) the client should be based. Your project manager can tell you that. You could work on the TRUNK instead, but then your changes will probably be subjected to an inappropriately lengthy regression when you submit.
Cd to the directory that you want to contain the workspace root directory, and run the following:
% a4 newclient <branch-name>
A unique client name will be selected and reported to you. You'll probably want to cd to that directory.
By default, a4 newclient retrieves all of the source files on the branch, which can take a while. Ideally, you're working on a branch that only has files that you care about in view. If not, then you might need to talk to your project manager to see if you ought to be working on a different branch, perhaps one that doesn't yet exist.
Alternatively, you could create the client without sync'ing the files, and then use p4 sync to retrieve only the files you care about:
% a4 newclient -n <branch-name> % cd <client-name> % p4 sync <path>/... % a4 maketree
Another way to deal with this is to modify the local view by adding exclusions to the postfiles file like this:
% a4 newclient -n <branch-name> % cd <client-name> % mkdir -p `a4 top -c`; vi `a4 top -c`/postfiles -/path/to/files/i/dont/care/about -/another/irrelevant/directory -/la/la/la/i/cant/hear/you % a4 sync
In most cases, you should use ordinary p4 commands to make changes to your client workspace. However, you may need to use a4 to submit those changes. (See the next section .)
The Perforce web site has lots of information about using p4. In particular, there is a quick start guide , as well as a set of technical notes .
In order to submit to a branch that has a regression, submissions must be locked. (Otherwise, it is possible to accept simultaneous submissions that are incompatible.) In order to lock the branch for submission, you need to use a4 submit instead of p4 submit:
% a4 submit
As with naked Perforce, your workspace must be up-to-date in order for the submission to succeed. If you need to p4 sync in order to update your workspace, then you may need to p4 resolve as well. (If you find p4 resolve annoying, try submitting more often. The burden of resolving is intrinsically shifted to whoever submits later.)
% a4 rmclient <client>
This removes both the p4 client record and its workspace.
[This section applies to whole-workspace branches. For inter-file branching, see below .]
Because branches are expensive, in terms of both maintenance and storage, you should refrain from creating branches needlessly. Normally, each project will have a single person in charge of creating branches. (If you don't know who that person is, then it's probably you.)
You'll need to create a branch, for example, when multiple contributors are working on a set of cooperating changes to the project (a.k.a. a job), and the changes of no single contributor will pass the project branch regression in isolation from the rest of the job. You then need to create a "development" branch based on the project branch, like this:
% a4 newbranch <project-branch>
The unique name of the new branch will be reported. Branches created via A4 always end in a slash ("/").
New branches are empty upon creation. You need to create a client based on the branch in order to populate it with its parent's files. (See the next section .) However, before you do that you should limit the branch's view to the relevant files. (See below .)
Here are some other reasons to create a branch:
To integrate changes from the client's parent branch, run this:
% a4 integrate <integrate-options>
This works very much like p4 integrate, except that it fills in the -b option for you. You may need to p4 resolve before you can a4 submit.
If you branch is designated as fully-merging (which is the default), then the best way to integrate the branch's changes back into the parent branch is to use a4 merge from a client based on the parent:
% a4 merge <child-branch>
This duplicates the child branch directly into the parent, but it will fail completely if the child branch doesn't already include all of the parent branch's changes.
It is important to note that integrating between child and parent branches is mandatory. If you don't do it, then improvements will get stranded and fail to propagate. In particular, bugs that have been fixed might then reappear in future releases, which tends to make you very unpopular with your customers. Developers often neglect integrating among branches because it requires them to p4 resolve, but this can be addressed by integrating more often rather than less often.
Even though different projects may have different dedicated branches, there is still a TRUNK branch that includes everything, and through which all changes propagate. Therefore, it is important to locate files within your client such that they can coexist with all the rest of the source code in your organization, including source code imported from other organizations.
Primarily, this means that you need to subordinate files within the directory hierarchy, such that they don't collide with locations needed by other projects. For example, there will most likely be a top-level directory for your project inside which all project-specific source files will reside. This might seem redundant, but it isn't.
On the other hand, source files that are intended to be shared with other projects should appear in a generic location, such that changes propagate among projects.
If at all possible, you should avoid copying files from one workspace location to another, even if you intend to modify one of the copies. That's because beneficial changes will not propagate from one copy to another automatically. Furthermore, the effort required to track the propagation of changes manually often becomes prohibitive, and the need to do so is often imperative.
To share a file as-is, create a symbolic link instead.
To share a file with trivial modifications, try generating each version from a common base, either though #ifdef, decomposition, inheritance or filtering.
To share a file with substantial modifications, use Perforce inter-file branching. However, remember always to integrate between a given pair of workspace locations from within the same branch, or else the integration relationship will become ambiguous. Also, if you create a branch specification for an inter-file branch, then its name should not end with a slash ("/").
Don't copy just because you need to update the file. The old version can be retrieved later (unless its type includes the +S modifier), and it's probably labeled if it's important. In the rare instance (such as interoperability testing) that you need an old snapshot to coexist with new versions, use inter-file branching to project the required subtree to a new location whose pathname includes the change number or label name.
If you rename a file, maintain the integration history so that subsequent changes don't get orphaned. Run p4 help rename for details.
A4 automatically generates the client view based on the depot location of the branch on which the client is based and that branch's file list. The default file list is the same as that of the parent branch, or the entire workspace tree if there is none.
While the TRUNK branch normally includes the entire tree, other branches would optimally include only those files that are relevant. For example, a project branch doesn't need to include files used only by another project, and the efficiency of client operations is reduced, sometimes dramatically, if it does include them.
In order to change the file list of a branch, edit the CFG/<branch-name>/top/files file and submit it. In case there are a lot of files that are relevant but rarely used, you can also distinguish between the default file list and the integration file list. It is also possible to modify the file list for individual clients. See the a4intro(1) man page for details.
As a rule, you shouldn't do that, even though it's possible if you edit the client view directly.
The reason that "mixed" views should be avoided is that they are difficult to reproduce. As long as you let A4 manage the view for you, then you can definitively specify any source configuration with a branch name and a changelist number.
To work on data from multiple branches, you should instead select a branch that has all the relevant files in-view, and wait for the relevant changes to propagate to that branch. This requires a little patience, but it's a lot less chaotic. If you find yourself doing it often, then you probably ought to consider combining the affected branches into a single branch whose view and regression include those of affected branches.
A branch can be designated as gated, meaning that its regression is run on each submission in strict order, thusly:
% a4 branch -g <branch-name>
The regression script is given by the CFG/<branch-name>/top/regr executable file.
Note that every branch (including the TRUNK) has a different regression. This is a good thing, but you'll need to avoid making redundant copies by calling common code from a shared location, preferably outside the CFG subtree, which is forced into the file list of every branch.
Nope.
In order to do useful things with a workspace, you usually need to generate a lot of files via some build system such as make(1). This might take a long time, perhaps even days.
You shouldn't attempt to get around this by storing generated files in the depot, because that leads to portability and maintenance problems, as well as difficulties with the build system and depot storage capacity issues.
Instead, you can deal with this by using reference builds. The idea is that a particular configuration (i.e. change number) of a particular branch is built in a well-known location, typically by a pseudo-user with a basic environment. Clients can then be built starting with the reference build as a basis thusly:
% a4 newclient -N % cd <client-name> % a4 clone <change-num> % a4 sync
At this point, the workspace can be rebuilt, and only the files affected by the difference between the reference build and the latest changes are regenerated (assuming that the build system is smart enough to figure that out using the file modification timestamps).
Pretty slick, eh?
The build system might need to know where the root of the workspace is, such that it can construct an absolute location within the workspace. The easiest may to do this is to use the .p4top symbolic link that is automatically generated in every directory in the workspace. Because each of these links points to the same directory, you can use a path beginning with .p4top where an absolute path would normally be required, such as the PREFIX installation variable of many UNIX programs.
To update these symbolic links after creating new directories, run this:
% a4 maketree
Note that the existence of these links effectively prevents you from using cp -r on a workspace. You probably don't really want to do that anyway, because it probably means that you're subverting the source control system.
Alternatively, you could run a4 top to find the true absolute path to the top of the workspace. However, care must be taken to ensure that this doesn't effectively prevent reference builds from being cloned.
If you make edits to a client based on the wrong branch, you might not be able to submit at all. This is a good thing to the extent that it prevents the branch from being corrupted, but you still need to be able to submit those edits to the correct branch without reproducing them from scratch.
That's where a4 receive comes in handy. All you need to do is to create a client based in the correct branch, and then transfer the changes from the wrong client thusly:
% cd right_client % a4 receive wrong_client
This is implemented by generating a patch file based on wrong_client, and then applying those changes to right_client using the patch(1) program. You can access these operations independently using a4 createpatch and a4 applypatch.