jj-history-investigation

Jujutsu (jj) History Investigation and Manipulation

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "jj-history-investigation" with this command: npx skills add edmundmiller/dotfiles/edmundmiller-dotfiles-jj-history-investigation

Jujutsu (jj) History Investigation and Manipulation

Overview

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:

  • Investigating commit history: Finding when changes were made and why

  • Using jj annotate: Tracking down who/when specific lines were added

  • Splitting commits: Breaking large commits into focused, reviewable pieces

  • Handling immutability: Overriding protections when rewriting shared history

  • Cleaning up redundancy: Removing duplicate or empty commits

  • Resolving conflicts: Fixing merge conflicts after rebases

Core Commands

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

Investigation Techniques

  1. Finding When a Change Was Made

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}

  1. Understanding Commit Lineage

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'

  1. Extracting Specific Commit Information

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

Splitting Commits

When to Split

Split a commit when it contains multiple logical changes that should be reviewed separately:

  • ✅ Good candidates: "Add frontend + backend + tests + docs"

  • ✅ Mixed concerns: Copyright page + acknowledgments + CV in one commit

  • ❌ Already focused: "Fix typo in README"

Basic Split Workflow

  1. Check if commit is immutable:

jj edit abc123

If error: "Commit abc123 is immutable"

Then use --ignore-immutable flag

  1. Edit the commit:

jj edit abc123 --ignore-immutable

Working copy now at abc123

File system shows state at that commit

  1. 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)

  1. Split incrementally:

After first split, you're at "remaining changes"

Split again to extract more pieces

jj split --ignore-immutable src/chapters/appendix.tex

  1. Update commit messages:

Update the commit you just split

jj describe @- -m "feat(frontmatter): Enable copyright page"

Continue until all pieces are separated

Advanced Split Techniques

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.

Handling Immutability

Why Commits Become Immutable

Commits are immutable when:

  • They have descendants (other commits built on top)

  • They're in shared history (pushed to remote)

  • They're marked explicitly in config

Overriding Immutability

When it's safe:

  • ✅ Local-only history (not pushed)

  • ✅ You own all descendant commits

  • ✅ No collaborators affected

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)

Cleaning Up History

Identifying Redundant Commits

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

Abandoning Commits

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

Cleaning Up After Split

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

Resolving Conflicts

After Rebase/Split

  1. Identify conflicts:

jj status

Output:

Warning: There are unresolved conflicts at these paths:

TODO.org 2-sided conflict

  1. Create commit to resolve:

Create new commit on top of conflict

jj new conflicted-commit-id

  1. 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

  1. 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

  1. Squash resolution:

Merge resolution into parent

jj squash

This resolves the conflict in the parent commit

Verifying History

After Major Changes

  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

  1. 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

  1. Check for conflicts:

List all commits with conflicts

jj log -r 'all()' | grep conflict

Should be empty after cleanup

  1. Verify bookmarks:

Check where main/feature branches point

jj log -r 'bookmarks()'

Ensure they're at correct positions

History Health Checklist

After split/rebase/cleanup:

  • No conflict markers in jj status

  • No (conflict) labels in jj log

  • No empty (empty) commits (unless intentional)

  • Commit messages are clear and descriptive

  • Each commit has focused changes

  • Bookmarks (main, feature, etc.) point correctly

  • TODO.org or similar files are consistent

Real-World Example

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.

Tips and Tricks

Speed Up Investigation

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)'

Avoid Common Pitfalls

  • Don't split without investigation: Always jj show first to understand what's in a commit

  • Save state before split: cp important-file /tmp/ before major history rewrites

  • Split incrementally: Don't try to split everything at once; do one logical piece at a time

  • Update TODO immediately: Mark TODO items in the same commit that does the work

  • Verify after each step: Check jj log frequently during split/rebase operations

When NOT to Split

  • Commit is already pushed and shared with others

  • Changes are truly atomic (e.g., "Fix typo + its test")

  • Split would make history harder to understand

  • Commit is ancient and rarely viewed

References

  • jj Documentation

  • jj Revset Language

  • jj Conflict Resolution

Related Skills

  • jj:jj-workflow

  • Basic jj operations and workflow

  • jj:commit-messages

  • Writing good commit messages

  • jj:commit-curation

  • Organizing commits for sharing

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

tmux-status-debug

No summary provided by upstream source.

Repository SourceNeeds Review
General

agenix-secrets

No summary provided by upstream source.

Repository SourceNeeds Review
General

using-jj-workspaces

No summary provided by upstream source.

Repository SourceNeeds Review
General

nix-rebuild

No summary provided by upstream source.

Repository SourceNeeds Review