git-p4son 0.6.0__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {git_p4son-0.6.0 → git_p4son-0.7.0}/PKG-INFO +448 -101
  2. {git_p4son-0.6.0 → git_p4son-0.7.0}/README.md +446 -99
  3. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/__init__.py +2 -2
  4. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/alias.py +25 -4
  5. git_p4son-0.7.0/git_p4son/backend.py +136 -0
  6. git_p4son-0.7.0/git_p4son/changelist_store.py +299 -0
  7. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/cli.py +171 -139
  8. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/common.py +89 -21
  9. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/complete.py +78 -40
  10. git_p4son-0.7.0/git_p4son/completions/git-p4son-jj.bash +53 -0
  11. git_p4son-0.7.0/git_p4son/completions/git-p4son-jj.ps1 +73 -0
  12. git_p4son-0.7.0/git_p4son/completions/git-p4son-jj.zsh +40 -0
  13. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/completions/git-p4son.bash +7 -2
  14. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/config.py +13 -48
  15. git_p4son-0.7.0/git_p4son/git.py +643 -0
  16. git_p4son-0.7.0/git_p4son/git_backend.py +191 -0
  17. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/hooks.py +5 -5
  18. git_p4son-0.7.0/git_p4son/included.py +131 -0
  19. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/init.py +86 -11
  20. git_p4son-0.7.0/git_p4son/jj.py +548 -0
  21. git_p4son-0.7.0/git_p4son/jj_backend.py +262 -0
  22. git_p4son-0.7.0/git_p4son/jj_lock.py +95 -0
  23. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/lib.py +82 -35
  24. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/list_changes.py +4 -2
  25. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/log.py +19 -4
  26. git_p4son-0.7.0/git_p4son/new.py +112 -0
  27. git_p4son-0.7.0/git_p4son/per_commit.py +122 -0
  28. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/perforce.py +34 -26
  29. git_p4son-0.7.0/git_p4son/skill/SKILL.md +10 -0
  30. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/skill/git-p4son.md +95 -42
  31. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/sync.py +92 -82
  32. git_p4son-0.7.0/git_p4son/sync_branch.py +28 -0
  33. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/sync_merge.py +5 -2
  34. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/sync_split_users.py +13 -12
  35. git_p4son-0.7.0/git_p4son/toml_writer.py +54 -0
  36. git_p4son-0.7.0/git_p4son/update.py +181 -0
  37. git_p4son-0.7.0/git_p4son/words.py +27 -0
  38. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/writable.py +10 -9
  39. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son.egg-info/PKG-INFO +448 -101
  40. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son.egg-info/SOURCES.txt +20 -4
  41. {git_p4son-0.6.0 → git_p4son-0.7.0}/pyproject.toml +22 -2
  42. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_alias.py +44 -2
  43. git_p4son-0.7.0/tests/test_catch_up.py +264 -0
  44. git_p4son-0.7.0/tests/test_changelist_store.py +387 -0
  45. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_cli.py +186 -91
  46. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_common.py +21 -3
  47. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_complete.py +32 -12
  48. git_p4son-0.7.0/tests/test_completion_scripts.py +199 -0
  49. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_config.py +2 -1
  50. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_git.py +173 -60
  51. git_p4son-0.7.0/tests/test_git_backend.py +188 -0
  52. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_hooks.py +13 -12
  53. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_init.py +270 -12
  54. git_p4son-0.7.0/tests/test_isolation.py +39 -0
  55. git_p4son-0.7.0/tests/test_jj.py +944 -0
  56. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_lib.py +122 -17
  57. git_p4son-0.7.0/tests/test_list_changes.py +50 -0
  58. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_log.py +22 -2
  59. git_p4son-0.7.0/tests/test_new.py +379 -0
  60. git_p4son-0.7.0/tests/test_per_commit.py +187 -0
  61. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_perforce.py +31 -24
  62. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_sync.py +206 -161
  63. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_sync_merge.py +8 -8
  64. git_p4son-0.7.0/tests/test_toml_writer.py +35 -0
  65. git_p4son-0.7.0/tests/test_update.py +513 -0
  66. git_p4son-0.6.0/git_p4son/changelist_store.py +0 -130
  67. git_p4son-0.6.0/git_p4son/git.py +0 -396
  68. git_p4son-0.6.0/git_p4son/new.py +0 -38
  69. git_p4son-0.6.0/git_p4son/rebase_todo.py +0 -158
  70. git_p4son-0.6.0/git_p4son/review.py +0 -36
  71. git_p4son-0.6.0/git_p4son/skill/SKILL.md +0 -8
  72. git_p4son-0.6.0/git_p4son/update.py +0 -59
  73. git_p4son-0.6.0/tests/test_changelist_store.py +0 -190
  74. git_p4son-0.6.0/tests/test_list_changes.py +0 -31
  75. git_p4son-0.6.0/tests/test_new.py +0 -131
  76. git_p4son-0.6.0/tests/test_rebase_todo.py +0 -219
  77. git_p4son-0.6.0/tests/test_review.py +0 -111
  78. git_p4son-0.6.0/tests/test_update.py +0 -211
  79. {git_p4son-0.6.0 → git_p4son-0.7.0}/LICENSE +0 -0
  80. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/__main__.py +0 -0
  81. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/completions/_git-p4son +0 -0
  82. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/completions/git-p4son.ps1 +0 -0
  83. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/depot.py +0 -0
  84. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/description.py +0 -0
  85. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/processes.py +0 -0
  86. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son/skill.py +0 -0
  87. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son.egg-info/dependency_links.txt +0 -0
  88. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son.egg-info/entry_points.txt +0 -0
  89. {git_p4son-0.6.0 → git_p4son-0.7.0}/git_p4son.egg-info/top_level.txt +0 -0
  90. {git_p4son-0.6.0 → git_p4son-0.7.0}/setup.cfg +0 -0
  91. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_depot.py +0 -0
  92. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_description.py +0 -0
  93. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_processes.py +0 -0
  94. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_skill.py +0 -0
  95. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_sync_split_users.py +0 -0
  96. {git_p4son-0.6.0 → git_p4son-0.7.0}/tests/test_writable.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-p4son
3
- Version: 0.6.0
4
- Summary: Utility for keeping a Perforce workspace and local git repo in sync
3
+ Version: 0.7.0
4
+ Summary: Utility for keeping a Perforce workspace and local git or jj repo in sync
5
5
  Author-email: Andreas Andersson <andreas@neoboid.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/neoboid/git-p4son
@@ -23,21 +23,20 @@ Dynamic: license-file
23
23
 
24
24
  # Git Perforcesson
25
25
 
26
- `git-p4son` is a tool for managing a local git repository within a Perforce workspace.
27
- This way you can use the familiar local git development flow even though you are
28
- working on a project where perforce is used for source control.
26
+ `git-p4son` lets you work in a Perforce workspace with a local git repository, or a colocated
27
+ [Jujutsu (jj)](#jujutsu-jj) one, so you can use the familiar local development flow even though your project uses
28
+ Perforce for source control.
29
29
 
30
- The idea is to have a `main` git branch that is kept in sync with a branch of a Perforce depot.
31
- From `main` you branch out into feature branches, where you do local changes
32
- and rebase on `main` whenever it is updated.
30
+ A `main` branch (a bookmark in jj) is kept in sync with your Perforce workspace, one commit per changelist. You work
31
+ on feature branches or jj changes and rebase them onto `main` after a sync; with jj, `sync` rebases the change you
32
+ sync from for you.
33
33
 
34
- Once your changes is ready to be submitted in perforce `git-p4son` can help you create both
35
- changelists and push them for reviews. You can even push individual git commits as patches
36
- for review, so that the reviewer can see the changes in the order you made them locally. Similar
37
- to the pull request worfkflow on github.
34
+ When your changes are ready, `git-p4son` creates the changelist and shelves it for a Swarm review. It can post each
35
+ commit as a patch of its own, so reviewers see the changes in the order you made them, much like a pull request.
36
+ Updates fold in only the new commits.
38
37
 
39
- This is a bit cumbersome to do manually, but this package provides commands
40
- that help out with the repetitive and error prone stuff.
38
+ With jj, `git-p4son` works on the jj change: the changelist's alias follows the change through rewrites, the message
39
+ comes from its description, and `jj p4son` runs every command.
41
40
 
42
41
  ## On big workspaces
43
42
  Sometimes git struggles to keep entire perforce workspaces in the repo, like when working
@@ -74,8 +73,8 @@ git add .
74
73
  git commit -m "Feature part2"
75
74
 
76
75
  # Create a Swarm review with all commits since main in one go.
77
- # This opens an interactive rebase with pre-filled exec lines updating
78
- # the changelist with git-p4son after each picked commit.
76
+ # Each commit is checked out in turn and gets a shelf of its own in the
77
+ # review, then you are back on your branch.
79
78
  # An alias called "my-fancy-feature" (derived from the branch name) is
80
79
  # saved so you can refer to this CL by name in follow-up commands.
81
80
  git p4son review -m "My fancy feature" -b main
@@ -104,6 +103,44 @@ git p4son alias delete my-fancy-feature
104
103
  git checkout -b my-next-fancy-feature
105
104
  ```
106
105
 
106
+ ### With jj
107
+
108
+ The same workflow in a [colocated jj workspace](#jujutsu-jj), with the [`jj p4son` alias](#the-jj-p4son-alias):
109
+
110
+ ```sh
111
+ # Start work on a new feature
112
+ jj new main -m "Feature part1"
113
+ # Change some code, which jj snapshots into the change
114
+
115
+ # Sync main from any change: the change you are on is rebased onto it
116
+ jj p4son sync
117
+
118
+ # Change even more code in a change of its own
119
+ jj new -m "Feature part2"
120
+ jj bookmark create my-fancy-feature
121
+
122
+ # Create a Swarm review with every change since main, one shelf each.
123
+ # The alias is named after the bookmark and records the jj change.
124
+ jj p4son review -m "My fancy feature" -b main
125
+
126
+ # After review feedback, add a change and move the bookmark to it
127
+ jj new -m "Address review feedback"
128
+ jj bookmark set my-fancy-feature -r @
129
+ jj p4son update --shelve
130
+
131
+ # After approval, submit in p4v, then abandon the submitted changes before
132
+ # syncing, which deletes the bookmark too
133
+ jj abandon main..my-fancy-feature
134
+ jj p4son alias delete my-fancy-feature
135
+ jj p4son sync
136
+
137
+ # Start working on the next feature
138
+ jj new main -m "Next fancy feature"
139
+ ```
140
+
141
+ `sync` only rebases the change you sync from, and the stack above it. Bring other bookmarks up to date with
142
+ `jj rebase -b <bookmark> -d main`.
143
+
107
144
  ## Installation
108
145
 
109
146
  ```sh
@@ -161,19 +198,13 @@ cloning.
161
198
 
162
199
  These steps set up git-p4son in an existing Perforce workspace. You only need to do this once.
163
200
 
164
- 1. **Set a git editor** if you don't have one already. The `review` command opens an interactive rebase in your
165
- editor. If you haven't configured one, set it with:
166
- ```sh
167
- git config --global core.editor <editor> # e.g. vim, nano, code --wait
168
- ```
169
-
170
- 2. **Sync your workspace to a known changelist.** Pick a changelist to use as the starting point for your git
201
+ 1. **Sync your workspace to a known changelist.** Pick a changelist to use as the starting point for your git
171
202
  history:
172
203
  ```sh
173
204
  p4 sync //...@12345
174
205
  ```
175
206
 
176
- 3. **Run `git p4son init`.** This can be anywhere inside your Perforce workspace - it doesn't have to be at the
207
+ 2. **Run `git p4son init`.** This can be anywhere inside your Perforce workspace - it doesn't have to be at the
177
208
  root:
178
209
  ```sh
179
210
  cd /path/to/your/workspace # or a subdirectory of it
@@ -182,16 +213,18 @@ These steps set up git-p4son in an existing Perforce workspace. You only need to
182
213
  The command verifies that you are inside a P4 workspace, prompts you to select a depot
183
214
  root (entire workspace or current directory subtree), runs `git init`, sets up `.gitignore` (copying from
184
215
  `.p4ignore` if available), and creates an initial commit.
216
+ With jj 0.41 or newer installed, it also offers to colocate jj and to add the `jj p4son` alias, see
217
+ [Jujutsu (jj)](#jujutsu-jj).
185
218
 
186
- 4. **Review `.gitignore`.** Edit the file to ensure build artifacts and other unwanted files are excluded.
219
+ 3. **Review `.gitignore`.** Edit the file to ensure build artifacts and other unwanted files are excluded.
187
220
 
188
- 5. **Add and commit all files manually the first time**
221
+ 4. **Add and commit all files manually the first time**
189
222
  ```sh
190
223
  git add .
191
224
  git commit -m "Initial submit all files"
192
225
  ```
193
226
 
194
- 6. **Run `git p4son sync`** to get your first official sync commit
227
+ 5. **Run `git p4son sync`** to get your first official sync commit
195
228
  ```sh
196
229
  git p4son sync
197
230
  ```
@@ -256,6 +289,10 @@ write most of them; the rest you edit by hand.
256
289
  `//$(workspace)/Engine`. See [Init Command](#init-command)
257
290
  - `[core] writable` (`true` or `false`, default `false`): [writable mode](#writable-command), which keeps
258
291
  git-tracked files writable
292
+ - `[core] backend` (`"auto"`, `"git"` or `"jj"`, default `"auto"`): the tool git-p4son follows for the change, the
293
+ base and the branch. `auto` means jj in a [jj workspace](#jujutsu-jj) and git otherwise
294
+ - `[sync] branch` (string, set by `init`): the sync branch, which `sync` commits onto from whatever branch you are
295
+ on. Without it, `sync` uses `main`. See [Sync Command](#sync-command)
259
296
  - `[sync] split-users` (list of strings, default empty): the users whose changelists `sync` gives a commit each;
260
297
  `$(user)` stands for you. See [Split users](#split-users)
261
298
  - `[sync] blocking-processes` (list of strings, default empty): processes that stop a sync while they run. See
@@ -308,9 +345,9 @@ Initialize a git repository inside a Perforce workspace:
308
345
  git p4son init
309
346
  ```
310
347
 
311
- This command checks that it runs in a Perforce workspace, then asks for the depot root, writable mode, split users
312
- and the Claude Code skill. It then sets up `.gitignore`, runs `git init` and creates a first commit holding only
313
- `.gitignore`. It ends by listing the next steps, and warns when no git editor is configured.
348
+ This command checks that it runs in a Perforce workspace, then asks for the depot root, writable mode, split users,
349
+ the Claude Code skill and jj. It then sets up `.gitignore`, runs `git init` and creates a first commit holding only
350
+ `.gitignore`. It ends by listing the next steps.
314
351
 
315
352
  The depot root determines which part of the Perforce workspace git-p4son syncs. You can choose to sync the entire
316
353
  workspace or just the directory's subtree where the git root is placed. The selection is saved in
@@ -336,17 +373,45 @@ saves the answer; run `git p4son writable apply` once your files are committed t
336
373
  The default is the current setting. Yes adds a `$(user)` entry to the split users and no removes it; other split
337
374
  users are left alone.
338
375
 
376
+ `init` records the [sync branch](#sync-command) as `[sync] branch`. In a repository it creates, that's the branch
377
+ `HEAD` is on. In an existing one, it's `main` if there is one, else the branch `HEAD` is on, else one you pick from
378
+ the local branches. It keeps a branch already set.
379
+
339
380
  If Claude Code is installed, `init` offers to install the [Claude Code skill](#claude-code-skill). An
340
381
  already installed skill is refreshed without asking.
341
382
 
383
+ With jj 0.41 or newer installed, `init` offers two things for [jj](#jujutsu-jj):
384
+
385
+ - **Colocating jj**, when the workspace has no `.jj` folder: "Use jj in this workspace too (colocated)?", default
386
+ no. On yes, in an existing repository it runs `jj git init --colocate` as its last step, so jj imports the branch
387
+ as a bookmark. It sets jj's [line endings](#line-endings) to match git's from jj's first snapshot on. In a
388
+ repository it creates, it lists the same commands as next steps after your initial commit instead, since jj would
389
+ otherwise snapshot every file before you have reviewed `.gitignore`.
390
+ - **The [`jj p4son` alias](#the-jj-p4son-alias)**, when the workspace has a `.jj` folder, or is to get one, and
391
+ jj's config has no `aliases.p4son`: default yes. It adds the alias to jj's user config.
392
+
393
+ Running `init` again asks neither once they are done.
394
+
342
395
  ### Sync Command
343
396
 
344
397
  Sync local git repository with a Perforce workspace:
345
398
 
346
399
  ```sh
347
- git p4son sync [changelist ...] [--force] [--dry-run] [--split-user NAME ...] [--no-split] [--ignore-blocking-processes]
400
+ git p4son sync [changelist ...] [--branch BRANCH] [--force] [--dry-run] [--split-user NAME ...] [--no-split]
401
+ [--ignore-blocking-processes]
348
402
  ```
349
403
 
404
+ `sync` commits onto the sync branch, `[sync] branch` in `.git-p4son/config.toml`, which `init` sets and which
405
+ defaults to `main`. From another branch, it checks the sync branch out, syncs and commits, then returns you to
406
+ where you started, telling you to `git rebase` your branch onto the sync branch; it doesn't rebase for you. The
407
+ pre-sync and post-sync hooks run on the sync branch. You stay on the sync branch when:
408
+
409
+ - local changes were [merged back](#how-sync-handles-writable-files) into the work tree, for you to review and
410
+ commit there
411
+ - a sync fails partway, with the files as Perforce left them and the commits so far kept
412
+
413
+ A failing pre-sync hook, or anything else that stops `sync` before it syncs, returns you to where you started.
414
+
350
415
  **Arguments:**
351
416
  - `changelist` (optional): Changelist number, `last-synced` to re-sync the last synced changelist, or `head`
352
417
  to sync to the latest changelist. Omit to sync to the latest changelist affecting the workspace. Several
@@ -358,6 +423,10 @@ git-tracked files opened in Perforce. It checks them after any [blocking process
358
423
  the [pre-sync hooks](#pre-sync-hook).
359
424
 
360
425
  **Options:**
426
+ - `--branch BRANCH`: Commit onto this branch instead of `[sync] branch`. With the jj backend, the bookmark to move
427
+ to each sync commit
428
+ - `--no-rebase`: With the jj backend, leave the changes you started from where they are, instead of rebasing them
429
+ onto the [sync bookmark](#syncing-a-bookmark)
361
430
  - `-f, --force`: Allow syncing to changelists older than the current one
362
431
  - `-n, --dry-run`: Print the resolved sync sequence without syncing. The arguments are validated as for a
363
432
  real sync, but nothing else runs: no clean-workspace checks, no hooks, and no prompts. Ends with
@@ -380,6 +449,7 @@ git p4son sync 12345 --force
380
449
  git p4son sync --dry-run # show the changelists a sync to latest would visit
381
450
  git p4son sync -u alice # also split out alice's changelists
382
451
  git p4son sync --no-split # sync without splitting out the configured split users
452
+ git p4son sync --branch p4 # commit onto the p4 branch this time
383
453
  ```
384
454
 
385
455
  #### Blocking processes
@@ -426,10 +496,12 @@ visits `101 102 103 104 105 106`.
426
496
  Splitting only moves forward from a previous sync. The first sync, `last-synced`, and syncing to an older changelist
427
497
  with `--force` are never split. Without any split users, `sync` makes no extra Perforce queries.
428
498
 
429
- Finding the split users' changelists costs one extra Perforce query over the range being synced. It runs after the
430
- blocking processes check, the clean-workspace checks and the pre-sync hooks, so a running blocking process, a dirty
431
- workspace or a hook that aborts the sync says so before that work. `--dry-run` skips the checks and hooks but still
432
- shows the resulting sequence.
499
+ Finding the split users' changelists costs one Perforce query per split user, listing their changelists in the range
500
+ being synced, then one batched query that checks each changelist found touched the workspace and finds the one before
501
+ it. None of them scans the files of every changelist in the range, which on a large range can exceed the server's
502
+ `maxscanrows`. They run after the blocking processes check, the clean-workspace checks and the pre-sync hooks, so a
503
+ running blocking process, a dirty workspace or a hook that aborts the sync says so before that work. `--dry-run` skips
504
+ the checks and hooks but still shows the resulting sequence.
433
505
 
434
506
  #### Hooks
435
507
 
@@ -467,8 +539,8 @@ is skipped with `--no-edit`. After opening files, unchanged ones are reverted (s
467
539
  [Keeping the changelist in sync with git](#keeping-the-changelist-in-sync-with-git)).
468
540
 
469
541
  ```sh
470
- git p4son new [-m <message> | -F <file>] [alias] [--base-branch BASE_BRANCH] [--force] [--dry-run] [--no-edit]
471
- [--no-alias] [--no-commit-list] [--shelve] [--review]
542
+ git p4son new -b BASE_BRANCH [-m <message> | -F <file>] [alias] [--force] [--dry-run] [--no-edit]
543
+ [--no-alias] [--no-commit-list] [--shelve] [--review] [--per-commit]
472
544
  ```
473
545
 
474
546
  **Arguments:**
@@ -480,44 +552,92 @@ git p4son new [-m <message> | -F <file>] [alias] [--base-branch BASE_BRANCH] [--
480
552
  multiple lines
481
553
  - `--no-commit-list`: Leave the enumerated commit list out of the changelist description, so it holds only the
482
554
  message
483
- - `-b, --base-branch BASE_BRANCH`: Base branch for enumerating commits and finding changed files. Default is
484
- `HEAD~1`
555
+ - `-b, --base-branch BASE_BRANCH`: Required. The base the change starts from, for enumerating commits and finding
556
+ changed files: a branch or bookmark, or a commit exactly one points at. The alias records it by name, as a commit
557
+ would go stale after a sync and rebase, along with the commits since it the changelist includes, so `update`
558
+ knows where the next ones start. With `--no-alias` any commit will do
485
559
  - `-f, --force`: Overwrite an existing alias file
486
560
  - `-n, --dry-run`: Pretend and print what would be done, but do not execute. Ends with `Dry run, nothing changed`
487
561
  - `--no-edit`: Skip opening changed files for edit in Perforce
488
562
  - `--no-alias`: Skip saving a changelist alias
489
563
  - `--shelve`: Shelve the changelist after creating it
490
564
  - `--review`: Add `#review` keyword and shelve to create a Swarm review
565
+ - `--per-commit`: Create the changelist on the first commit since the base branch, then update it once per later
566
+ commit, as described below. Refuses `--no-edit` and `--no-alias`
567
+
568
+ With `--per-commit`, `new` checks out the first commit since the base branch and runs `git p4son new` on it, then
569
+ checks out each later commit and runs `git p4son update` on it, and finally returns to where you started. With
570
+ `--shelve` or `--review`, every step shelves, so each commit shows up as its own patch in the Swarm review, and every
571
+ step but the last sleeps 5 seconds to give Perforce/Swarm time to process the shelf. It needs a clean git
572
+ workspace even with `--dry-run`. [`review`](#review-command) is `new --review --per-commit`, and describes the steps,
573
+ the dry run and resuming after a failed step.
491
574
 
492
575
  **Examples:**
493
576
  ```sh
494
- git p4son new -m "Fix login bug"
495
577
  git p4son new -m "Add feature" -b main
496
- git p4son new -m "Fix bug" myalias
497
- git p4son new -m "Fix bug" --no-alias
578
+ git p4son new -m "Fix bug" myalias -b main
579
+ git p4son new -m "Fix bug" --no-alias -b HEAD~1
498
580
  git p4son new -m "New feature" --review -b main
581
+ git p4son new -m "New feature" --shelve --per-commit -b main # a shelf per commit, without a Swarm review
499
582
  ```
500
583
 
501
584
  ### Update Command
502
585
 
503
- Update an existing Perforce changelist description. Commits since the base branch replace their existing
504
- entries in the enumerated commit list (matched by subject) and new ones are appended; entries outside the
505
- range are kept and the list is renumbered. So `update -b main` rebuilds the whole list without duplicating
506
- it, while `update -b HEAD~3` only refreshes the last three entries. The message above the list is kept unless `-m`
507
- or `-F` replaces it. By default also opens changed files for
508
- edit, and reverts files that are no longer part of the git change (see
586
+ Update an existing Perforce changelist with the commits it doesn't include yet. The message above the commit list
587
+ is kept unless `-m` or `-F` replaces it. By default also opens changed files for edit, and reverts files that are no
588
+ longer part of the git change (see
509
589
  [Keeping the changelist in sync with git](#keeping-the-changelist-in-sync-with-git)). Opening files requires a
510
590
  clean git workspace, untracked files included; `update` refuses to run otherwise. The check is skipped with
511
591
  `--no-edit`.
512
592
 
513
- With `--per-commit`, `update` runs once per commit since the base branch instead of once for the whole range, the
514
- way `review` does after creating the changelist. It replays the commits with an interactive rebase, without opening
515
- an editor, and runs `git p4son update` after each one. With `--shelve`, every commit gets a shelf of its own and so
516
- shows up as its own patch in the Swarm review. Use it after making several commits since the last update:
517
- `update -b HEAD~2 --per-commit --shelve` updates the changelist with the last two commits, one at a time. The
518
- changelist is resolved before the rebase starts, and `-m`/`-F` is applied on the first step only. The rebase needs a
519
- clean git workspace, also with `--no-edit`. If a step fails, the rebase stops; fix the problem and run
520
- `git rebase --continue`.
593
+ **Changelists made by `new` or `review`.** Their alias records the base, by name, and the commits since it the
594
+ changelist includes, by their jj change IDs or, with git, their author dates, which survive rebases and amends.
595
+ `update` folds in the commits it doesn't know: it appends their subjects to the commit list, opens their files, and
596
+ records them. The entries the list already has are left as they are, so an entry you reworded in the changelist
597
+ stays as you wrote it. Every update counts, shelved or not.
598
+
599
+ - `-b` only chooses which files to open, from that commit on, for example `-b main` to open them all again after a
600
+ manual `p4 revert`. The commit list is still only appended to. A `-b` above the last included commit is refused,
601
+ as the commits it skips would be counted without being opened.
602
+ - An included commit that is gone from the branch, dropped or squashed into another, gets a warning, and the update
603
+ goes on with the new commits. Its patch stays in the review: to undo a shelved commit, revert it in a new commit.
604
+ - A new commit below included ones is refused, as its own patch would undo the included commits above it. Move it
605
+ after them, then update again.
606
+ - An amend, or a squash into an included commit, keeps the commit's identity, so `update` sees no new commit. With
607
+ nothing new, `update` opens every file since the base, which takes in what the amend added. With new commits as
608
+ well, run `update -b main` to open them.
609
+
610
+ **After a sync.** A shelf holds whole files, so once a sync has changed files the changelist touches, its next shelf
611
+ would mix those changes into the next commit's patch. So when `update --shelve` finds that the base moved since the
612
+ last shelve, the branch was rebased onto it, and it changed such a file, it first inserts an empty commit, `Rebase
613
+ onto CL <N>`, right after the last included commit, with the commits after it rebased onto it. Its shelf is a patch
614
+ of its own, holding what the sync changed, and its entry keeps the commit list matching the patches.
615
+
616
+ - With `--per-commit`, it's the first step. Without it, `update --shelve` shelves twice: at the rebase commit, then at
617
+ the tip.
618
+ - An update without `--shelve` leaves the catch-up to the next one that shelves, and a changelist that was never
619
+ shelved has no patches to catch up. A dry run says what it would insert.
620
+
621
+ **Other changelists**, whose alias records nothing (made by `alias new` or an older git-p4son), or named by a number
622
+ no alias holds: the commits since the base branch, `HEAD~1` by default, replace their existing entries in the
623
+ commit list (matched by subject) and new ones are appended; entries outside the range are kept and the list is
624
+ renumbered.
625
+
626
+ With `--per-commit`, `update` runs once per commit instead of once for the whole range, the way `new --per-commit`
627
+ and `review` do after creating the changelist. It checks out each commit in turn, oldest first, runs the update on
628
+ it, and returns to where you started. With `--shelve`, every commit gets a shelf of its own and so shows up as its
629
+ own patch in the Swarm review.
630
+
631
+ - **With a record,** plain `update --per-commit --shelve` runs on exactly the commits the changelist doesn't include
632
+ yet. `-b` is refused there, as it would shelve included commits again.
633
+ - **Without one,** pass the range: `update -b HEAD~2 --per-commit --shelve` updates the changelist with the last two
634
+ commits, one at a time. The base is resolved once, before the first step, so a relative base like `HEAD~2` means
635
+ the same commit for every step.
636
+
637
+ `-m`/`-F` is applied on the first step only. It needs a clean git workspace, also with `--no-edit`. If a step
638
+ fails, `update` returns to where you started and names the step and commit that failed. The error ends with the
639
+ command that resumes after the last commit that worked, so earlier commits get no second shelf; with a record,
640
+ running the same command again does the same.
521
641
 
522
642
  ```sh
523
643
  git p4son update [changelist] [-m <message> | -F <file>] [--base-branch BASE_BRANCH] [--dry-run]
@@ -531,27 +651,27 @@ git p4son update [changelist] [-m <message> | -F <file>] [--base-branch BASE_BRA
531
651
  - `-m, --message MESSAGE`: Replace the changelist description message above the commit list. The commit list is
532
652
  kept and updated as usual, and so is a Swarm `#review` keyword on a line of its own at the end of the description
533
653
  - `-F, --file FILE`: Like `-m`, but read the message from a file. The message may span multiple lines
534
- - `-b, --base-branch BASE_BRANCH`: Base branch for enumerating commits and finding changed files. Default is
535
- `HEAD~1`
654
+ - `-b, --base-branch BASE_BRANCH`: Where the commits to fold in start, for the commit list and the files to open.
655
+ Defaults to after the last commit the changelist includes, when its alias records that, else to `HEAD~1`
536
656
  - `-n, --dry-run`: Pretend and print what would be done, but do not execute. Ends with `Dry run, nothing changed`
537
657
  - `--no-commit-list`: Leave the enumerated commit list as it is. Without `-m` or `-F` the description is not
538
658
  touched at all
539
- - `--no-edit`: Skip opening changed files for edit in Perforce
659
+ - `--no-edit`: Skip opening changed files for edit in Perforce. With a record, the new commits are then neither
660
+ appended nor counted, so the next update that opens files includes them
540
661
  - `--shelve`: Re-shelve the changelist after updating
541
- - `--per-commit`: Update the changelist once per commit since the base branch, oldest first, through an
542
- interactive rebase. With `--shelve`, steps sleep 5 seconds between them to give Perforce/Swarm time to process
543
- each shelf.
544
- `--dry-run` prints the generated rebase todo. A message from `-m` or `-F` is saved to
545
- `.git-p4son/reviews/message` for the first step
662
+ - `--per-commit`: Update the changelist once per commit it doesn't include yet, or since the base branch without
663
+ a record, oldest first, with each commit checked out in turn. With `--shelve`, steps sleep 5 seconds between them
664
+ to give Perforce/Swarm time to process each shelf. `--dry-run` prints the steps, one per commit
546
665
 
547
666
  **Examples:**
548
667
  ```sh
549
- git p4son update # update changelist for current branch
668
+ git p4son update # fold the new commits into the changelist for the current branch
550
669
  git p4son update --shelve # update and re-shelve
551
670
  git p4son update 12345
552
- git p4son update myalias -b main
671
+ git p4son update myalias -b main # open every file of the change again
553
672
  git p4son update -F description.txt # replace the message, e.g. after review feedback
554
- git p4son update -b HEAD~2 --per-commit --shelve # one shelf for each of the last two commits
673
+ git p4son update --per-commit --shelve # one shelf for each commit the changelist doesn't include yet
674
+ git p4son update 12345 -b HEAD~2 --per-commit --shelve # no record: the last two commits
555
675
  ```
556
676
 
557
677
  ### Keeping the changelist in sync with git
@@ -577,14 +697,16 @@ are left out of it.
577
697
 
578
698
  ### Review Command
579
699
 
580
- Automate the interactive rebase workflow for creating Swarm reviews. This command generates a rebase todo with `exec`
581
- lines that run `git p4son new --review` on the first commit and `git p4son update --shelve` on each subsequent commit,
582
- then opens it in your editor for review before executing. Like `new`, it needs a clean git workspace, untracked
583
- files included, and checks it even with `--dry-run`.
700
+ Create a Swarm review with one shelf per commit. `review` is short for `git p4son new --review --per-commit`, taking the
701
+ same arguments apart from `--review`, `--per-commit`, `--shelve`, `--no-edit` and `--no-alias`. It checks out the first
702
+ commit since the base branch and runs `git p4son new --review` on it, then checks out each later commit and runs
703
+ `git p4son update --shelve`, and finally returns to where you started. Every step but the last sleeps 5 seconds to give
704
+ Perforce/Swarm time to process the shelf. The commits are reviewed where they are: to review them on top of the latest
705
+ `main`, rebase first. Like `new`, it needs a clean git workspace, untracked files included, and checks it even with
706
+ `--dry-run`.
584
707
 
585
708
  ```sh
586
- git p4son review [alias] [-m <message> | -F <file>] [--base-branch BASE_BRANCH] [--force] [--dry-run]
587
- [--no-commit-list] [--no-edit-todo]
709
+ git p4son review -b BASE_BRANCH [alias] [-m <message> | -F <file>] [--force] [--dry-run] [--no-commit-list]
588
710
  ```
589
711
 
590
712
  **Arguments:**
@@ -594,48 +716,40 @@ git p4son review [alias] [-m <message> | -F <file>] [--base-branch BASE_BRANCH]
594
716
  - `-m, --message MESSAGE`: Changelist description message. Defaults to the HEAD commit subject
595
717
  - `-F, --file FILE`: Read the changelist description message from a file instead of `-m`. The message may span
596
718
  multiple lines
597
- - `-b, --base-branch BASE_BRANCH`: Base branch to rebase onto and find commits since. Default is `HEAD~1`
719
+ - `-b, --base-branch BASE_BRANCH`: Required. The base the change starts from, to find commits since: a branch or
720
+ bookmark, or a commit exactly one points at, which the alias records as for [`new`](#new-command)
598
721
  - `-f, --force`: Overwrite an existing alias file
599
- - `-n, --dry-run`: Print the generated rebase todo without executing. Ends with `Dry run, nothing changed`
722
+ - `-n, --dry-run`: Print the steps, one per commit, without running them. Ends with `Dry run, nothing changed`
600
723
  - `--no-commit-list`: Leave the enumerated commit list out of the changelist description. Passed on to `new` and
601
- each `update` in the rebase todo
602
- - `--no-edit-todo`: Accept the generated rebase todo as is and start the rebase without opening an editor. No
603
- editor needs to be configured. Useful for scripts and AI agents, which can check the todo with `--dry-run` first
724
+ each `update` step
604
725
 
605
- When run, the command generates a todo like this and opens it in your editor:
726
+ A dry run prints the plan like this, with the base resolved to the commit every step uses; the message, which only
727
+ the first step gets, is printed below it:
606
728
 
607
729
  ```
608
- pick abc1234 First commit
609
- exec git p4son new my-feature --review -F .git-p4son/reviews/message --sleep 5
610
- pick def5678 Second commit
611
- exec git p4son update my-feature --shelve --sleep 5
612
- pick ghi9012 Third commit
613
- exec git p4son update my-feature --shelve
730
+ abc1234 First commit: git p4son new my-feature --review -b 5f1d0e9c2a7b4e8d9c0b1a2f3e4d5c6b7a8f9e0d
731
+ def5678 Second commit: git p4son update my-feature --shelve -b 5f1d0e9c2a7b4e8d9c0b1a2f3e4d5c6b7a8f9e0d
732
+ ghi9012 Third commit: git p4son update my-feature --shelve -b 5f1d0e9c2a7b4e8d9c0b1a2f3e4d5c6b7a8f9e0d
614
733
  ```
615
734
 
616
- You can edit the todo before saving (e.g. reorder commits, remove lines), or abort by clearing the file - just like a
617
- normal `git rebase -i`. The message is saved to `.git-p4son/reviews/message` for the first step, and every step but
618
- the last sleeps 5 seconds to give Perforce/Swarm time to process the shelf.
619
-
620
- If the rebase fails mid-way, fix the issue, re-run the failed `git p4son` command (git does not re-run a failed
621
- `exec` line), and run `git rebase --continue`.
735
+ If a step fails, `review` returns to where you started and names the step and commit that failed. Once the first
736
+ step has created the changelist, the error also says how to finish: after fixing the problem, run the
737
+ `git p4son update my-feature --shelve --per-commit -b <commit>` it prints, which resumes after the last commit that
738
+ worked.
622
739
 
623
740
  **Examples:**
624
741
  ```sh
625
742
  # Review all commits since main (alias defaults to branch name)
626
743
  git p4son review -m "Add my feature" -b main
627
744
 
628
- # Review just the last commit (default base branch)
629
- git p4son review -m "Fix bug"
745
+ # Review a branch's only commit: HEAD~1 is main's commit, so the alias records main
746
+ git p4son review -m "Fix bug" -b HEAD~1
630
747
 
631
- # Preview the generated todo without executing
748
+ # Preview the steps without running them
632
749
  git p4son review -m "Add my feature" -b main --dry-run
633
750
 
634
751
  # Take a multi-line description from a file
635
752
  git p4son review -F description.txt -b main
636
-
637
- # Run the rebase without reviewing the todo in an editor
638
- git p4son review -m "Add my feature" -b main --no-edit-todo
639
753
  ```
640
754
 
641
755
  ### List-Changes Command
@@ -643,16 +757,19 @@ git p4son review -m "Add my feature" -b main --no-edit-todo
643
757
  List commit subjects since a base branch in chronological order (oldest first):
644
758
 
645
759
  ```sh
646
- git p4son list-changes [--base-branch BASE_BRANCH]
760
+ git p4son list-changes [--base-branch BASE_BRANCH] [--porcelain]
647
761
  ```
648
762
 
649
763
  **Options:**
650
764
  - `-b, --base-branch BASE_BRANCH`: Base branch to compare against. Default is `HEAD~1`.
765
+ - `--porcelain`: Print only the numbered list, and nothing when there are no changes, for scripts and agents. Errors
766
+ still go to stderr.
651
767
 
652
768
  **Examples:**
653
769
  ```sh
654
770
  git p4son list-changes
655
771
  git p4son list-changes --base-branch main
772
+ git p4son list-changes --base-branch main --porcelain
656
773
  ```
657
774
 
658
775
  This command is useful for generating changelist descriptions by listing all commit messages since the base branch,
@@ -660,7 +777,12 @@ numbered sequentially.
660
777
 
661
778
  ### Alias Command
662
779
 
663
- Manage changelist aliases stored in `.git-p4son/changelists/`.
780
+ Manage changelist aliases, stored one per file as `.git-p4son/aliases/<alias>.toml` (`changelist = 12345`, and
781
+ with the jj backend `change = "<change ID>"`, the [jj change](#jujutsu-jj) it was made for). An alias that `new` or
782
+ `review` saved also records the changelist's base and the commits since it it includes, which
783
+ [`update`](#update-command) goes on from: `base`, `base_commit` and `included`, a list of the commits' identities.
784
+ Aliases saved by older versions in `.git-p4son/changelists/` keep working, and move to `.git-p4son/aliases/` when
785
+ written, or all at once with `alias clean`.
664
786
 
665
787
  #### alias list
666
788
 
@@ -748,7 +870,8 @@ Clean up changelist aliases:
748
870
  git p4son alias clean
749
871
  ```
750
872
 
751
- The command lists all aliases and asks how to proceed:
873
+ It first moves any aliases still in `.git-p4son/changelists/` to `.git-p4son/aliases/`, then lists all aliases and
874
+ asks how to proceed:
752
875
  - `a` (all): Delete all aliases
753
876
  - `i` (interactive): Review each alias one by one
754
877
  - `q` (quit): Exit without deleting anything
@@ -842,7 +965,8 @@ at defined times during git-p4son operations. Scripts get run in filename (lexic
842
965
  order.
843
966
 
844
967
  The hook process CWD is the directory where git-p4son was invoked, and `GIT_P4SON_REPO_ROOT_DIR` is
845
- set to the git-p4son repository root.
968
+ set to the git-p4son repository root. `GIT_P4SON_BACKEND` is `git` or `jj`, the [backend](#choosing-the-backend)
969
+ the command runs with.
846
970
 
847
971
  On Windows, `.ps1`, `.nu`, `.sh`, and `.py` files are run with
848
972
  `powershell.exe -NoProfile -ExecutionPolicy Bypass -File`, `nu.exe`, `bash.exe`, and `python.exe` respectively.
@@ -868,6 +992,208 @@ before the first sync.
868
992
  After a successful `git-p4son sync` that actually performs sync work, git-p4son runs executable hooks from
869
993
  `.git-p4son/hooks/post-sync/`. They don't run when the workspace is already at the requested changelist.
870
994
 
995
+ ## Jujutsu (jj)
996
+
997
+ git-p4son detects a jj workspace colocated with the git repository, which needs jj 0.41 or newer. CI tests both
998
+ 0.41 and the latest jj release, and the minimum only rises with a note in the CHANGELOG. Separate jj workspaces,
999
+ sparse working copies, and conflicted or divergent changes are not supported. Without a `.jj` folder, nothing
1000
+ changes.
1001
+
1002
+ ### The jj p4son alias
1003
+
1004
+ jj has no external subcommands, but an alias can run git-p4son, so `jj p4son sync` runs `git p4son sync`. Add it
1005
+ once per machine to jj's user config, or let `init` offer it. From jj 0.42, an alias can carry a doc, which jj's
1006
+ shell completion shows next to `p4son`:
1007
+
1008
+ ```sh
1009
+ jj config set --user aliases.p4son '{ definition = ["util", "exec", "--", "git-p4son"], doc = "Run git-p4son" }'
1010
+ ```
1011
+
1012
+ With jj 0.41, set `aliases.p4son` to the list alone, `'["util", "exec", "--", "git-p4son"]'`. `init` writes
1013
+ whichever form the installed jj supports.
1014
+
1015
+ `jj util exec` neither locks nor snapshots, so `jj p4son` behaves exactly as `git p4son`, and git-p4son's messages
1016
+ keep saying `git p4son`. Run it from inside the workspace: `jj -R other-workspace p4son` would still run in the
1017
+ current directory, so git-p4son refuses with "Run jj p4son from inside the workspace; -R is not supported." Shell
1018
+ completion for `jj p4son` needs [a hook of its own](#jj-p4son).
1019
+
1020
+ ### Choosing the backend
1021
+
1022
+ The backend is the tool git-p4son follows for the change, the base and the branch: git or jj. By default it is jj
1023
+ in a jj workspace and git otherwise. To choose, set `core.backend` in `.git-p4son/config.toml`, or put the global
1024
+ `--backend` flag before the command, which overrides the setting:
1025
+
1026
+ ```toml
1027
+ [core]
1028
+ backend = "git" # "auto" (the default), "git" or "jj"
1029
+ ```
1030
+
1031
+ ```sh
1032
+ git p4son --backend git new -b main
1033
+ ```
1034
+
1035
+ `jj` outside a jj workspace is an error. In a jj workspace, the jj backend refuses to run when jj is missing or
1036
+ older than 0.41, and points to `--backend git`. The git backend needs no jj, and still holds jj's working-copy
1037
+ lock, which jj 0.41 or newer honours, so jj cannot snapshot halfway through a command.
1038
+
1039
+ With the jj backend in a jj workspace:
1040
+
1041
+ - `new`, `update` and `list-changes` work on the jj working change `@`. When `@` is empty, has no description or
1042
+ bookmark and has one parent, they work on that parent instead, so a fresh working change above a feature still
1043
+ refers to the feature.
1044
+ - `new` records the change's ID in the alias it saves, so later commands find the alias by change, whatever the
1045
+ change's bookmarks or description have become since. A new alias is named after the one local bookmark at the
1046
+ change, with `/` replaced by `-`, or, with no bookmark or several, after the first 8 letters of its change ID.
1047
+ - An alias records one change, and a change one alias: `new` refuses a change another alias records, naming it, and
1048
+ `--force` moves the record to the new alias. `alias new` records no change. `alias list` shows each recorded change,
1049
+ marked `(gone)` once jj no longer has it.
1050
+ - The default message is the first line of the change's description. Use `jj describe`, `-m` or `-F`.
1051
+ - `-b/--base-branch` takes a jj revision that resolves to one change, such as a bookmark, change ID or commit ID.
1052
+ Where it has a default, it's the change's parent rather than git's `HEAD~1`. `new` and `review` record it as a
1053
+ bookmark, so it has to name one, or resolve to a commit exactly one points at.
1054
+ - `new` and `update` use jj's snapshot of the working copy, check that it did not change while git-p4son took the
1055
+ lock, and refuse files jj left untracked. You do not need a separate committed change before opening files in
1056
+ Perforce. Before a Perforce revert overwrites a file, git-p4son saves the working file and puts it back
1057
+ afterwards, keeping its bytes, line endings and permissions instead of taking git's index version.
1058
+ - `init` holds jj's working-copy lock while it sets up the git repository. It also offers to colocate jj and to
1059
+ add the `jj p4son` alias, see [Init Command](#init-command).
1060
+
1061
+ For example:
1062
+
1063
+ ```sh
1064
+ jj git init --colocate # Add jj to an existing git-p4son repository (see Line endings), or answer yes in init
1065
+ jj p4son sync # From any change: syncs main, then rebases your changes onto it
1066
+
1067
+ jj new main -m "Fix login"
1068
+ # Edit files
1069
+ jj bookmark create fix-login
1070
+ jj p4son new -b main --review
1071
+ # Edit more files
1072
+ jj p4son update -b main --shelve
1073
+ ```
1074
+
1075
+ ### Syncing a bookmark
1076
+
1077
+ `sync` works from any change, as with git. It moves the sync bookmark, `[sync] branch` or `--branch`, defaulting
1078
+ to `main`, to each sync commit:
1079
+
1080
+ 1. It puts the bookmark's files on disk. Your working change keeps its content in jj, which snapshotted it first.
1081
+ 2. It syncs and commits, then releases jj's lock.
1082
+ 3. It rebases the changes you started from onto the moved bookmark, as `jj rebase -b <your change> -d main`, and
1083
+ lists any change that got conflicts, which jj records rather than stopping on.
1084
+ 4. It returns you to where you started: `jj edit` on the change you were editing, or `jj new` above it.
1085
+
1086
+ From the sync bookmark itself, nothing is rebased. `--no-rebase` skips the rebase and still returns you.
1087
+
1088
+ `sync` refuses when the sync bookmark points at the working change itself, which a sync would make part of the
1089
+ bookmark's history. Move the bookmark back with `jj bookmark set main -r @- --allow-backwards`, or, to keep the edits
1090
+ as a change on it, run `jj describe` and `jj new`.
1091
+
1092
+ You stay on the sync bookmark when:
1093
+
1094
+ - local changes were merged back: they are in a new working change on it, for you to review, and your changes are
1095
+ still rebased
1096
+ - a sync fails partway: jj's next command puts a new working change there, holding the files as Perforce left them,
1097
+ and nothing is rebased
1098
+ - the rebase fails: sync reports it with the `jj rebase` command to run and a non-zero exit code, and the sync is
1099
+ not undone
1100
+
1101
+ A failing pre-sync hook, or anything else before the first sync, puts your working change's files back.
1102
+
1103
+ `sync` still stages and commits with git, including git attributes and filters, and sets `core.autocrlf` to match
1104
+ jj's line ending conversion.
1105
+
1106
+ ### Line endings
1107
+
1108
+ jj writes the working files by its `working-copy.eol-conversion`, and git by its `core.autocrlf`. When the two
1109
+ disagree, jj and git see different line endings, and jj fills changes with line ending edits. Set them to go
1110
+ together:
1111
+
1112
+ | git's `core.autocrlf` | jj's `working-copy.eol-conversion` |
1113
+ | --- | --- |
1114
+ | `false`, the default | `none`, the default |
1115
+ | `true`, the default in Git for Windows | `input-output` |
1116
+ | `input` | `input` |
1117
+
1118
+ On the jj backend, git-p4son warns when they disagree, and names the jj setting that matches git, for example
1119
+ `jj config set --repo working-copy.eol-conversion input-output`. The check reads only `core.autocrlf`: `text` and
1120
+ `eol` attributes in a `.gitattributes` override it in git, and jj doesn't read them.
1121
+
1122
+ Set it before jj's first snapshot, as `jj git init --colocate` takes one. Once jj has recorded the files with the
1123
+ wrong line endings, a later setting doesn't fix them, as jj only reads the files again when they change. `init`
1124
+ colocates with the matching setting, and to colocate by hand, pass it to the init too:
1125
+
1126
+ ```sh
1127
+ jj --config working-copy.eol-conversion=input-output git init --colocate
1128
+ jj config set --repo working-copy.eol-conversion input-output
1129
+ ```
1130
+
1131
+ If you colocated without it, and jj shows every text file as changed, colocate again before doing anything else in
1132
+ jj. Don't `jj restore` that change: it holds every file the first snapshot added, and restoring it deletes them from
1133
+ disk, including your untracked work. Colocating again leaves the files alone:
1134
+
1135
+ ```sh
1136
+ rm -rf .jj # Remove-Item -Recurse -Force .jj in PowerShell
1137
+ git reset # Drops the entries jj added to git's index
1138
+ jj --config working-copy.eol-conversion=input-output git init --colocate
1139
+ jj config set --repo working-copy.eol-conversion input-output
1140
+ ```
1141
+
1142
+ If git ignores folders only by matching without case, see [Ignored files](#ignored-files): `git p4son init`
1143
+ colocates with both settings.
1144
+
1145
+ ### Ignored files
1146
+
1147
+ git matches `.gitignore` patterns without case when `core.ignorecase` is true, the default on Windows and macOS. jj
1148
+ always matches them with case, so it would track a `Thirdparty/` folder that git ignores through `ThirdParty/`.
1149
+
1150
+ To keep jj from tracking them, git-p4son leaves each such folder, or file, out of jj's `snapshot.auto-track` for the
1151
+ repository, building on any value you have, for example `(all()) ~ root:Some/Plugin/Source/Thirdparty`. That's
1152
+ jj's own setting, so it works whatever `.gitignore` re-includes with `!`, which would outrank `.git/info/exclude`,
1153
+ and it leaves git alone. `jj status` then lists the folders under "Untracked paths". As the repository's value holds
1154
+ yours, a later change to your user-level `snapshot.auto-track` doesn't reach the repository.
1155
+
1156
+ git-p4son does so when `init` colocates, passing the setting to `jj git init`, as jj snapshots straight away, and in
1157
+ the steps it lists for colocating a repository it creates by hand. It also does so at the end of each sync on the jj
1158
+ backend, as a sync can bring in new folders. To let jj track such a folder again, remove its `root:"..."` from
1159
+ `snapshot.auto-track`, with `jj config edit --repo`.
1160
+
1161
+ If jj tracks such files anyway, for example in a workspace colocated before git-p4son did this, git-p4son warns about
1162
+ the ones the change you're working on adds, unless it's a commit in the sync branch's history, which git made. The
1163
+ warning lists the `jj config set` that leaves them out and the `jj file untrack` command that stops tracking them,
1164
+ which leaves the files on disk. Both are quoted to paste into cmd, PowerShell or a POSIX shell alike. They name a
1165
+ folder only when git tracks nothing else in it, so files git tracks stay tracked. Files you track on purpose, with
1166
+ `jj file track --include-ignored`, are left alone.
1167
+
1168
+ ### Locking, and the git workflow
1169
+
1170
+ git-p4son holds jj's working-copy lock while commands run, including sync hooks and temporary Perforce reverts,
1171
+ so other jj processes cannot snapshot files halfway through. A busy lock stops the command. Queries during the
1172
+ command use a pinned jj operation without snapshotting.
1173
+
1174
+ jj's first snapshot and final reconciliation happen outside the lock, and so do sync's rebase and return, so
1175
+ another jj command can import the finished git changes before git-p4son does: this is not one atomic jj
1176
+ transaction. `--verbose` logs the jj operation git-p4son pinned at the start. The lock does not keep out
1177
+ editors, git commands or other jj workspaces. If a command fails, look at the workspace before continuing: the
1178
+ lock is released, but Perforce operations that completed are not undone.
1179
+
1180
+ Hooks must not run ordinary jj commands, which would wait for the lock their caller holds. Read-only queries such
1181
+ as `jj --at-op=@ log` avoid snapshotting and the git import. A hook that runs git-p4son itself needs
1182
+ `--backend git`, which reuses the caller's lock.
1183
+
1184
+ `review`, `new --per-commit` and `update --per-commit` run once per commit since the base, up to the jj change, as they
1185
+ do in git. Under jj's lock, each step puts its commit's files on disk with git, and the working change's files go back
1186
+ at the end, also when a step fails, so jj sees no change. No jj command runs, and untracked and ignored files are left
1187
+ alone. They refuse to start if files changed after jj's snapshot, as the checkouts would overwrite them:
1188
+
1189
+ ```sh
1190
+ git p4son review -b main
1191
+ git p4son update --per-commit --shelve -b main
1192
+ ```
1193
+
1194
+ To use the git workflow on purpose, put `--backend git` **before** the command. This uses git branches, `HEAD` and
1195
+ git's clean-workspace check.
1196
+
871
1197
  ## Claude Code Skill
872
1198
 
873
1199
  git-p4son ships a skill for Claude Code that teaches it how git-p4son is meant to be used: keeping `main` in sync
@@ -930,3 +1256,24 @@ Add the following to your PowerShell profile (`$PROFILE`):
930
1256
 
931
1257
  All three enable completion for `git p4son <TAB>` and `git-p4son <TAB>`.
932
1258
 
1259
+ ### jj p4son
1260
+
1261
+ jj completes an alias's arguments as file paths, so `jj p4son <TAB>` needs a hook of its own. It completes the words
1262
+ after `p4son` as `git p4son` does, skipping jj's global options such as `-R` or `--color` before it, and hands every
1263
+ other `jj` completion to jj's own. Load it after jj's completion; the PowerShell hook needs PowerShell 7 (`pwsh`):
1264
+
1265
+ ```bash
1266
+ source $(git-p4son completion bash --jj) # ~/.bashrc
1267
+ ```
1268
+
1269
+ ```zsh
1270
+ source $(git-p4son completion zsh --jj) # ~/.zshrc, after compinit
1271
+ ```
1272
+
1273
+ ```powershell
1274
+ . $(git-p4son completion powershell --jj) # $PROFILE
1275
+ ```
1276
+
1277
+ Each loads the `git p4son` completion for its shell too. If you named the alias something other than `p4son`, set
1278
+ `GIT_P4SON_JJ_ALIAS` to its name.
1279
+