Skip to main content

Git Perforcesson

git-p4son is a tool for managing a local git repository within a Perforce workspace. This way you can use the familiar local git development flow even though you are working on a project where perforce is used for source control.

The idea is to have a main git branch that is kept in sync with a branch of a Perforce depot. From main you branch out into feature branches, where you do local changes and rebase on main whenever it is updated.

Once your changes is ready to be submitted in perforce git-p4son can help you create both changelists and push them for reviews. You can even push individual git commits as patches for review, so that the reviewer can see the changes in the order you made them locally. Similar to the pull request worfkflow on github.

This is a bit cumbersome to do manually, but this package provides commands that help out with the repetitive and error prone stuff.

On big workspaces

Sometimes git struggles to keep entire perforce workspaces in the repo, like when working on a Unreal Engine game with all source code in the workspace. If so, it helps to edit .gitignore and include only the subdirectories in which you work, the rest of the files will still be managed by perforce of course.

Usage Example

Here's a typical workflow when developing a feature using git-p4son:

# Sync main with latest changes from perforce
git checkout main
git p4son sync

# Start work on a new feature
git checkout -b my-fancy-feature

# Change some code
git add .
git commit -m "Feature part1"

# Sync to the latest changelist affecting the workspace
git checkout main
git p4son sync

# Rebase your changes on main
git checkout my-fancy-feature
git rebase main

# Change even more code
git add .
git commit -m "Feature part2"

# Create a Swarm review with all commits since main in one go.
# This opens an interactive rebase with pre-filled exec lines updating
# the changelist with git-p4son after each picked commit.
# An alias called "my-fancy-feature" (derived from the branch name) is
# saved so you can refer to this CL by name in follow-up commands.
git p4son review -m "My fancy feature" -b main

# After review feedback, make more changes
git add .
git commit -m "Address review feedback"

# Update the changelist with latest commit, re-open files, and re-shelve.
# The changelist is looked up by branch name automatically.
git p4son update --shelve

# After approval, submit in p4v

# Sync to the latest changelist from perforce
git checkout main
git p4son sync

# Force remove old branch as you don't need it anymore
git branch -D my-fancy-feature

# Remove changelist alias for the branch
git p4son alias delete my-fancy-feature

# Start working on the next feature
git checkout -b my-next-fancy-feature

Installation

pip install git-p4son

Requires Python 3.11 or later.

Updating

pip install --upgrade git-p4son

For tab completion (zsh and PowerShell), see Shell Completions.

Development

To install from source in editable mode:

git clone https://github.com/neoboid/git-p4son.git
cd git-p4son
pip install -e .

The -e flag installs the package in "editable" mode, meaning changes to the code are immediately available without reinstalling. This is also handy if you want to auto-update git-p4son whenever you pull from GitHub.

git-p4son only uses Python standard library modules - no additional packages are required.

Git Hooks

The repository includes git hooks in the hooks/ directory:

  • pre-commit - verifies that staged Python files are formatted with autopep8. Install it with pip install autopep8.
  • pre-push - runs the test suite and blocks the push if any test fails.

To activate the hooks, run:

scripts/setup-hooks.sh

This configures core.hooksPath so git uses the hooks from the repository. You only need to run this once after cloning.

Setup

These steps set up git-p4son in an existing Perforce workspace. You only need to do this once.

  1. Set a git editor if you don't have one already. The review command opens an interactive rebase in your editor. If you haven't configured one, set it with:

    git config --global core.editor <editor>   # e.g. vim, nano, code --wait
    
  2. Sync your workspace to a known changelist. Pick a changelist to use as the starting point for your git history:

    p4 sync //...@12345
    
  3. Run git p4son init. This can be anywhere inside your Perforce workspace - it doesn't have to be at the root:

    cd /path/to/your/workspace    # or a subdirectory of it
    git p4son init
    

    The command verifies that you are inside a P4 workspace, prompts you to select a depot root (entire workspace or current directory subtree), runs git init, sets up .gitignore (copying from .p4ignore if available), and creates an initial commit.

  4. Review .gitignore. Edit the file to ensure build artifacts and other unwanted files are excluded.

  5. Add and commit all files manually the first time

    git add .
    git commit -m "Initial submit all files"
    
  6. Run git p4son sync to get your first official sync commit

    git p4son sync
    

    This creates a commit with message that records the CL you have synced.

From here, branch off main for local development. See the Usage Example for a typical workflow.

How sync handles writable files

When you switch branches with git checkout, git removes the read-only flag on every file it writes. This means that when you switch back to main for a sync, Perforce may report "Can't clobber writable file" errors. git-p4son handles these automatically by classifying each writable file:

writable-unchanged - The file on disk is identical to the last sync (only the read-only flag was removed by git). This is the common case after switching branches. git-p4son detects this by comparing git blob IDs against the last sync commit that touched the file, restores the read-only flag, and lets the sync overwrite the file.

writable-changed - The file on disk differs from the last sync. This happens when you manually edit a file on main and commit it to git without submitting to Perforce. git-p4son lets the sync overwrite the file and includes it in the sync commit (so the commit reflects pure Perforce state), then three-way merges your local changes on top of the new Perforce content - similar to a git rebase. If the merge is clean, the result appears as an unstaged change. If there are conflicts, standard conflict markers are inserted for you to resolve.

added both locally and in Perforce - A file you committed to git is also being added by Perforce (p4 has never synced it to your workspace). There is no common baseline, so git-p4son merges the two versions against an empty base: identical content merges cleanly, otherwise the file gets a single conflict block containing both full versions for you to resolve.

binary files - Binary files that have local changes cannot be three-way merged. git-p4son restores your local version to disk after the sync commit so it shows up in git status for you to handle manually.

added locally, deleted upstream - A file with local changes in git was deleted in Perforce. git-p4son lets the delete stand and warns you - your version remains available in git history.

deleted locally, added upstream - A file you deleted from git was modified in Perforce. The Perforce version is kept in the sync commit. You can delete it again if you want.

ignored - Files ignored by .gitignore are skipped with a warning. These are outside git's domain and should be managed manually or via Perforce directly.

After syncing, git-p4son prints a summary of how each writable file was handled, and instructs you to review and commit any files that need attention.

Usage

git-p4son provides nine commands: init, sync, sync-split, new, update, review, list-changes, alias, and completion.

To see help for any command, use -h:

git p4son -h
git p4son sync -h

Note: When invoking via git p4son, the --help flag is intercepted by git (to look for man pages). Use -h instead, or git p4son -- --help to force it through. Alternatively, call the executable directly: git-p4son --help.

Global options:

  • -v, --verbose: Show verbose output (commands, elapsed times, raw subprocess output)
  • --version: Show program version

Init Command

Initialize a git repository inside a Perforce workspace:

git p4son init

This command checks preconditions (Perforce workspace), configures the depot root, runs git init, sets up .gitignore, and creates an initial commit.

The depot root determines which part of the Perforce workspace git-p4son syncs. You can choose to sync the entire workspace or just the directory's subtree where the git root is placed. The selection is saved in .git-p4son/config.toml and used by all subsequent commands.

The saved root uses a $(workspace) placeholder for the workspace (client) name, e.g. root = "//$(workspace)/Engine". git-p4son substitutes the live workspace name each time it runs, so renaming the Perforce workspace does not break the config. You can still store a concrete name (e.g. //my-workspace/Engine) by hand-editing config.toml; both forms work.

The .gitignore is set up using this priority:

  • If .gitignore already exists, it is left as is
  • If .p4ignore exists, it is copied to .gitignore as a starting point
  • Otherwise, an empty .gitignore is created

Sync Command

Sync local git repository with a Perforce workspace:

git p4son sync [changelist ...] [--force]

Arguments:

  • changelist (optional): Changelist number, last-synced to re-sync the last synced changelist, or head to sync to the latest changelist. Omit to sync to the latest changelist affecting the workspace. Several strictly increasing changelist numbers may be given to sync each in sequence, producing one commit per changelist. A trailing head may close out such a sequence to finish at the latest changelist.

Options:

  • -f, --force: Allow syncing to changelists older than the current one.

Examples:

git p4son sync              # sync to latest
git p4son sync head         # sync to latest (explicit)
git p4son sync 12345
git p4son sync 123 156 178  # sync each changelist in sequence, one commit each
git p4son sync 123 156 head # sync 123, 156, then the latest changelist
git p4son sync last-synced
git p4son sync 12345 --force

pre-sync hook

Before git-p4son sync syncs any changelist, git-p4son runs hooks from .git-p4son/hooks/pre-sync/. If any hook exits non-zero the sync is aborted. When a sequence of changelists is given, the pre-sync hooks run once, before the first sync.

post-sync hook

After a successful git-p4son sync that actually performs sync work, git-p4son runs hooks from .git-p4son/hooks/post-sync/.

Sync-Split Command

Sync forward from the last synced changelist, splitting selected users' changelists into their own commits:

git p4son sync-split [changelist] [--user NAME ...] [--dry-run]

Syncing straight to the latest changelist lumps your own submitted changelists together with everyone else's in a single commit. sync-split resolves the sequence for you: it looks up the last synced changelist in the git log, asks Perforce which changelists the selected users submitted since then, and syncs the changelist submitted immediately before each of those first. The result is a git history where every changelist those users submitted is a commit containing nothing but their own change.

By default the selected user is you (the current p4 user), which covers the common case of reviewing your own submits one at a time. Repeat --user to split out several people's changelists in the same run.

It is equivalent to working out the numbers by hand and running git p4son sync <before-theirs> <theirs> ... head, and it delegates to sync once the sequence is resolved, so pre-sync and post-sync hooks, writable-file merging, and the clean-workspace checks all behave exactly as they do for sync. The clean-workspace checks and the pre-sync hooks are the one thing that runs earlier: resolving the sequence costs several Perforce queries, so they go first, and a dirty workspace or a hook that aborts the sync says so before that work. They run once for the whole sync, and not at all for a --dry-run or when there is nothing to sync.

Arguments:

  • changelist (optional): Changelist number to sync up to, or head for the latest. Omit to sync to the latest changelist affecting the workspace.

Options:

  • -u, --user NAME: Perforce user whose changelists to split into their own commits. Repeat the flag to select several users. Defaults to the current p4 user (from p4 info).
  • -n, --dry-run: Print the resolved sync sequence without syncing.

Examples:

git p4son sync-split                  # sync to latest, your own changelists split out
git p4son sync-split head             # same, explicit
git p4son sync-split 12345            # stop at changelist 12345
git p4son sync-split --dry-run        # show the sequence that would be synced
git p4son sync-split -u alice         # split out alice's changelists instead of yours
git p4son sync-split -u alice -u bob  # split out both alice's and bob's changelists

For example, with the last sync at CL 100 and your own changelists at 102 and 105, sync-split resolves to git p4son sync 101 102 104 105 106, producing five commits where CL 102 and CL 105 each hold only your change.

sync-split only moves forward: it requires a previous sync to start from, and refuses a target older than the last synced changelist (use git p4son sync --force for that).

New Command

Create a new Perforce changelist and add changed files to it. Description will contain an enumerated list of git commits since the base branch. Optionally creates a Swarm review.

git p4son new -m <message> [alias] [--base-branch BASE_BRANCH] [--force] [--dry-run] [--no-edit] [--no-alias]
                           [--shelve] [--review]

Arguments:

  • alias (optional): Alias name to save the new changelist number under. Defaults to the current branch name.

Options:

  • -m, --message MESSAGE: Changelist description message (required)
  • -b, --base-branch BASE_BRANCH: Base branch for enumerating commits and finding changed files. Default is HEAD~1
  • -f, --force: Overwrite an existing alias file
  • -n, --dry-run: Pretend and print what would be done, but do not execute
  • --no-edit: Skip opening changed files for edit in Perforce
  • --no-alias: Skip saving a changelist alias
  • --shelve: Shelve the changelist after creating it
  • --review: Add #review keyword and shelve to create a Swarm review
  • -s, --sleep SECONDS: Sleep for the specified number of seconds after the command is done

Examples:

git p4son new -m "Fix login bug"
git p4son new -m "Add feature" -b main
git p4son new -m "Fix bug" myalias
git p4son new -m "Fix bug" --no-alias
git p4son new -m "New feature" --review -b main

Update Command

Update an existing Perforce changelist description. Commits since the base branch replace their existing entries in the enumerated commit list (matched by subject) and new ones are appended; entries outside the range are kept and the list is renumbered. So update -b main rebuilds the whole list without duplicating it, while update -b HEAD~3 only refreshes the last three entries. By default also opens changed files for edit.

git p4son update [changelist] [--base-branch BASE_BRANCH] [--dry-run] [--no-desc] [--no-edit] [--shelve]

Arguments:

  • changelist (optional): Changelist number or named alias to update. Defaults to the current branch name.

Options:

  • -b, --base-branch BASE_BRANCH: Base branch for enumerating commits and finding changed files. Default is HEAD~1
  • -n, --dry-run: Pretend and print what would be done, but do not execute
  • --no-desc: Skip updating the changelist description
  • --no-edit: Skip opening changed files for edit in Perforce
  • --shelve: Re-shelve the changelist after updating
  • -s, --sleep SECONDS: Sleep for the specified number of seconds after the command is done

Examples:

git p4son update              # update changelist for current branch
git p4son update --shelve     # update and re-shelve
git p4son update 12345
git p4son update myalias -b main

Review Command

Automate the interactive rebase workflow for creating Swarm reviews. This command generates a rebase todo with exec lines that run git p4son new --review on the first commit and git p4son update --shelve on each subsequent commit, then opens it in your editor for review before executing.

git p4son review [alias] -m <message> [--base-branch BASE_BRANCH] [--force] [--dry-run]

Arguments:

  • alias (optional): Alias name for the new changelist. Defaults to the current branch name.

Options:

  • -m, --message MESSAGE: Changelist description message (required)
  • -b, --base-branch BASE_BRANCH: Base branch to rebase onto and find commits since. Default is HEAD~1
  • -f, --force: Overwrite an existing alias file
  • -n, --dry-run: Print the generated rebase todo without executing

When run, the command generates a todo like this and opens it in your editor:

pick abc1234 First commit
exec git p4son new my-feature --review -m 'My feature'
pick def5678 Second commit
exec git p4son update my-feature --shelve
pick ghi9012 Third commit
exec git p4son update my-feature --shelve

You can edit the todo before saving (e.g. reorder commits, remove lines), or abort by clearing the file — just like a normal git rebase -i. Each exec line automatically sleeps after shelving to give Perforce/Swarm time to process.

If the rebase fails mid-way, you can fix the issue and run git rebase --continue as usual.

Examples:

# Review all commits since main (alias defaults to branch name)
git p4son review -m "Add my feature" -b main

# Review just the last commit (default base branch)
git p4son review -m "Fix bug"

# Preview the generated todo without executing
git p4son review -m "Add my feature" -b main --dry-run

List-Changes Command

List commit subjects since a base branch in chronological order (oldest first):

git p4son list-changes [--base-branch BASE_BRANCH]

Options:

  • -b, --base-branch BASE_BRANCH: Base branch to compare against. Default is HEAD~1.

Examples:

git p4son list-changes
git p4son list-changes --base-branch main

This command is useful for generating changelist descriptions by listing all commit messages since the base branch, numbered sequentially.

Alias Command

Manage changelist aliases stored in .git-p4son/changelists/.

alias list

List all aliases and their changelist numbers:

git p4son alias list

Examples:

git p4son alias list

alias new

Save a changelist number under a named alias:

git p4son alias new <changelist> [alias] [--force]

Arguments:

  • changelist: Changelist number to save
  • alias (optional): Alias name to save the changelist number under. Defaults to the current branch name.

Options:

  • -f, --force: Overwrite an existing alias file

Examples:

git p4son alias new 12345              # alias defaults to branch name
git p4son alias new 12345 myfeature
git p4son alias new 67890 myfeature -f

alias delete

Delete a changelist alias:

git p4son alias delete [alias]

Arguments:

  • alias (optional): Alias name to delete. Defaults to the current branch name.

Examples:

git p4son alias delete              # delete alias for current branch
git p4son alias delete myfeature

alias clean

Clean up changelist aliases:

git p4son alias clean

The command lists all aliases and asks how to proceed:

  • a (all): Delete all aliases
  • i (interactive): Review each alias one by one
  • q (quit): Exit without deleting anything

In interactive mode, each alias is displayed in turn with a prompt:

  • y (yes): Delete this alias
  • n (no): Keep this alias
  • a (all): Delete this and all remaining aliases (handy for keeping the first few and sweeping the rest)
  • q (quit): Stop and keep remaining aliases

Examples:

git p4son alias clean

Completion Command

Print the path to a shell completion script:

git-p4son completion <shell> [--dirname]

Arguments:

  • shell: Shell to print completion script path for (bash, zsh, or powershell)

Options:

  • -d, --dirname: Print the directory containing the completion script instead of the full file path

See Shell Completions below for installation instructions.

The branch keyword

Most commands that accept an alias or changelist argument default to the current branch name. You can also pass the branch keyword explicitly — it resolves to an alias name derived from the current git branch.

The keyword cannot be used in a detached HEAD state. Use --no-alias (on new and review) or supply an explicit alias name instead.

Hooks

Hooks work similarly to git hooks. They are executables (unix)/scripts (windows) that you place in the .git-p4son/hooks/[hook-name]/ directory. These scripts will be called to add or modify behavior at defined times during git-p4son operations. Scripts get run in filename (lexicographically) sorted order.

The hook process CWD is the directory where git-p4son was invoked, and GIT_P4SON_REPO_ROOT_DIR is set to the git-p4son repository root.

On Windows, .ps1, .nu, .sh, and .py files are run with powershell.exe, nushell.exe, bash.exe, and python.exe respectively. Customize or add Windows script associations in .git-p4son/config.toml:

[hooks.extension-associations]
".ps1" = ["pwsh.exe", "-NoProfile", "-File"]
".py" = "python.exe"

Non-executable or unrecognized hook files are skipped with a warning.

pre-sync hook

Before git-p4son sync syncs any changelist, git-p4son runs executable hooks from .git-p4son/hooks/pre-sync/. If any hook exits with a non-zero code the sync is aborted before any changelist is synced. When a sequence of changelists is synced, the pre-sync hooks run only once, before the first sync.

post-sync hook

After a successful git-p4son sync that actually performs sync work, git-p4son runs executable hooks from .git-p4son/hooks/post-sync/.

Example hooks

The examples/hooks/ directory holds ready-made hooks. Nothing installs them for you: symlink or copy the one you want into the matching hook directory in your workspace. Symlinking keeps the hook up to date with the clone it points at, and on macOS and Linux the executable bit comes from the file in the clone, so no chmod is needed.

block-while-running (pre-sync)

Aborts the sync while a named process is running, so a sync cannot swap out assets that a running editor still has loaded. Link it into your workspace's pre-sync hook directory - on macOS and Linux:

ln -s /path/to/git-p4son/examples/hooks/pre-sync/block-while-running.py .git-p4son/hooks/pre-sync/

On Windows, from PowerShell. Note that -Path is the link to create and -Target the existing file, the reverse of the ln -s argument order, and that -Target should be absolute: a relative path is resolved against the current directory and quietly produces a broken link.

New-Item -ItemType SymbolicLink `
  -Path   "C:\p4\my-workspace\.git-p4son\hooks\pre-sync\block-while-running.py" `
  -Target "C:\src\git-p4son\examples\hooks\pre-sync\block-while-running.py"

Creating a symlink on Windows needs either an elevated shell or Developer Mode enabled (Settings > System > For developers). Without one of those, copy the file instead and re-copy it whenever the clone updates.

Which processes block a sync is read from your workspace's own .git-p4son/config.toml, so a symlinked hook stays per-project:

[hooks.block-while-running]
processes = ["UnrealEditor", "UnrealLightmass"]

Names are matched with any .exe suffix stripped and case ignored, so a single entry covers UnrealEditor.exe on Windows and UnrealEditor on macOS. Without that table the hook checks nothing and the sync proceeds; a table that is present but malformed aborts the sync rather than silently skipping the check.

Set GIT_P4SON_SKIP_PROCESS_CHECK=1 to run one sync without the check.

Shell Completions

Tab completion is available for bash, zsh, and PowerShell, including commands, flags, and dynamic alias names.

bash

Add the following to ~/.bashrc:

source $(git-p4son completion bash)

zsh

Add the following to ~/.zshrc before compinit:

fpath=($(git-p4son completion -d zsh) $fpath)
autoload -Uz compinit && compinit

PowerShell

Add the following to your PowerShell profile ($PROFILE):

. $(git-p4son completion powershell)

All three enable completion for git p4son <TAB> and git-p4son <TAB>.

Metadata

Release files for git-p4son 0.3.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for git-p4son 0.3.3
File Size Uploaded
git_p4son-0.3.3.tar.gz 101.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for git-p4son 0.3.3
File Interpreter ABI Platform
git_p4son-0.3.3-py3-none-any.whl Python 3 none any Details

Total release size: 167.5 kB

Release files / git_p4son-0.3.3.tar.gz

Download URL git_p4son-0.3.3.tar.gz
Size 101.4 kB
Tags Source
SHA-256 checksum
How to use checksums
1036bcee2d40e844132213cbd23abda3da582a8867849c02653f1912d0b61d2d
BLAKE2b-256 checksum
How to use checksums
47607169bb04591424c7b6fb53012a214a9a1e152fd5a13db0113f7458901d21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release files / git_p4son-0.3.3-py3-none-any.whl

Download URL git_p4son-0.3.3-py3-none-any.whl
Size 66.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
826435405233d67b12ff49bb189bb598603920d9b194ddba727b1b3d0c87fa03
BLAKE2b-256 checksum
How to use checksums
3e6212b7949d27e90ca12d3cc9088b8b6d45640a0eef23bab7cc78630bb9000c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 27, 2026.

Transparency log

Release history Release notifications | RSS feed

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.4

2 release files

This release

0.3.3 This release

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.11

2 release files

0.2.10

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page