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. To let Claude Code drive git-p4son, see Claude Code Skill.
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.
Run the tests with python -m pytest tests/ (pip install pytest). With pytest-xdist installed, add
-n auto --maxprocesses 8 to run them in parallel, which is about three times faster.
Git Hooks
The repository includes git hooks in the hooks/ directory:
- pre-commit - verifies that staged Python files are formatted with
autopep8. Install it withpip install autopep8. - pre-push - runs the test suite and blocks the push if any test fails. It needs
pytestinstalled, and runs the tests in parallel whenpytest-xdistis too.
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.
-
Set a git editor if you don't have one already. The
reviewcommand 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
-
Sync your workspace to a known changelist. Pick a changelist to use as the starting point for your git history:
p4 sync //...@12345
-
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.p4ignoreif available), and creates an initial commit. -
Review
.gitignore. Edit the file to ensure build artifacts and other unwanted files are excluded. -
Add and commit all files manually the first time
git add . git commit -m "Initial submit all files"
-
Run
git p4son syncto get your first official sync commitgit 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 refuse to overwrite those files because they are
writable. 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.
Workspaces with allwrite - When your client spec has the allwrite option, every file is writable by design,
so the read-only flag carries no information about local changes. git-p4son classifies files exactly as above (the
verdict comes from git, not from the flag), but leaves the write bit alone on files it finds unchanged: Perforce
compares digests before refusing to overwrite a writable file, and unchanged files pass that check. Only files with
local changes are made read-only for the duration of the sync, since those are the ones Perforce would otherwise
refuse to overwrite. The same digest check applies to git-ignored files: unchanged ones sync normally, and after the
sync git-p4son lists the ones Perforce skipped because they were modified locally.
Writable mode - With writable mode on, the files a sync touches are made writable again after
the sync if git tracks them. The sync itself works as described above, since the workspace is not allwrite.
Configuration
git-p4son keeps its per-repository settings in .git-p4son/config.toml. init, writable and sync-split-users
write most of them; the rest you edit by hand.
[depot] root(string, set byinit): the depot pathsyncsyncs, e.g.//$(workspace)or//$(workspace)/Engine. See Init Command[core] writable(trueorfalse, defaultfalse): writable mode, which keeps git-tracked files writable[sync] split-users(list of strings, default empty): the users whose changelistssyncgives a commit each;$(user)stands for you. See Split users[sync] blocking-processes(list of strings, default empty): processes that stop a sync while they run. See Blocking processes[hooks.extension-associations](table, Windows only): the command that runs a hook script, by file extension. See Hooks
For example:
[depot]
root = "//$(workspace)"
[core]
writable = true
[sync]
split-users = ["$(user)", "alice"]
blocking-processes = ["UnrealEditor"]
A value of the wrong type, such as writable = "true" or split-users = "alice", or a file that is not valid TOML,
stops the command with an error naming the setting, so a typo is never silently ignored.
Usage
git-p4son provides eleven commands: init, sync, sync-split-users, new, update, review,
list-changes, alias, writable, completion, and skill.
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 that it runs in a Perforce workspace, then asks for the depot root, writable mode, split users
and the Claude Code skill. It then sets up .gitignore, runs git init and creates a first commit holding only
.gitignore. It ends by listing the next steps, and warns when no git editor is configured.
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.
Nothing in .git-p4son/ belongs in git. The folder has its own .gitignore that ignores everything in it, so there
is no need to list it in the workspace .gitignore.
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
.gitignorealready exists, it is left as is - If
.p4ignoreexists, it is copied to.gitignoreas a starting point - Otherwise, an empty
.gitignoreis created
init also asks whether to turn on writable mode, defaulting to the current setting. It only
saves the answer; run git p4son writable apply once your files are committed to update their permissions.
init then asks whether to sync your own changelists individually, making you a split user.
The default is the current setting. Yes adds a $(user) entry to the split users and no removes it; other split
users are left alone.
If Claude Code is installed, init offers to install the Claude Code skill. An
already installed skill is refreshed without asking.
Sync Command
Sync local git repository with a Perforce workspace:
git p4son sync [changelist ...] [--force] [--dry-run] [--split-user NAME ...] [--no-split] [--ignore-blocking-processes]
Arguments:
changelist(optional): Changelist number,last-syncedto re-sync the last synced changelist, orheadto 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 trailingheadmay close out such a sequence to finish at the latest changelist.
sync requires clean git and Perforce workspaces: no uncommitted changes, untracked files included, and no
git-tracked files opened in Perforce. It checks them after any blocking processes and before
the pre-sync hooks.
Options:
-f, --force: Allow syncing to changelists older than the current one-n, --dry-run: Print the resolved sync sequence without syncing. The arguments are validated as for a real sync, but nothing else runs: no clean-workspace checks, no hooks, and no prompts. Ends withDry run, nothing changed-u, --split-user NAME: Also split out this user's changelists, on top of the configured split users. Repeat to give several users. Each name is checked against the Perforce server.--no-split: Ignore the configured split users for this sync. Users given with--split-userare still split out, so--no-split -u alicesplits out only alice's changelists.--ignore-blocking-processes: Sync even while a blocking process is running.
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
git p4son sync --dry-run # show the changelists a sync to latest would visit
git p4son sync -u alice # also split out alice's changelists
git p4son sync --no-split # sync without splitting out the configured split users
Blocking processes
Syncing while e.g. the Unreal editor is open lets p4 replace assets the editor still has loaded, so the editor
keeps working against files that no longer match what is on disk. List such processes in .git-p4son/config.toml
and sync refuses to start while any of them is running:
[sync]
blocking-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. The check runs before the clean-workspace checks and the pre-sync hooks. A
malformed setting, or a process list that cannot be read, aborts the sync rather than silently
skipping the check.
Pass --ignore-blocking-processes to sync once without the check, or remove the setting to turn it off for good.
Split users
Syncing straight to the latest changelist lumps your own submitted changelists together with everyone else's in a
single commit. Split users fix that: for each changelist a split user submitted since the last sync, sync first
syncs the changelist submitted immediately before it, so every changelist they submitted becomes a commit containing
nothing but their own change.
The split users are stored in .git-p4son/config.toml, and edited with the
sync-split-users command or by answering yes in init:
[sync]
split-users = ["$(user)", "alice"]
$(user) stands for the current Perforce user (from p4 info), resolved each time sync runs, so the config
survives a change of user, the same way $(workspace) works in the depot root.
For example, with the last sync at CL 100, your own changelists at 102 and 105, and the latest changelist at 106,
sync visits 101 102 104 105 106, producing five commits where CL 102 and CL 105 each hold only your change.
Changelists given on the command line are all still visited, with the split points merged in: sync 103 head
visits 101 102 103 104 105 106.
Splitting only moves forward from a previous sync. The first sync, last-synced, and syncing to an older changelist
with --force are never split. Without any split users, sync makes no extra Perforce queries.
Finding the split users' changelists costs one extra Perforce query over the range being synced. It runs after the
blocking processes check, the clean-workspace checks and the pre-sync hooks, so a running blocking process, a dirty
workspace or a hook that aborts the sync says so before that work. --dry-run skips the checks and hooks but still
shows the resulting sequence.
Hooks
sync runs the pre-sync and post-sync hooks.
Sync-Split-Users Command
Show or edit the split users, the Perforce users whose changelists sync gives a commit each:
git p4son sync-split-users # list the split users (same as list)
git p4son sync-split-users list
git p4son sync-split-users add --me # add yourself, stored as $(user)
git p4son sync-split-users add alice bob
git p4son sync-split-users delete alice
git p4son sync-split-users delete --me
listprints one user per line.$(user)is shown with the name it stands for, e.g.$(user) (alice).addchecks every name against the Perforce server first, since a misspelled name would never match a changelist. If any name is not a Perforce user, nothing is added. Names are stored as the server spells them, and a name that is already a split user is skipped.deleteremoves names regardless of case. If any name is not a split user, nothing is removed.
--me adds or removes the $(user) entry. A quoted '$(user)' does the same, but the quotes matter: unquoted,
bash, zsh and PowerShell run $(user) as a command substitution.
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.
Opening files requires a clean git workspace, untracked files included; new refuses to run otherwise. The check
is skipped with --no-edit. After opening files, unchanged ones are reverted (see
Keeping the changelist in sync with git).
git p4son new [-m <message> | -F <file>] [alias] [--base-branch BASE_BRANCH] [--force] [--dry-run] [--no-edit]
[--no-alias] [--no-commit-list] [--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. Defaults to the HEAD commit subject-F, --file FILE: Read the changelist description message from a file instead of-m. The message may span multiple lines--no-commit-list: Leave the enumerated commit list out of the changelist description, so it holds only the message-b, --base-branch BASE_BRANCH: Base branch for enumerating commits and finding changed files. Default isHEAD~1-f, --force: Overwrite an existing alias file-n, --dry-run: Pretend and print what would be done, but do not execute. Ends withDry run, nothing changed--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#reviewkeyword and shelve to create a Swarm review
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. The message above the list is kept unless -m
or -F replaces it. By default also opens changed files for
edit, and reverts files that are no longer part of the git change (see
Keeping the changelist in sync with git). Opening files requires a
clean git workspace, untracked files included; update refuses to run otherwise. The check is skipped with
--no-edit.
With --per-commit, update runs once per commit since the base branch instead of once for the whole range, the
way review does after creating the changelist. It replays the commits with an interactive rebase, without opening
an editor, and runs git p4son update after each one. With --shelve, every commit gets a shelf of its own and so
shows up as its own patch in the Swarm review. Use it after making several commits since the last update:
update -b HEAD~2 --per-commit --shelve updates the changelist with the last two commits, one at a time. The
changelist is resolved before the rebase starts, and -m/-F is applied on the first step only. The rebase needs a
clean git workspace, also with --no-edit. If a step fails, the rebase stops; fix the problem and run
git rebase --continue.
git p4son update [changelist] [-m <message> | -F <file>] [--base-branch BASE_BRANCH] [--dry-run]
[--no-commit-list] [--no-edit] [--shelve] [--per-commit]
Arguments:
changelist(optional): Changelist number or named alias to update. Defaults to the current branch name.
Options:
-m, --message MESSAGE: Replace the changelist description message above the commit list. The commit list is kept and updated as usual, and so is a Swarm#reviewkeyword on a line of its own at the end of the description-F, --file FILE: Like-m, but read the message from a file. The message may span multiple lines-b, --base-branch BASE_BRANCH: Base branch for enumerating commits and finding changed files. Default isHEAD~1-n, --dry-run: Pretend and print what would be done, but do not execute. Ends withDry run, nothing changed--no-commit-list: Leave the enumerated commit list as it is. Without-mor-Fthe description is not touched at all--no-edit: Skip opening changed files for edit in Perforce--shelve: Re-shelve the changelist after updating--per-commit: Update the changelist once per commit since the base branch, oldest first, through an interactive rebase. With--shelve, steps sleep 5 seconds between them to give Perforce/Swarm time to process each shelf.--dry-runprints the generated rebase todo. A message from-mor-Fis saved to.git-p4son/reviews/messagefor the first step
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
git p4son update -F description.txt # replace the message, e.g. after review feedback
git p4son update -b HEAD~2 --per-commit --shelve # one shelf for each of the last two commits
Keeping the changelist in sync with git
new and update open every file changed since the base branch in the changelist. A file can later stop being
part of the change, for example when a later commit undoes an edit, or a file is added in one commit and deleted
in the next. Such files would otherwise stay opened as no-ops, so after opening files both commands revert them:
- Files opened for edit that are unchanged compared to the depot are reverted with
p4 revert -a. Changed files stay opened. - Files opened for add that are missing from disk are reverted.
- Files opened for delete that git still has (deleted in one commit, restored in a later one) are reverted and restored from git.
Only files git-p4son has authority over are touched: files tracked by git, and files opened for add that are
missing from disk. Anything else in the changelist, such as binaries that only exist in Perforce and were opened
by hand, is left alone even when unchanged. Files opened with other actions (move/add, integrate, ...) are
also left alone. This step is skipped with --no-edit.
Shelving (--shelve, --review) replaces the whole shelf with the changelist's opened files using
p4 shelve -r -a leaveunchanged, so the shelf never holds stale files, and unchanged files that are still opened
are left out of it.
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. Like new, it needs a clean git workspace, untracked
files included, and checks it even with --dry-run.
git p4son review [alias] [-m <message> | -F <file>] [--base-branch BASE_BRANCH] [--force] [--dry-run]
[--no-commit-list] [--no-edit-todo]
Arguments:
alias(optional): Alias name for the new changelist. Defaults to the current branch name.
Options:
-m, --message MESSAGE: Changelist description message. Defaults to the HEAD commit subject-F, --file FILE: Read the changelist description message from a file instead of-m. The message may span multiple lines-b, --base-branch BASE_BRANCH: Base branch to rebase onto and find commits since. Default isHEAD~1-f, --force: Overwrite an existing alias file-n, --dry-run: Print the generated rebase todo without executing. Ends withDry run, nothing changed--no-commit-list: Leave the enumerated commit list out of the changelist description. Passed on tonewand eachupdatein the rebase todo--no-edit-todo: Accept the generated rebase todo as is and start the rebase without opening an editor. No editor needs to be configured. Useful for scripts and AI agents, which can check the todo with--dry-runfirst
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 -F .git-p4son/reviews/message --sleep 5
pick def5678 Second commit
exec git p4son update my-feature --shelve --sleep 5
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. The message is saved to .git-p4son/reviews/message for the first step, and every step but
the last sleeps 5 seconds to give Perforce/Swarm time to process the shelf.
If the rebase fails mid-way, fix the issue, re-run the failed git p4son command (git does not re-run a failed
exec line), and run git rebase --continue.
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
# Take a multi-line description from a file
git p4son review -F description.txt -b main
# Run the rebase without reviewing the todo in an editor
git p4son review -m "Add my feature" -b main --no-edit-todo
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 isHEAD~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 show
Print the changelist number saved under an alias:
git p4son alias show [alias]
Only the number is printed to stdout, so scripts and AI agents can capture it, e.g. to find the changelist that
new or review created. Exits with an error if the alias does not exist.
Arguments:
alias(optional): Alias name to show. Defaults to the current branch name.
Examples:
git p4son alias show # changelist for current branch
git p4son alias show myfeature
p4 describe -s $(git p4son alias show)
alias new
Save a changelist number under a named alias:
git p4son alias new <changelist> [alias] [--force]
Arguments:
changelist: Changelist number to savealias(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, or all of them:
git p4son alias delete [alias | --all]
Arguments:
alias(optional): Alias name to delete. Defaults to the current branch name.
Options:
--all: Delete all aliases without prompting. Unlikealias clean, it does not ask first, which suits scripts and AI agents
Examples:
git p4son alias delete # delete alias for current branch
git p4son alias delete myfeature
git p4son alias delete --all
alias clean
Clean up changelist aliases:
git p4son alias clean
The command lists all aliases and asks how to proceed:
a(all): Delete all aliasesi(interactive): Review each alias one by oneq(quit): Exit without deleting anything
In interactive mode, each alias is displayed in turn with a prompt:
y(yes): Delete this aliasn(no): Keep this aliasa(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
Writable Command
Writable mode keeps git-tracked files writable, so you can edit them without running p4 edit first. git-p4son
opens changed files in Perforce itself when you run new, update or review. Git-ignored files, such as content
and other Perforce-only files, stay read-only and are checked out through Perforce as usual.
git p4son writable # Show whether writable mode is on
git p4son writable enable # Turn writable mode on, then apply it
git p4son writable disable # Turn writable mode off, then apply it
git p4son writable apply # Make git-tracked files match the current mode
apply makes every git-tracked file writable when the mode is on. When it's off, it makes them read-only, except
files opened in Perforce in any changelist. On a workspace with the allwrite option, making files read-only is
skipped, since the client spec wants every file writable. Git-ignored files are never changed.
The setting is stored in .git-p4son/config.toml:
[core]
writable = true
With the mode on, sync makes the files it synced writable again if git tracks them, since Perforce writes synced
files read-only. Files made read-only outside git-p4son, for example by submitting from P4V, p4 revert, or "Get
Latest" in P4V, stay read-only until a later sync touches them. Run git p4son writable apply to fix them right
away.
This replaces the post-sync hook some users run to make every tracked file writable after a sync.
Turning the mode off also makes tracked files whose Perforce type carries the +w (always writable) modifier
read-only. The next sync of such a file makes it writable again.
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, orpowershell)
Options:
-d, --dirname: Print the directory containing the completion script instead of the full file path
See Shell Completions below for installation instructions.
Skill Command
Install or show the Claude Code skill:
git p4son skill install # install the skill for your user
git p4son skill show # print the instructions the skill gives to AI agents
show also prints the workspace's own overrides from .git-p4son/skill-override.md, if it
has any.
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) 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 -NoProfile -ExecutionPolicy Bypass -File, nu.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, and invalid association entries, 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/. All of them run, and if any 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/. They don't run when the workspace is already at the requested changelist.
Claude Code Skill
git-p4son ships a skill for Claude Code that teaches it how git-p4son is meant to be used: keeping main in sync
with Perforce, never submitting on your behalf, creating reviews with full control of the changelist description,
updating them after feedback, and cleaning up after a submit. With it installed, you can
ask Claude to sync, put a branch up for review or address review feedback, and it will use git-p4son to do it.
Install it once for your user:
git p4son skill install
This writes ~/.claude/skills/git-p4son/SKILL.md (under $CLAUDE_CONFIG_DIR if set). init offers to do the same.
The skill is installed for your user rather than in the workspace, where it would show up as an untracked file and
block sync, new and update.
The installed file is only a short stub: it tells Claude when the skill applies and to run git p4son skill show,
which prints the full instructions from the installed git-p4son. So the instructions always match your version of
git-p4son, and upgrading with pip needs no reinstall. Run git p4son skill show yourself to read what Claude is told.
Workspace overrides
To teach Claude something that only holds for one workspace, such as a team's submit conventions, write it in
.git-p4son/skill-override.md. Run inside that workspace, skill show prints it after the shipped instructions,
under a heading saying it takes precedence where the two disagree. Start your own headings at ###, since they land
under that ## heading. The file lives in .git-p4son/, so git ignores it and reinstalling the skill leaves it alone.
Claude picks the skill up on its own when you work in a git-p4son workspace. You can also invoke it directly with
/git-p4son.
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.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| git_p4son-0.6.0.tar.gz | 153.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| git_p4son-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 238.3 kB
Release files / git_p4son-0.6.0.tar.gz
| Download URL | git_p4son-0.6.0.tar.gz |
|---|---|
| Size | 153.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bbeb653a18a27de49a3f7ad0bb5c75418fbe688e9a4127214123715144cd6a03
|
|
BLAKE2b-256 checksum How to use checksums |
653a5340d552b493656ed15511e5a657a8d5a2f1ec4abf1f50ab43937f940511
|
| 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 Oct 15, 2026.
Transparency logRelease files / git_p4son-0.6.0-py3-none-any.whl
| Download URL | git_p4son-0.6.0-py3-none-any.whl |
|---|---|
| Size | 84.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65f6c49823e549a8f08825547fec9fb7d6d71b09392eca6eae0812c695804e6b
|
|
BLAKE2b-256 checksum How to use checksums |
acc8f44c29cd3dbaeda241ff142c0684e0ba6dbec802c60b8d18e2a0bb82b58b
|
| 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 Oct 15, 2026.
Transparency log