opencode-gitbutler 0.1.3 → 0.1.5
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.
- package/README.md +13 -21
- package/command/b-branch-gc.md +8 -0
- package/dist/index.js +58 -11
- package/dist/reword.d.ts.map +1 -1
- package/package.json +1 -1
- package/skill/gitbutler/SKILL.md +55 -0
- package/skill/gitbutler/references/cheatsheet.md +14 -0
- package/skill/gitbutler/references/concepts.md +34 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ This plugin bridges the gap by bringing GitButler's virtual branch power directl
|
|
|
13
13
|
### What This Plugin Does Differently
|
|
14
14
|
|
|
15
15
|
- Only tool that combines automatic branch creation, LLM commits, file assignment, and context injection.
|
|
16
|
-
- Zero-config setup. Just
|
|
16
|
+
- Zero-config setup. Just add it to your plugins and go.
|
|
17
17
|
- Works with GitButler virtual branches to avoid worktree overhead.
|
|
18
18
|
- Impersonates Cursor for full GitButler CLI compatibility.
|
|
19
19
|
- Unique multi-agent session mapping.
|
|
@@ -21,37 +21,29 @@ This plugin bridges the gap by bringing GitButler's virtual branch power directl
|
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
### 1. Add plugin to OpenCode config
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
bun add opencode-gitbutler
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Or add it to your plugins array in `.opencode/config.json`:
|
|
26
|
+
Add to your `opencode.json` (global or project-level):
|
|
31
27
|
|
|
32
28
|
```json
|
|
33
29
|
{
|
|
34
|
-
"
|
|
30
|
+
"plugin": [
|
|
31
|
+
"opencode-gitbutler@latest"
|
|
32
|
+
]
|
|
35
33
|
}
|
|
36
34
|
```
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
OpenCode will install the plugin automatically on next launch.
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
- **OpenCode** — v1.1.0 or later
|
|
42
|
-
- **Bun** — v1.0.0 or later (plugin runtime)
|
|
38
|
+
### 2. Install GitButler CLI
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
```bash
|
|
41
|
+
brew install gitbutler
|
|
42
|
+
```
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
See [GitButler installation docs](https://docs.gitbutler.com/installation) for other methods.
|
|
49
45
|
|
|
50
|
-
|
|
51
|
-
{
|
|
52
|
-
"plugins": ["opencode-gitbutler"]
|
|
53
|
-
}
|
|
54
|
-
```
|
|
46
|
+
### 3. Restart OpenCode
|
|
55
47
|
|
|
56
48
|
The plugin automatically:
|
|
57
49
|
- Creates and renames branches based on your prompts
|
package/command/b-branch-gc.md
CHANGED
|
@@ -6,6 +6,14 @@ description: Clean up empty or orphaned GitButler branches
|
|
|
6
6
|
|
|
7
7
|
Identify and remove empty or orphaned `ge-branch-*` branches that have accumulated from past sessions.
|
|
8
8
|
|
|
9
|
+
## When to Run
|
|
10
|
+
|
|
11
|
+
- **After multi-session work** — each agent session may create `ge-branch-*` branches that become empty after commits are moved or squashed
|
|
12
|
+
- **When `but status` shows 3+ `ge-branch-*` branches** — workspace clutter slows down branch selection and increases lock contention
|
|
13
|
+
- **After `but cursor stop` failures** — the plugin's auto-cleanup has ~12% failure rate; empty branches may persist
|
|
14
|
+
- **When user reports "too many branches"** — proactively offer to run this command
|
|
15
|
+
- **User-named branches are NEVER auto-cleaned** — only `ge-branch-*` pattern branches are candidates
|
|
16
|
+
|
|
9
17
|
## Additional Instructions
|
|
10
18
|
|
|
11
19
|
$ARGUMENTS
|
package/dist/index.js
CHANGED
|
@@ -892,8 +892,46 @@ function createRewordManager(deps) {
|
|
|
892
892
|
let cleanupCount = 0;
|
|
893
893
|
let failCount = 0;
|
|
894
894
|
let latestBranchName = null;
|
|
895
|
+
const owner = branchOwnership.get(conversationId);
|
|
896
|
+
const ownershipMatches = !owner || owner.rootSessionID === rootSessionID;
|
|
897
|
+
if (!ownershipMatches && owner) {
|
|
898
|
+
log.warn("branch-ownership-mismatch", {
|
|
899
|
+
conversationId,
|
|
900
|
+
expectedRootSessionID: owner.rootSessionID,
|
|
901
|
+
actualRootSessionID: rootSessionID
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
const candidateBranchCliIds = new Set;
|
|
905
|
+
if (ownershipMatches && editedFiles && editedFiles.size > 0) {
|
|
906
|
+
for (const filePath of editedFiles) {
|
|
907
|
+
const branchInfo = cli.findFileBranch(filePath, status);
|
|
908
|
+
if (branchInfo.branchCliId) {
|
|
909
|
+
candidateBranchCliIds.add(branchInfo.branchCliId);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
if (ownershipMatches && owner?.branchName && !owner.branchName.startsWith("conversation-")) {
|
|
914
|
+
for (const stack of status.stacks) {
|
|
915
|
+
for (const branch of stack.branches ?? []) {
|
|
916
|
+
if (branch.name === owner.branchName) {
|
|
917
|
+
candidateBranchCliIds.add(branch.cliId);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
const candidateBranches = status.stacks.flatMap((stack) => stack.branches ?? []).filter((branch) => candidateBranchCliIds.has(branch.cliId));
|
|
923
|
+
if (candidateBranches.length === 0) {
|
|
924
|
+
log.info("post-stop-no-candidate-branches", {
|
|
925
|
+
conversationId,
|
|
926
|
+
rootSessionID,
|
|
927
|
+
editedFiles: editedFiles?.size ?? 0,
|
|
928
|
+
ownerBranchName: owner?.branchName
|
|
929
|
+
});
|
|
930
|
+
}
|
|
895
931
|
for (const stack of status.stacks) {
|
|
896
932
|
for (const branch of stack.branches ?? []) {
|
|
933
|
+
if (!candidateBranchCliIds.has(branch.cliId))
|
|
934
|
+
continue;
|
|
897
935
|
if (branch.branchStatus !== "completelyUnpushed")
|
|
898
936
|
continue;
|
|
899
937
|
if (branch.commits.length === 0)
|
|
@@ -993,18 +1031,27 @@ function createRewordManager(deps) {
|
|
|
993
1031
|
}
|
|
994
1032
|
}
|
|
995
1033
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1034
|
+
const shouldSetTitle = candidateBranches.length === 1;
|
|
1035
|
+
if (!shouldSetTitle && candidateBranches.length > 1) {
|
|
1036
|
+
log.info("session-title-skipped-ambiguous", {
|
|
1037
|
+
conversationId,
|
|
1038
|
+
rootSessionID,
|
|
1039
|
+
candidateBranches: candidateBranches.map((b) => ({
|
|
1040
|
+
cliId: b.cliId,
|
|
1041
|
+
name: b.name
|
|
1042
|
+
}))
|
|
1043
|
+
});
|
|
1001
1044
|
}
|
|
1002
|
-
if (
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1045
|
+
if (shouldSetTitle) {
|
|
1046
|
+
const singleBranch = candidateBranches[0];
|
|
1047
|
+
const titleToSet = latestBranchName ?? singleBranch.name;
|
|
1048
|
+
if (titleToSet) {
|
|
1049
|
+
client.session.update({
|
|
1050
|
+
path: { id: rootSessionID },
|
|
1051
|
+
body: { title: titleToSet }
|
|
1052
|
+
}).catch(() => {});
|
|
1053
|
+
addNotification(sessionID, `Session title updated to \`${titleToSet}\``);
|
|
1054
|
+
}
|
|
1008
1055
|
}
|
|
1009
1056
|
for (const stack of status.stacks) {
|
|
1010
1057
|
for (const branch of stack.branches ?? []) {
|
package/dist/reword.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reword.d.ts","sourceRoot":"","sources":["../src/reword.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,GAAG,EAAiB,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,GAAG,CAAC;IACT,MAAM,EAAE,qBAAqB,CAAC;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,EAAE,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,KAAK,MAAM,CAAC;IAC9D,sBAAsB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC9C,0BAA0B,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,eAAe,EAAE,CACf,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,EAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,EACrB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,KACpC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,MAAM,EAAE;QACN,OAAO,EAAE;YACP,QAAQ,EAAE,CAAC,IAAI,EAAE;gBACf,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBACrB,KAAK,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAA;iBAAE,CAAC;aAC1B,KAAK,OAAO,CAAC;gBACZ,IAAI,CAAC,EAAE,KAAK,CAAC;oBACX,IAAI,EAAE;wBAAE,IAAI,EAAE,MAAM,CAAA;qBAAE,CAAC;oBACvB,KAAK,EAAE,KAAK,CAAC;wBAAE,IAAI,EAAE,MAAM,CAAC;wBAAC,IAAI,CAAC,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAC;iBAC/C,CAAC,CAAC;aACJ,CAAC,CAAC;YACH,MAAM,EAAE,CAAC,IAAI,EAAE;gBACb,IAAI,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAA;iBAAE,CAAC;aACzB,KAAK,OAAO,CAAC;gBAAE,IAAI,CAAC,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,CAAC,CAAC;YACzC,MAAM,EAAE,CAAC,IAAI,EAAE;gBACb,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBACrB,IAAI,EAAE;oBACJ,KAAK,EAAE;wBAAE,UAAU,EAAE,MAAM,CAAC;wBAAC,OAAO,EAAE,MAAM,CAAA;qBAAE,CAAC;oBAC/C,MAAM,EAAE,MAAM,CAAC;oBACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;oBAC7B,KAAK,EAAE,KAAK,CAAC;wBAAE,IAAI,EAAE,MAAM,CAAC;wBAAC,IAAI,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAC;iBAC9C,CAAC;aACH,KAAK,OAAO,CAAC;gBACZ,IAAI,CAAC,EAAE;oBACL,KAAK,EAAE,KAAK,CAAC;wBAAE,IAAI,EAAE,MAAM,CAAC;wBAAC,IAAI,CAAC,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAC;iBAC/C,CAAC;aACH,CAAC,CAAC;YACH,MAAM,EAAE,CAAC,IAAI,EAAE;gBACb,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;aACtB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;YACvB,MAAM,EAAE,CAAC,IAAI,EAAE;gBACb,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBACrB,IAAI,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAA;iBAAE,CAAC;aACzB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SACxB,CAAC;KACH,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,CA8BA,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAiBtD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAStE;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC/D,wBAAwB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3F,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpH,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAU5D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"reword.d.ts","sourceRoot":"","sources":["../src/reword.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skill/gitbutler/SKILL.md
CHANGED
|
@@ -60,6 +60,9 @@ You can batch multiple file edits before committing - no need to commit after ev
|
|
|
60
60
|
- After edits, run `but status --json` and move your file/hunk IDs to the correct branch via `but stage` or `but commit --changes`.
|
|
61
61
|
3. **Validate branch ownership before commit.**
|
|
62
62
|
- Confirm each changed file/hunk belongs to the intended branch/task, then commit only those IDs.
|
|
63
|
+
4. **Respect branch ownership across sessions.**
|
|
64
|
+
- In multi-agent environments, branches may belong to other agent sessions. Never reword, rename, or push branches you didn't create in this session.
|
|
65
|
+
- If you need to modify another session's branch, ask the user first.
|
|
63
66
|
|
|
64
67
|
## Quick Start
|
|
65
68
|
|
|
@@ -257,6 +260,34 @@ but absorb # Absorb any auto-formatted c
|
|
|
257
260
|
| Lefthook `pre-commit.old` accumulates | Lefthook creates `pre-commit.old` backup that conflicts on next install | Add `rm -f .git/hooks/pre-commit.old` to `prepare` script in package.json |
|
|
258
261
|
| `but pull` before unapply | Pulling with merged branches still applied causes orphan errors | **Always** `but unapply <merged-branch>` before `but pull` |
|
|
259
262
|
| `but unapply` after remote branch deletion | `but unapply` fails with "branch not found" when remote deleted the branch (e.g. `--delete-branch` on merge), and subsequent `but pull` fails with "resolution mismatch" | Use GitButler desktop app to pull, or `but teardown` → `but setup` → `but config target origin/<branch>` |
|
|
263
|
+
| Split-hunk files stuck in `zz` | File has hunks locked to commits on different branches — GitButler sets `stack_id=None`, plugin considers file "handled" via lock reference | Manually commit each hunk: `but diff --json` to get hunk IDs, then `but commit <branch> -m "msg" --changes <hunk-id>` for each |
|
|
264
|
+
| Plugin auto-cleanup misses empty branches | `ge-branch-*` cleanup has ~12% failure rate; user-named empty branches are never auto-cleaned | Run `/b-branch-gc` command or manually `but unapply <branch-id>` |
|
|
265
|
+
| Notifications not reaching agent | Plugin notification delivery is ~55% (259 queued vs 142 delivered in observed sessions) | Always verify state with `but status --json` — don't rely on `<system-reminder>` notifications alone |
|
|
266
|
+
|
|
267
|
+
### Diagnosing `zz` Stuck Files
|
|
268
|
+
|
|
269
|
+
When files are stuck in `zz` (unassigned) and don't auto-recover:
|
|
270
|
+
|
|
271
|
+
1. **Identify the cause:**
|
|
272
|
+
```bash
|
|
273
|
+
but status --json -f # Look for files in zz with [LOCKED] markers
|
|
274
|
+
but diff --json # Get hunk-level IDs and see lock targets
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
2. **If hunks are locked to different branches** (split-hunk scenario):
|
|
278
|
+
- Each hunk must be committed to its locked branch individually
|
|
279
|
+
- `but commit <branch> -m "msg" --changes <hunk-id>` for each hunk
|
|
280
|
+
- Or `but rub <hunk-id> <commit-id>` to amend into the locked commit
|
|
281
|
+
|
|
282
|
+
3. **If files have no locks but are still in `zz`:**
|
|
283
|
+
- Plugin's `after-edit` may have failed silently — stage manually
|
|
284
|
+
- `but stage <file-id> <branch>` or commit with `--changes`
|
|
285
|
+
|
|
286
|
+
4. **If many files are stuck after `but cursor stop`:**
|
|
287
|
+
- Run `but rub <file-id> <branch-id>` for each file to force assignment
|
|
288
|
+
- This is the most reliable recovery method
|
|
289
|
+
|
|
290
|
+
**Key insight:** Auto-recovery won't fix multi-branch locked files. If you see `[LOCKED]` in `zz`, manual intervention is required.
|
|
260
291
|
|
|
261
292
|
## Critical Safety Rules
|
|
262
293
|
|
|
@@ -273,3 +304,27 @@ but absorb # Absorb any auto-formatted c
|
|
|
273
304
|
6. Use `--dry-run` flags (push, absorb) when unsure
|
|
274
305
|
7. **Run `but pull` frequently** — at session start, before creating branches, and before pushing. Stale workspace = merge conflicts
|
|
275
306
|
8. When updating this skill, use `but skill install --path <known-path>` to avoid prompts
|
|
307
|
+
9. **Check for `zz` files with locks before finishing work.** Run `but status --json -f` and look for files in `zz` with `[LOCKED]` markers. These won't auto-recover — you must manually commit each hunk to its correct branch using `--changes <hunk-id>`. See [references/concepts.md — Hunk Locking](references/concepts.md) for details.
|
|
308
|
+
10. **Don't trust notifications alone** — plugin notification delivery is ~55%. Always verify workspace state with `but status --json` before making assumptions about branch assignments or commit status.
|
|
309
|
+
|
|
310
|
+
## Plugin Auto-Behaviors (What Happens Behind the Scenes)
|
|
311
|
+
|
|
312
|
+
The GitButler plugin performs several actions automatically. **You do NOT need to do these yourself** — but you should know they happen so you don't duplicate work or get confused by unexpected state changes.
|
|
313
|
+
|
|
314
|
+
| Behavior | Trigger | What It Does |
|
|
315
|
+
|----------|---------|--------------|
|
|
316
|
+
| **File auto-assign** | After each file edit | Finds which branch owns the file and runs `but cursor after-edit` or `but rub` to assign it |
|
|
317
|
+
| **Auto-commit** | Session idle (agent stops editing) | Runs `but cursor stop` which commits all uncommitted changes to their assigned branches |
|
|
318
|
+
| **LLM commit reword** | After auto-commit | Rewrites generic commit messages using Claude Haiku based on the actual diff |
|
|
319
|
+
| **Branch rename** | After auto-commit | Renames `ge-branch-*` branches to descriptive names based on user's prompt |
|
|
320
|
+
| **Empty branch cleanup** | After auto-commit | Removes `ge-branch-*` branches with 0 commits (~88% success rate) |
|
|
321
|
+
| **Session title sync** | After auto-commit | Updates session title from branch name |
|
|
322
|
+
| **Context injection** | Before each agent message | Injects `<system-reminder>` with workspace notifications (branch created, commits made, etc.) |
|
|
323
|
+
|
|
324
|
+
### What This Means for You
|
|
325
|
+
|
|
326
|
+
1. **Don't manually rename `ge-branch-*` branches** — the plugin will do it after idle
|
|
327
|
+
2. **Don't reword auto-generated commit messages** — the plugin rewrites them with LLM
|
|
328
|
+
3. **If you see unexpected commits** — check if the plugin auto-committed during an idle event
|
|
329
|
+
4. **Notification delivery is ~55%** — not all injected notifications reach you. Always verify state with `but status --json` rather than relying on notifications
|
|
330
|
+
5. **Empty branch cleanup can fail** — if you see stale `ge-branch-*` branches, run `/b-branch-gc`
|
|
@@ -201,6 +201,7 @@ but pull # Then pull merged changes
|
|
|
201
201
|
| `M` | Modified |
|
|
202
202
|
| `D` | Deleted |
|
|
203
203
|
| `[LOCKED]` | File depends on specific commit (absorb target) |
|
|
204
|
+
| `zz` | Unassigned — file not staged to any branch |
|
|
204
205
|
| `●` | Commit |
|
|
205
206
|
| `CONFLICTED` | Needs conflict resolution |
|
|
206
207
|
|
|
@@ -269,3 +270,16 @@ but pr new feat/my-feature -t
|
|
|
269
270
|
but unapply feat/my-feature
|
|
270
271
|
but pull
|
|
271
272
|
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Troubleshooting Quick Reference
|
|
277
|
+
|
|
278
|
+
| Symptom | Cause | Fix |
|
|
279
|
+
|---|---|---|
|
|
280
|
+
| Files stuck in `zz` with `[LOCKED]` | Hunks locked to commits on different branches | `but diff --json` → commit each hunk individually with `--changes <hunk-id>` |
|
|
281
|
+
| Files in `zz` after edits (no locks) | Plugin `after-edit` didn't auto-assign | `but stage <file-id> <branch>` or `but commit <branch> -m "msg" --changes <id>` |
|
|
282
|
+
| Many empty `ge-branch-*` branches | Plugin auto-cleanup failed (~12% failure rate) | Run `/b-branch-gc` or `but unapply <branch-id>` for each |
|
|
283
|
+
| `but absorb` puts hunk on wrong commit | Hunk locked to commit on different branch | Use `but amend <file-id> <commit-id>` for explicit control |
|
|
284
|
+
| `but pull` fails after PR merge | Merged branch still applied in workspace | `but unapply <merged-branch>` first, then `but pull` |
|
|
285
|
+
| Changes "disappear" after `but cursor stop` | Plugin auto-committed to a `ge-branch-*` | `but status --json -f` — check all branches for your files |
|
|
@@ -177,6 +177,40 @@ Prevents you from creating broken states:
|
|
|
177
177
|
- Can't stage changes to wrong branches
|
|
178
178
|
- Ensures each branch remains independently functional
|
|
179
179
|
|
|
180
|
+
## Hunk Locking & Split Files
|
|
181
|
+
|
|
182
|
+
When a file has changes touching lines that belong to commits on **different branches**, GitButler "locks" those hunks.
|
|
183
|
+
|
|
184
|
+
### How Locking Works
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
File: src/api.ts
|
|
188
|
+
Line 10-15: depends on commit C1 (branch: feat/auth)
|
|
189
|
+
Line 40-50: depends on commit C3 (branch: fix/validation)
|
|
190
|
+
Line 80-90: new code, no dependency
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
- Lines 10-15 are **locked to** `feat/auth` — shown as `[LOCKED → C1]`
|
|
194
|
+
- Lines 40-50 are **locked to** `fix/validation` — shown as `[LOCKED → C3]`
|
|
195
|
+
- Lines 80-90 are **free** — can be staged/committed to any branch
|
|
196
|
+
|
|
197
|
+
### Split Hunk Assignment (`zz` Trap)
|
|
198
|
+
|
|
199
|
+
When a file's hunks are locked to **multiple different branches**, GitButler cannot auto-assign the file to any single branch. The file stays in `zz` (unassigned) with lock markers.
|
|
200
|
+
|
|
201
|
+
**This is the most common cause of files "stuck in `zz`"** — the plugin's `after-edit` hook sees the file is mentioned on a branch (via lock) and considers it "handled", but the file remains unassigned.
|
|
202
|
+
|
|
203
|
+
**Resolution:**
|
|
204
|
+
|
|
205
|
+
1. Run `but status --json -f` to identify locked files in `zz`
|
|
206
|
+
2. Use `but diff --json` to see individual hunk IDs and their lock targets
|
|
207
|
+
3. Commit or amend each hunk individually: `but commit <branch> -m "msg" --changes <hunk-id>`
|
|
208
|
+
4. Or use `but rub <hunk-id> <commit-id>` to amend specific hunks into their locked commits
|
|
209
|
+
|
|
210
|
+
### Key Takeaway
|
|
211
|
+
|
|
212
|
+
If files are stuck in `zz` with `[LOCKED]` markers, **don't wait for auto-recovery** — it won't happen for multi-branch locks. Manually assign each hunk to its correct branch.
|
|
213
|
+
|
|
180
214
|
## Empty Commits as Placeholders
|
|
181
215
|
|
|
182
216
|
You can create empty commits:
|