Update From Branch
Sync the current branch from another (usually main) via merge or rebase, auto-stashing dirty work and surfacing conflicts clearly — never discards changes. Any repo.
How to use it
Comes with the ai-devkit plugin (install — two commands). Once installed, type this in any repo:
/update-from-branch mainYou can also just describe the task in plain words — the agent picks the skill up by itself. That’s the Claude Code form — in Codex, type $update-from-branch instead, or just name the skill in plain words.
What a run looks like:
That’s all you do — the agent runs the whole workflow itself. Curious, or want to audit it? The playbook it follows is collapsed under Under the hood below.
Under the hood — the playbook the agent follows nothing in here is for you to run
Everything in this section is read and executed by the agent when you invoke the skill. It’s published so you can audit it, learn from it, or adapt it — not because you need to follow it yourself.
Bring the latest changes from another branch (usually main) into the branch you're on, without ever losing uncommitted work. The skill stashes dirty changes, fetches, applies via merge or rebase, restores the stash, and reports any conflicts with clear next steps. Safety-first: it shows what will happen before doing it and never discards anything.
Source Branch (optional):#
$ARGUMENTS
Workflow#
Phase 1: Orient + Detect the Source#
CURRENT=$(git branch --show-current)
# Source to sync FROM: the argument, else the detected base branch.
SOURCE="$ARGUMENTS"
if [ -z "$SOURCE" ]; then
if git ls-remote --heads origin develop | grep -q .; then SOURCE=develop
else SOURCE=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p'); SOURCE=${SOURCE:-main}; fi
fi
echo "Updating '$CURRENT' from '$SOURCE'"Guard rails — refuse politely if:
CURRENTisSOURCE(nothing to sync — you're on the branch you'd merge from).- There's a merge/rebase already in progress (
git statusshows it) — tell the user to finish or--abortit first.
Phase 2: Protect Dirty Work (auto-stash)#
DIRTY=0
if [ -n "$(git status --porcelain)" ]; then
DIRTY=1
git stash push --include-untracked -m "update-from-branch: WIP on $CURRENT"
echo "Stashed uncommitted changes (including untracked). They will be restored after the update."
fiThe work is parked in a named stash — it is never thrown away, even if later steps fail.
Phase 3: Fetch the Latest#
git fetch origin "$SOURCE" # syncs from the REMOTE origin/$SOURCE (the common case, e.g. main)
# How far apart are we? (behind = commits to pull in; ahead = your local commits)
BEHIND=$(git rev-list --count "HEAD..origin/$SOURCE")
AHEAD=$(git rev-list --count "origin/$SOURCE..HEAD")
echo "Behind origin/$SOURCE by $BEHIND · ahead by $AHEAD"If BEHIND is 0, you're already up to date — restore the stash (Phase 5) and stop.
Phase 4: Choose Strategy + Apply#
Decide merge vs rebase and state the choice before running it:
- Rebase — replays your local commits on top of
$SOURCEfor a clean, linear history. Best when your branch is unpushed or solo. Never rebase commits others have already pulled. - Merge — records a merge commit, preserving true history and the original commits. Best when the branch is shared/pushed, or your team prefers explicit merge topology.
Default heuristic: if the branch has an upstream and may be shared (git rev-parse --abbrev-ref @{u} succeeds and others could have it), prefer merge; for a private, unpushed branch, rebase is fine. When unsure, ask once, then default to merge (the non-rewriting, safer choice).# --- Rebase path ---
git rebase "origin/$SOURCE"
# --- Merge path ---
git merge --no-edit "origin/$SOURCE"Phase 5: Restore Dirty Work#
Only after the update lands cleanly:
if [ "$DIRTY" = "1" ]; then
git stash pop || echo "Stash pop hit conflicts — your WIP is safe in 'git stash list'; resolve, then 'git stash drop'."
fiPhase 6: Conflict Handling + Report#
If merge/rebase or the stash pop reports conflicts, stop and guide — do not attempt blind auto-resolution:
git status --short | grep '^UU\|^AA\|^DD' # conflicted paths
git diff --name-only --diff-filter=U # files needing resolutionTell the user, per path, the resolution loop and the abort escape hatch:
Conflicts in:
src/api/client ← edit to resolve, then: git add src/api/client
config ← edit to resolve, then: git add config
After resolving all: git rebase --continue (or: git commit, for a merge)
To back out entirely: git rebase --abort (or: git merge --abort)
Your stashed WIP (if any) remains in: git stash listFinal summary (clean case):
✅ '<current>' updated from '<source>'
Strategy: <merge|rebase> · pulled <N> commit(s)
WIP restored: <yes|none>Quick Reference#
Merge vs rebase#
| Merge | Rebase | |
|---|---|---|
| History | Preserves topology, adds merge commit | Linear, rewrites your commits |
| Use when | Branch is shared/pushed | Branch is private/unpushed |
| Risk | Extra merge commits | Never rewrite shared history |
Safety invariants#
- Dirty work is stashed, never discarded — recover from
git stash list. - Show the plan (behind/ahead, chosen strategy) before applying.
- On conflict: surface paths + resolution + abort command; never force.
- Refuse if a merge/rebase is already in progress.
Escape hatches#
Abort in-progress: git rebase --abort / git merge --abort. · Recover WIP: git stash list → git stash pop.
Adapt it to another stack ready-made prompt for Claude / Codex
Adapt to your platform
Copy this prompt into Claude or Codex, fill in your stack, and it will generate an adapted version for your project.
Port an agent skill (Claude Code / Codex SKILL.md format) to my project.
Below is "update-from-branch" from ai-devkit (origin platform: cross, portability: portable).
What it does: Sync the current branch from another (usually main) via merge or rebase, auto-stashing dirty work and surfacing conflicts clearly — never discards changes. Any repo.
Read it, then produce an equivalent SKILL.md (plus any scripts) for MY project:
<describe your stack, conventions, and repo here>
Keep the workflow's structure and intent. Replace platform-specific tooling and references per these notes:
Generic and safety-first. Pick the default merge-vs-rebase choice to match your team's history policy (rebase for linear history; merge to preserve true topology and never rewrite shared commits). The base-branch detection and auto-stash are portable as-is.
List what you changed and why.
--- SKILL.md ---
# Update From Branch
Bring the latest changes from another branch (usually `main`) into the branch you're on, **without ever losing uncommitted work**. The skill stashes dirty changes, fetches, applies via merge or rebase, restores the stash, and reports any conflicts with clear next steps. Safety-first: it shows what will happen before doing it and never discards anything.
## Source Branch (optional):
$ARGUMENTS
## Workflow
### Phase 1: Orient + Detect the Source
```bash
CURRENT=$(git branch --show-current)
# Source to sync FROM: the argument, else the detected base branch.
SOURCE="$ARGUMENTS"
if [ -z "$SOURCE" ]; then
if git ls-remote --heads origin develop | grep -q .; then SOURCE=develop
else SOURCE=$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p'); SOURCE=${SOURCE:-main}; fi
fi
echo "Updating '$CURRENT' from '$SOURCE'"
```
Guard rails — refuse politely if:
- `CURRENT` **is** `SOURCE` (nothing to sync — you're on the branch you'd merge from).
- There's a merge/rebase already in progress (`git status` shows it) — tell the user to finish or `--abort` it first.
### Phase 2: Protect Dirty Work (auto-stash)
```bash
DIRTY=0
if [ -n "$(git status --porcelain)" ]; then
DIRTY=1
git stash push --include-untracked -m "update-from-branch: WIP on $CURRENT"
echo "Stashed uncommitted changes (including untracked). They will be restored after the update."
fi
```
The work is parked in a named stash — it is never thrown away, even if later steps fail.
### Phase 3: Fetch the Latest
```bash
git fetch origin "$SOURCE" # syncs from the REMOTE origin/$SOURCE (the common case, e.g. main)
# How far apart are we? (behind = commits to pull in; ahead = your local commits)
BEHIND=$(git rev-list --count "HEAD..origin/$SOURCE")
AHEAD=$(git rev-list --count "origin/$SOURCE..HEAD")
echo "Behind origin/$SOURCE by $BEHIND · ahead by $AHEAD"
```
If `BEHIND` is 0, you're already up to date — restore the stash (Phase 5) and stop.
### Phase 4: Choose Strategy + Apply
Decide **merge vs rebase** and state the choice before running it:
- **Rebase** — replays your local commits on top of `$SOURCE` for a clean, linear history. Best when your branch is **unpushed or solo**. Never rebase commits others have already pulled.
- **Merge** — records a merge commit, preserving true history and the original commits. Best when the branch is **shared/pushed**, or your team prefers explicit merge topology.
> Default heuristic: if the branch has an upstream and may be shared (`git rev-parse --abbrev-ref @{u}` succeeds and others could have it), prefer **merge**; for a private, unpushed branch, **rebase** is fine. When unsure, ask once, then default to merge (the non-rewriting, safer choice).
```bash
# --- Rebase path ---
git rebase "origin/$SOURCE"
# --- Merge path ---
git merge --no-edit "origin/$SOURCE"
```
### Phase 5: Restore Dirty Work
Only after the update lands cleanly:
```bash
if [ "$DIRTY" = "1" ]; then
git stash pop || echo "Stash pop hit conflicts — your WIP is safe in 'git stash list'; resolve, then 'git stash drop'."
fi
```
### Phase 6: Conflict Handling + Report
If merge/rebase or the stash pop reports conflicts, **stop and guide** — do not attempt blind auto-resolution:
```bash
git status --short | grep '^UU\|^AA\|^DD' # conflicted paths
git diff --name-only --diff-filter=U # files needing resolution
```
Tell the user, per path, the resolution loop and the abort escape hatch:
```
Conflicts in:
src/api/client ← edit to resolve, then: git add src/api/client
config ← edit to resolve, then: git add config
After resolving all: git rebase --continue (or: git commit, for a merge)
To back out entirely: git rebase --abort (or: git merge --abort)
Your stashed WIP (if any) remains in: git stash list
```
Final summary (clean case):
```
✅ '<current>' updated from '<source>'
Strategy: <merge|rebase> · pulled <N> commit(s)
WIP restored: <yes|none>
```
## Quick Reference
### Merge vs rebase
| | Merge | Rebase |
|---|---|---|
| History | Preserves topology, adds merge commit | Linear, rewrites your commits |
| Use when | Branch is shared/pushed | Branch is private/unpushed |
| Risk | Extra merge commits | Never rewrite shared history |
### Safety invariants
- Dirty work is **stashed**, never discarded — recover from `git stash list`.
- Show the plan (behind/ahead, chosen strategy) before applying.
- On conflict: surface paths + resolution + abort command; never force.
- Refuse if a merge/rebase is already in progress.
### Escape hatches
Abort in-progress: `git rebase --abort` / `git merge --abort`. · Recover WIP: `git stash list` → `git stash pop`.