Tutorial: Moving from Subversion (svn) to Perforce (p4)

I suppose most tutorials out there go the other direction, from Perforce to Subversion. Yet I ended up in a situation recently where needed to work on a codebase that was being managed with Perforce. Coming from a Subversion-heavy background, I found myself initially puzzled and frustrated by a lot of conventions in Perforce–especially when it came to using the command line client, p4. So after gaining some familiarity with p4, I decided to write up some notes for anyone else who might be in my situation.

Gotchas

The main thing to keep in mind when moving from svn to p4 is this:
Subversion works off of the file system via .svn folders. This is perhaps slower for large checkouts, but it also means that you don’t have to tell svn every little change you want to make. Using svn st, you can easily get a diff between the repository version and your current checkout.
Perforce, on the other hand, works off a relational database. This allows it to be much faster when doing checkouts, checkins, etc. However, it also means that you have to tell your Perforce client every little thing you want to do. Want to edit a file? Whoa, not so fast–you have to tell Perforce that you’re editing it! Added a file locally and forgot to add it to the changelist? Too bad, p4 won’t (easily) help you out there! These differences means you’re going to have to change your workflow when working on “checked out” code. See the “Best Practices” section below.
Other things to keep in mind:
  • Unlike Subversion, Perforce does not keep folders in version control–only files. This means you’ll see a lot of placeholder.txt in your depot tree.
  • In Subversion, you “check out” a local copy of the code from a given root (for example, svn co http://svn.mysite.com/svn/project/trunk). In Perforce, you must create a workspace and configure a view, which will tell the Perforce client what code you want to check out. In subversion you could have many different roots checked out in different places on your file system, and be able to move between them seamlessly. In Perforce you can only use one workspace (aka client) at a time. See svn co below for more information.
  • In Subversion, you need to be inside your working copy if you want to run svn commands. In Perforce, since you are only working with one workspace/client at a time, you can run p4 commands from anywhere in the filesystem.

Equivalent Commands

svn st

Frustratingly, there is no direct equivalent of svn st in p4. You can get all of information that svn st would give you, but you have to run multiple commands.
The first thing you should look at is p4 opened. This gives you a list of all files that have been modified (with p4 edit), plus files that have been added (p4 add) or deleted (p4 delete).

In Subversion, if there are files you have added or deleted locally, and these files are not in version control, then when you run svn st they will show up marked with a “?”. With p4, there is no easy way to get a list of these sorts of files. You really have to become diligent with your workflow, and never forget to add files after creating them.
In Subversion, if there are files in the repository but you have deleted them locally without telling subversion, it will come up in svn st marked with a “!”. To get this list of files in p4, use p4 diff -sd. Similarly, if you have modified files locally and haven’t told p4 by running p4 edit on the file, you can see the list using p4 diff -se.

In Subversion, if you have made local edits to files and these changes conflict with the latest from the repo, they will come up marked with “C”. To list these files in p4, use p4 resolve -n.

To summarize:


A — p4 opened
M — p4 opened
D — p4 opened
C — p4 resolve -n
! — p4 diff -se / p4 diff -sd / p4 diff -sb
? — (no equivalent)

svn diff

This one is pretty straightforward. p4 diff will give you diffs on all files currently open for edit, so it is very similar to svn diff in that sense. One key difference: p4 diff will NOT tell you files that have been added or deleted, either locally or in a changelist.

svn update

Use p4 sync. You can also specify which files you want to sync. For example, if you want to sync recursively from your current folder on down, you can execute: p4 sync ./…

If p4 sync isn’t behaving how you expect, or you’ve somehow screwed up your workspace, you can try p4 sync -f. The ‘-f‘ parameter “forces” the sync. While this command can be very useful, you really need to understand what you’re doing when you use it, because it’s possible you could blow away your changes.

So here’s what’s going on: When you run p4 sync the first time, Perforce remembers what you have synced and assumes the files are still there in the same state in your local copy. So if, say, you accidentally deleted a file and you try running p4 sync again, p4 will do nothing. If you force the sync, your Perforce client will “refresh”–that is, re-download–all of your files from the depot. For every file that it finds, if you’re not currently editing the file, it will overwrite that file. This means that, depending on your settings, running a force sync may clobber writable files. In other words, if you have been editing a file manually, without running p4 edit beforehand, your changes will be blown away.


svn commit

Use p4 submit.

svn checkout

To get a revision of the code, you must setup a workspace (aka client) and define one or more views for that workspace.
First, make sure the P4PORT environment variable is set (Confusingly enough, this variable is actually the full url plus port of the perforce server). E.g.,
export P4PORT="perforce.mydomain.com:1234"

Then, setup your client by running p4 client. This will open up a text editor with a bunch of defaults. It might look something like this

# A Perforce Client Specification.
## Client: The client name.
# Update: The date this specification was last modified.
# Access: The date this client was last used in any way.
# Owner: The user who created this client.
# Host: If set, restricts access to the named host.
# Description: A short description of the client (optional).
# Root: The base directory of the client workspace.
# AltRoots: Up to two alternate client workspace roots.# Options: Client options:
# [no]allwrite [no]clobber [no]compress
# [un]locked [no]modtime [no]rmdir
# SubmitOptions:
# submitunchanged/submitunchanged+reopen
# revertunchanged/revertunchanged+reopen
# leaveunchanged/leaveunchanged+reopen
# LineEnd: Text file line endings on client: local/unix/mac/win/share.
# View: Lines to map depot files into the client workspace.
## Use 'p4 help client' to see more about client views and options.

Client: jaustin-laptop

Owner: vaustje

Host: jaustin-laptop

Description:Created by jaustin.

Root: /home/jaustin/work/jaustin

Options: noallwrite noclobber nocompress unlocked nomodtime normdir

SubmitOptions: submitunchanged

LineEnd: local

View://depot_root/... //jaustin-laptop/depot_root/...
The main thing you care about are “Client:“, “Root:” and “View:“. Client will default to your hostname, and you can change it to whatever you want. It must be unique, so if you’ve already created a workspace with the name my_awesome_workspace, you can’t create it again. Root is where the code will be downloaded to, and defaults to your current directory. View lists the mapping between the code in the depot and your local checkout. It’s kind of like running svn co http://svn.mydomain.com/svn/project/trunk to get everything, versus running svn co http://svn.mydomain.com/svn/project/trunk/web/src/main/resources to get a specific part of the repository.
After you’ve configured that file how you want, save and close it. p4 should then say something like

Client jaustin-laptop saved.

To use your workspace, you must set the P4CLIENT variable:
export P4CLIENT="jaustin-laptop"

Finally, you can run p4 sync to download the latest revisions of files in your view.

If you use a client often, I recommend adding the P4PORT and P4CLIENT variables to some file that is always sourced, for example .bashrc or .bash_profile. That way you don’t have to worry about setting them for every session.

svn info

Use p4 info.

svn log

Use p4 filelog.

svn add

Use p4 add.

svn copy

As far as I can tell this doesn’t exist? Please correct me if I’m wrong.

svn delete

Use p4 delete. Keep in mind that you can’t delete a file if it is open for edit.

svn move

Newer versions of the p4 client (2009.1 onwards) have a move command, so you can move a file by running p4 edit followed by p4 move against a file.
If you have an older version of p4, you need to run p4 integrate followed by p4 delete on the original file. Like so:
p4 integrate //depot/foo/bar.c //depot/foo/baz.c
p4 delete //depot/foo/bar.c
p4 submit

svn revert

Use p4 revert.

Best Practices

DON’T get around Perforce by manually edit file permissions

This will only cause you pain and headaches. If you can’t understand why you can’t get the file open for edit, it’s better to figure out that issue early on, because if you manually edit files, by the time you want to submit you’ll be completely lost.
DO get in the practice of telling p4 everything you want to do
I’ve seen the following scenario happen a lot of times: Developer opens up a file that’s under a Perforce workspace and makes some edits. When they go to save, the editor tells them that the file is read-only and prompts them to override and save anyway. The dev says, “Oh, I just want to make a temporary change that I plan to revert… I’ll just save the file and not worry about running p4 edit.” Afterwards, they completely forget that they made the edit. This means that a few weeks down the road, their edit will be either completely forgotten when submitting code back to the depot, or it will be blown away when they run a p4 sync -f. Please, even for quick edits that you plan to revert immediately, just tell p4 what you are doing. It will save you a lot of headaches in the future.

Use a GUI tool or IDE plugin if possible

Unlike svn, it seems that p4 is not intended to be the ultimate solution for developers working in Perforce. Frankly, it’s limited and difficult to use for anything more than the simple add, edit, and revert commands. There are tools built upon it, however, that will give you that functionality that is missing and remove some of the frustrations of developing using p4. P4V is a common GUI tool, and I always use it when doing something tricky like a branch integration. If you’re using an IDE for development, I definitely recommend getting a plugin. p4wsad is a decent plugin for Eclipse, and JetBrains products, like Intellij IDEA and RubyMine, come with built-in Perforce integration that works pretty well.
Run p4 sync twice (followed by p4 resolve -n)
Most of the time, when you run p4 sync, a lot of information will go flying by on the screen. In all that, there might be some useful messages that you missed. One of these messages is “Can’t clobber writable file.” This occurs when you’ve made a read-only file writable. Perforce doesn’t want to clobber this file, so it will skip over it when running a sync. This is a very important message, because it means that your workspace isn’t actually up-to-date! My solution to this is to always run p4 sync twice–the first time to pull down the latest revisions, and the second time to makes sure I see the message File(s) up-to-date.

Another thing I like to run after a sync is p4 resolve -n. This command will tell me what files I have locally opened for edit that conflict with the latest in the depot. While Perforce will force me to resolve these conflicts before submitting, I like to know about issues BEFORE running the submit, and the messages telling me about conflicts are also often lost in the huge list of changes when I run p4 sync.

Tips and Tricks

Checking out your entire client workspace

Since Perforce wants you to tell it every time you edit a file or add something new, it will actuallychange the file permissions when you get code from the depot. If you have a file that has not been checked out with p4 edit, it will be in read-only mode. Of course this means that you will often land in the frustrating position of opening a file, editing it, and then not being able to save it when you’re done. While I don’t recommend it except for in special cases, sometimes checking out your entire workspace is a good option. For one thing, it will make a p4 diff pretty pointless, and reverting unchanged files afterwards is dangerous, but at least you don’t have to worry about file permissions as you go about your business.

You can do a recursive checkout with the following command (‘…’ acts as a recursive wildcard):
p4 edit //workspace_name/...

When you’re ready to submit and want to get rid of all the unchanged files that show up when you run p4 diff, you can run this to revert all unchanged files:
p4 revert -a //workspace_name/...

This is somewhat dangerous since if you forget the ‘-a‘ you risk reverting your local changes! If you find yourself doing this a lot, I recommend making an alias for p4 revert -a that will save you from a mistake.

Marking a file as always executable

If you have scripts checked into Perforce, and want to set the execute flag, run the following against the file:

p4 edit -t text+x FILE_NAME

For example:

[hosting@somebox tmp]$ ls -l
total 1-r--r--r-- 1
hosting hosting 1407 Mar 17 09:38 some-script.sh
[hosting@somebox tmp]$ p4 edit -t text+x some-script.sh
[hosting@somebox tmp]$ ls -l
total 1-rwxrwxr-x 1
hosting hosting 1407 Mar 17 09:38 some-script.sh
[hosting@somebox tmp]$ p4 submit some-script.sh