.agents/skills/jj-history-investigation/SKILL.md
Investigate jj commit history, split commits, annotate file changes, and clean up redundant commits. Use when debugging history, splitting large commits, or rewriting shared history.
npx skillsauth add edmundmiller/dotfiles jj-history-investigationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When working with complex jj repositories, you may need to investigate historical commits, split monolithic changes into focused commits, or clean up messy history. This skill documents efficient techniques for:
| Command | Purpose | Example |
| ------------------ | ---------------------------- | ------------------------------------------ |
| jj log | View commit history | jj log -r 'ancestors(@, 10)' |
| jj show | Display commit details | jj show abc123 |
| jj file annotate | Line-by-line change tracking | jj file annotate src/main.tex |
| jj edit | Start editing a commit | jj edit abc123 --ignore-immutable |
| jj split | Split a commit into pieces | jj split --ignore-immutable path/to/file |
| jj abandon | Remove commits from history | jj abandon abc123 |
| jj rebase | Move commits to new base | jj rebase -b main -d new-base |
When you know a file changed but not when, use these approaches:
Check recent commit messages:
# Search for commits mentioning "copyright"
jj log -r 'description(copyright)' --no-graph
# Output:
# trsozpwy feat(frontmatter): Add post-defense required sections
# (shows commits with "copyright" in the message)
View commit details:
# Show full commit with statistics
jj show trsozpwy --stat
# Show just the files changed
jj show trsozpwy --summary
Check specific file history:
# See who/when each line was added
jj file annotate src/main.tex | grep "copyrightpage"
# Output:
# trsozpwy edmund.m 2025-11-09 00:13:05 49: \copyrightpage{2025}
Check what's built on top of a commit:
# View descendants of commit abc123
jj log -r 'abc123::@' --limit 20
# Check ancestors of current commit
jj log -r 'ancestors(@, 10)'
Find relationships between branches:
# See where main and feature branches diverged
jj log -r 'ancestors(main) & ancestors(feature)'
# Check if commit is ancestor of main
jj log -r 'abc123::main'
Get commit metadata:
# Show commit ID, author, date
jj show abc123 | head -10
# Get just the change ID
jj log -r abc123 --no-graph -T 'change_id'
Check file changes:
# See what changed in a commit
jj diff -r abc123
# See specific file diff
jj diff -r abc123 src/main.tex
Split a commit when it contains multiple logical changes that should be reviewed separately:
1. Check if commit is immutable:
jj edit abc123
# If error: "Commit abc123 is immutable"
# Then use --ignore-immutable flag
2. Edit the commit:
jj edit abc123 --ignore-immutable
# Working copy now at abc123
# File system shows state at that commit
3. Split by file/directory:
# Split out specific files
jj split --ignore-immutable src/main.tex
# jj will prompt for commit messages for:
# - Selected changes (what you specified)
# - Remaining changes (everything else)
4. Split incrementally:
# After first split, you're at "remaining changes"
# Split again to extract more pieces
jj split --ignore-immutable src/chapters/appendix.tex
5. Update commit messages:
# Update the commit you just split
jj describe @- -m "feat(frontmatter): Enable copyright page"
# Continue until all pieces are separated
Split and update TODO in same commit:
# 1. Split out the main change
jj split --ignore-immutable src/main.tex
# 2. In the split commit, also update TODO.org
# Edit TODO.org to mark item as DONE
jj describe -m "feat(frontmatter): Enable copyright page
Enable copyright on page ii as recommended by UTD.
Mark as complete in TODO.org."
Split multiple files into one commit:
# Save final state
cp src/main.tex /tmp/main.tex.final
# Restore to parent
jj restore --from @- src/main.tex
# Apply changes incrementally
# Make change 1, commit
# Make change 2, commit
# etc.
Commits are immutable when:
When it's safe:
How to override:
# Edit immutable commit
jj edit abc123 --ignore-immutable
# Split immutable commit
jj split --ignore-immutable src/main.tex
# Note: This will rewrite ALL descendant commits
What happens:
# Before split:
# abc123 (monolithic commit)
# ↓
# def456 (descendant 1)
# ↓
# ghi789 (descendant 2)
# After split:
# abc123 (piece 1)
# ↓
# xyz111 (piece 2)
# ↓
# def456' (rewritten descendant 1)
# ↓
# ghi789' (rewritten descendant 2)
Find empty commits:
# Show commit stats
jj show abc123 --stat
# If output shows "0 files changed", it's empty
Find conflicting commits:
# Check for conflicts
jj log -r 'all()' | grep conflict
# Output:
# × abc123 (conflict) feat: Add something
Remove redundant commits:
# Abandon single commit
jj abandon abc123
# Abandon multiple commits
jj abandon abc123 def456 ghi789
# What happens:
# - Commit is removed from history
# - Descendants are rebased onto parent
# - Changes are preserved in descendants
Abandon empty commits automatically:
# After rebasing, jj may create empty commits
# Abandon them to clean up
jj log -r 'all()' --summary | grep "(empty)"
jj abandon empty-commit-id
Common pattern:
# 1. Split monolithic commit
jj edit old-commit --ignore-immutable
jj split src/file1.txt
jj split src/file2.txt
# (repeat for each logical piece)
# 2. Check for redundant descendants
jj log -r 'old-commit::@' --limit 30
# 3. Abandon redundant commits
jj abandon redundant-commit-1 redundant-commit-2
# 4. Verify history
jj log -r 'old-commit::@' --no-graph
1. Identify conflicts:
jj status
# Output:
# Warning: There are unresolved conflicts at these paths:
# TODO.org 2-sided conflict
2. Create commit to resolve:
# Create new commit on top of conflict
jj new conflicted-commit-id
3. Read conflict markers:
cat TODO.org
# Output:
# <<<<<<< Conflict 1 of 1
# +++++++ Contents of side #1
# ** DONE Add something
# %%%%%%% Changes from base to side #2
# ** TODO Add something
# >>>>>>> Conflict 1 of 1 ends
4. Choose resolution:
# Edit file to pick side #1, side #2, or merge both
# Remove conflict markers
# Or use editor with conflict resolution
jj resolve TODO.org
5. Squash resolution:
# Merge resolution into parent
jj squash
# This resolves the conflict in the parent commit
1. Check commit lineage:
# View commits from base to current
jj log -r 'base-commit::@' --limit 50
# Check all commits are present
jj log -r 'commit1 | commit2 | commit3' --no-graph
2. Verify each commit is focused:
# For each commit in your split:
jj show commit-id --stat
# Should show:
# - Small number of files changed
# - Logical grouping of changes
# - Clear commit message
3. Check for conflicts:
# List all commits with conflicts
jj log -r 'all()' | grep conflict
# Should be empty after cleanup
4. Verify bookmarks:
# Check where main/feature branches point
jj log -r 'bookmarks()'
# Ensure they're at correct positions
After split/rebase/cleanup:
jj status(conflict) labels in jj log(empty) commits (unless intentional)Scenario: Historical commit trsozpwy contains 6 different changes mixed together. Need to split for review.
Investigation phase:
# 1. Find the commit
jj log -r 'description(post-defense)' --no-graph
# Found: trsozpwy
# 2. Check what's in it
jj show trsozpwy --stat
# Shows: 10 files changed
# 3. Check if immutable
jj edit trsozpwy
# Error: Commit is immutable (16 descendants)
# 4. Verify we can override
jj log -r 'trsozpwy::@' --limit 20
# Shows descendant commits, all owned by us
Split phase:
# 1. Override immutability
jj edit trsozpwy --ignore-immutable
# 2. Split out Python scripts
jj split --ignore-immutable src/figures/modules/
jj describe @- -m "feat(ch3): Add core overlap Venn diagram scripts"
# 3. Split out appendix fix
jj split --ignore-immutable src/chapters/appendix-*.tex
jj describe @- -m "fix(appendix): Remove 'A' label from solo appendix"
# 4. Split frontmatter pieces incrementally
# (Copyright page)
# Edit src/main.tex to add only copyright
jj commit -m "feat(frontmatter): Enable copyright page (2025)"
# (Acknowledgments)
# Edit src/main.tex to add acknowledgments
jj commit -m "feat(frontmatter): Add acknowledgments section"
# (And so on for biographical sketch, CV)
Cleanup phase:
# 1. Check for redundant descendants
jj log -r 'trsozpwy::@' | grep "Mark post-defense"
# Found: kqkm (redundant - duplicates our split work)
# 2. Abandon redundant commits
jj abandon kqkm lkto qqkq srls
# 3. Rebase main branch onto splits
jj rebase -b main -d last-split-commit
# 4. Resolve conflicts
jj new conflicted-commit
# Edit TODO.org to pick correct side
jj squash
Verification phase:
# 1. Check final structure
jj log -r 'first-split::@' --limit 30
# 2. Verify each split commit
jj show trso --stat # Core overlap scripts
jj show nlnr --stat # Appendix fix
jj show zspm --stat # Copyright page
jj show mrpk --stat # Acknowledgments
jj show wqrn --stat # Biographical sketch
jj show ynom --stat # CV
# 3. Confirm no conflicts
jj status
# (should be clean)
# 4. Check bookmarks
jj log -r 'bookmarks()'
# main and Cleanup should be on correct commits
Result: Successfully split 1 monolithic commit into 6 focused commits, each with clear intent and proper TODO.org updates.
Use aliases for common queries:
# In ~/.jjconfig.toml
[aliases]
recent = ["log", "-r", "ancestors(@, 20)", "--no-graph"]
conflicts = ["log", "-r", "conflicts()"]
empty = ["log", "-r", "empty()"]
Search commit messages efficiently:
# Case-insensitive search
jj log -r 'description("(?i)copyright")'
# Multiple terms
jj log -r 'description(frontmatter) & description(copyright)'
jj show first to understand what's in a commitcp important-file /tmp/ before major history rewritesjj log frequently during split/rebase operationsjj:jj-workflow - Basic jj operations and workflowjj:commit-messages - Writing good commit messagesjj:commit-curation - Organizing commits for sharingdevelopment
Read-only Linear issue access via the Linear GraphQL API.
data-ai
## <!-- Purpose: Teach agents fast day-to-day memory browse/search/read/sync workflows in pi-context-repo. --> name: searching-memory description: > Search, browse, and inspect memory quickly in pi-context-repo. Use when asked to find prior notes, inspect memory files, locate preferences, or sync recent memory updates. Trigger phrases: "search memory", "list memory files", "find in memory", "read memory file", "memory status", "sync memory". --- # Searching Memory Use this workflow for fast
development
Comprehensive guide for initializing or reorganizing agent memory into a deeply hierarchical file structure. Use when running /init, when user asks to set up memory, or when memory needs a major reorganization. Trigger phrases: "initialize memory", "set up memory", "populate memory", "build my memory", "memory init".
data-ai
Decomposes and reorganizes agent memory files into focused, single-purpose components. Use when memory has large multi-topic blocks, redundancy, or poor organization. Trigger phrases: "defrag memory", "reorganize memory", "clean up memory files", "split memory blocks".