Skip to content

Feature: let update-patch target an intermediate patch in a stack (rebase later patches on top) #1335

Description

@sawachikaminoru

Summary

When a project carries a stack of multiple patches, update-patch can only regenerate the last one. There is no supported way to record a change into an intermediate patch. I'd like update-patch to accept a target patch, so a change that belongs to an earlier patch can be recorded there, with the later patches rebased on top to keep the stack consistent.

Background

Since 0.12.0 a project can carry a list of patches (a stack). Splitting local modifications into several concern-focused patches is genuinely useful: each patch stays small, readable, and individually upstreamable, and format-patch already leans into treating patches as coherent, contributable changesets.

A typical concern-separated stack for a vendored library might look like:

0001-rename-namespace.patch          # adapt symbols to our namespace
0002-swap-logging-backend.patch      # replace the logging backend; defines DEFAULT_LOG_LEVEL = "info"
0003-local-project-overlay.patch     # small project-specific tweaks

Problem

Say I now want to change DEFAULT_LOG_LEVEL from "info" to "debug". That constant is introduced by 0002, so 0002 is its correct home.

After making the edit in the working tree:

  • update-patch regenerates only the last patch, folding the change into 0003 as an override that re-changes a line 0002 already owns. 0003 now contradicts 0002 — a smell that defeats the point of a concern-separated stack.
  • The only correct alternative today is to hand-edit 0002.patch: fix the added line, and also fix the file's index <blob>..<blob> hash and any hunk offsets by hand. Fragile and easy to get subtly wrong, especially for larger edits.

Proposed behavior

Let update-patch take a target patch, e.g.:

dfetch update-patch <project> --patch <index|patch-name>
  • With no target it behaves exactly as today (regenerate the last patch), so this is fully backward compatible.
  • With a target K, it records the current working-tree change into patch K and rebases K+1..N on top, re-applying each. If a later patch genuinely depends on the changed lines the conflict is surfaced rather than silently corrupting context — the same honest signal git rebase gives.

Because the regeneration goes through git, the index blob hashes and hunk offsets of every affected patch file are recomputed automatically, so the whole stack stays consistent with no hand-editing.

For the example above, update-patch <project> --patch 0002-swap-logging-backend would update only 0002.patch; 0003 (which doesn't touch that line) rebases cleanly and stays unchanged.

Relation to #883

#883 (edit-patch) is the closest existing work, but it targets the patch (singular). In a stack its reverse/stage/re-apply flow leaves the baseline at "upstream + 0001 + 0002" and the worktree at "…+ 0003", so it still regenerates the last patch — it doesn't target an intermediate patch or rebase the later ones. The two could share machinery: an interactive edit-patch flow fits the "clean tree, take me to patch K to edit it" case, while the extension proposed here fits the "I already made the change; record it into patch K" case.

Constraints

Ideally this stays in-place on the vendored directory with no external state (no temp repo/worktree for dfetch to track and clean up), recoverable with dfetch update -f <project>. The reverse/stage/re-apply mechanism sketched in #883 seems extendable to a stack: reverse N..K, regenerate K from the working-tree change, then re-apply K+1..N in place — that last step being the rebase, where any conflict surfaces.

Alternatives considered

  • Hand-editing the intermediate .patch — works for one-liners but is fragile (manual blob hashes / offsets) and error-prone at scale.
  • Overriding in the last patch — keeps update-patch happy but produces contradictory, redundant patches and erodes the concern separation.
  • Restructuring so edits always land in the last patch — gives up the benefit of a concern-separated stack.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions