Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migration guide

This page covers breaking changes introduced in recent releases and how to adapt to them.

josh pull moved to josh changes pull

Applies when upgrading from r26.07.28 to r26.08.28.

The top-level josh pull command has moved under josh changes. The new command requires Git 2.41 or newer, always integrates with rebase-style restacking and autostash, and no longer accepts --rebase or --autostash.

How to migrate: Upgrade Git to 2.41 or newer, replace josh pull with josh changes pull, and remove the --rebase and --autostash flags from scripts.

Pattern-filter construction is fallible

Applies when upgrading from r26.07.28 to r26.08.28.

Invalid globs now fail when a pattern filter is constructed rather than when it is first applied. The Rust Filter::pattern API therefore returns an error result.

How to migrate: Update Rust callers to propagate or handle the result from Filter::pattern(...), for example with Filter::pattern(pattern)?. Validate user-supplied patterns at construction time instead of expecting application to report the error.

Malformed Git trees are preserved instead of normalized

Applies when upgrading from r26.07.28 to r26.08.28.

Filtering no longer sorts, deduplicates, or canonicalizes unchanged entries in fsck-invalid Git trees. Rebuilt filtered commits can consequently have different object IDs than earlier Josh versions when their source history contains malformed trees.

How to migrate: Run git fsck on source repositories that may contain malformed trees. If external systems pin filtered commit IDs from such histories, regenerate those references with the new Josh version or replace the malformed source trees with canonical commits.

josh publish renamed to josh changes publish

Applies when upgrading from r26.05.08 to r26.06.11.

The publish subcommand has moved under a new changes subcommand group, making room for further changes-related subcommands alongside publish.

How to migrate: Update any scripts or CI invocations that call josh publish to use josh changes publish instead. All arguments and flags are unchanged.

“stack” push mode removed; refs/split/... magic ref renamed to refs/publish/...

Applies when upgrading from r26.05.08 to r26.06.11.

The old “stack” push mode has been removed. The proxy’s publish magic ref has also been renamed from refs/split/for/<branch> to refs/publish/for/<branch> so that the proxy and the CLI use the same vocabulary.

How to migrate: If you were pushing to refs/split/for/<branch>, push to refs/publish/for/<branch> instead. There is no replacement for the “stack” push mode; use publish mode instead.

Applies when upgrading from r26.05.08 to r26.06.11.

The downstack algorithm has been rewritten to use path intersection rather than diff application. As part of that, the Requires: commit footer used to force ordering between commits has been replaced with Change-Series:. Assign two or more commits to a named series to keep them merged in order even when they touch disjoint files.

How to migrate: Replace any Requires: commit footers with Change-Series: <name>, giving the series a stable name shared by all commits that must be merged in order.

Applies when upgrading from r26.05.08 to r26.06.11.

Change:, Change-Id:, and Change-Series: lines are now only recognised when they appear in the trailing block of trailers at the end of the commit message. A body line that happens to start with one of those prefixes is no longer mis-parsed as a real trailer.

How to migrate: If a commit message body contains a line that begins with Change:, Change-Id:, or Change-Series: outside the trailing footer block and you intended it as a trailer, move it into the footer block at the very end of the message.

CRLF line endings in gpgsig headers preserved

Applies when upgrading from r24.10.04 to r26.04.19.

Josh previously accidentally normalized \r\n line endings to \n inside the gpgsig header of commit objects. This was a bug — git treats gpgsig values as opaque bytes, and the standard format uses \n throughout — and has been fixed. Josh now preserves whichever line endings are present in the original commit.

What changed: If your upstream history contains commits with \r\n in their gpgsig headers, the filtered commit hashes will differ from those produced by the old version.

How to restore the old behavior: Use the gpgsig="norm-lf" meta option if you need to reproduce a history that was created with the old normalization:

:~(gpgsig="norm-lf")[:/your/filter]

See the gpgsig option for details.

Trivial merges removed by default

Applies when upgrading from r24.10.04 to r26.04.19.

Josh previously kept all merge commits in the filtered history, even when the filtered tree of a merge commit was identical to its first parent’s tree (a “trivial merge”). Trivial merges are now removed by default during history simplification.

What changed: Filtered histories produced by the same filter may differ from those produced by older versions of Josh if the upstream history contains trivial merges. The internal cache version has been bumped, so all results will be recomputed on the first run after upgrading.

How to restore the old behavior: Wrap your filter with the history="keep-trivial-merges" meta option:

:~(history="keep-trivial-merges")[:/your/filter]

See Filter options for details.

:join filter removed

Applies when upgrading from r24.10.04 to r26.04.19.

The :join filter has been removed. It was a limited alternative to using --reverse/push for reconstructing upstream history from a filtered view.

How to migrate: Use josh-filter --reverse or josh push to write changes back to the upstream repository.

josh-ui (web UI) removed

Applies when upgrading from r24.10.04 to r26.04.19.

The /ui endpoint and the josh-ui component have been removed from the project.

How to migrate: There is no direct replacement. Remove any links or integrations that pointed to the /ui endpoint.

:rev filter syntax changed; :from and :concat removed

Applies when upgrading from r24.10.04 to r26.04.19.

The :rev filter now requires an explicit match operator before each SHA, and the zero-SHA shorthand for the default case has been replaced by _.

What changed:

The old syntax used a bare SHA for each entry:

:rev(sha0:filter0,sha1:filter1,0000000000000000000000000000000000000000:default_filter)

The new syntax requires an operator prefix and uses _ for the default case:

:rev(<=sha0:filter0,<=sha1:filter1,_:default_filter)

Available operators:

  • <=sha — matches the commit and all its ancestors (inclusive); equivalent to the old bare-SHA behavior
  • <sha — matches only strict ancestors (excludes the named commit itself)
  • ==sha — matches only the exact commit

How to migrate :rev filters:

  • Replace each bare sha:filter entry with <=sha:filter
  • Replace 0000000000000000000000000000000000000000:filter with _:filter

:from removed:

The :from(sha:filter) filter has been removed. It kept the original history up to and including sha unchanged and applied filter to all later commits.

Equivalent :rev expression:

:rev(<=sha:/,_:filter)

:concat removed:

The :concat(sha:filter) filter has been removed. It was the internal building block of :from and sometimes appeared in normalized filter output (e.g. from josh-filter -p). Filters of the form filter:concat(sha:filter) are equivalent to :from(sha:filter) and should be migrated to:

:rev(<=sha:/,_:filter)

Cache location changed

Applies when upgrading from r24.10.04 to r26.04.19.

The local sled cache is now stored at .git/josh/cache instead of .git/josh.

What changed: On the first run after upgrading, Josh will not find any existing cache entries and will recompute all filter results from scratch. This is a one-time cost.

No action required for most users - the cache will be rebuilt automatically. If you have scripts or tooling that reference .git/josh directly (e.g. to delete or back up the cache), update them to point to .git/josh/cache instead.