Skip to content

Apply a pull request as a checkout of its branch, not as a diff on the ticket branch #458

Closed

Description

@juanmaguitar

What problem does this address?

"Apply" on a linked pull request downloads the PR as a .diff and runs git apply on the ticket branch's working tree (src/git-write.cjs:285, no --index). To Git, those hunks are the contributor's own edits. The only record that a PR was applied is appliedPatch in the app's settings.json under userData (src/main.js:2035), outside the repository.

The next time the app parks (ticket switch, unlink, trunk update, rebase), parkCurrentWork rolls everything into the branch's single WIP commit (src/ticket-branches.js:204). From then on the PR's hunks and the contributor's own work are one commit, and nothing can tell them apart: not Git, not the app. That is why the Review & submit panel refuses with "PR #7701 is part of this checkout. The app cannot safely separate its author's changes from edits made afterward".

What a mentor sees in the terminal, on a site with PR #7701 applied and nothing else written:

$ git status
On branch ticket/62010
nothing to commit, working tree clean

$ git log --oneline trunk..HEAD
58219e2aee Work in progress (WordPress Contributor Toolkit)

A commit by the contributor, containing someone else's code, with no trace of where it came from. Meanwhile the app says "You have 1 unsubmitted change for ticket #62010" and "PR #7701 is applied", both read from a store Git cannot see. This is the concrete case behind #350's first invariant (the app never creates a state Git cannot understand), and the record-outside-the-repo is the same failure class as #348.

What is your proposed solution?

Make "Apply" do what a Git user would do: fetch the PR's head and check it out.

  • git fetch origin pull/<N>/head:pr/<N> and checkout pr/<N>. The ticket's own work is parked on ticket/<number> untouched, through the existing park.
  • "Revert this patch" becomes "switch back to ticket/<number>". No appliedPatch record: the checked-out branch is the record, and git status says On branch pr/7701.
  • A Trac .diff/.patch has no branch to fetch. Keep git apply for those, but commit the result on a patch/<ticket>-<attachment> branch, author set to the uploader, so the same model holds with a different source.
  • The UI for the newcomer does not change: still "Apply..." and "Revert". The branch names are what the mentor sees with their own tools.

What this gives:

  • The PR is tried as its author wrote it, against its own base, which closes A pull request can only be tried against today's trunk, never as its author wrote it #290.
  • No app-side record that can drift from the repository (trunk update, discard, a checkout touched outside the app).
  • git log and git status tell the truth, and the day the contributor opens the site with Git, their history reads as history.
  • The WIP invariant (one commit reparented on baseOid, src/ticket-branches.js:16) is not touched. Fetch, checkout and park are primitives that already exist in src/git-write.cjs and src/ticket-branches.js.

What it drops: trying "the PR plus my own edits" in one checkout. That is exactly the state that cannot be submitted today, and in practice a contributor tries someone else's PR to give feedback, not to merge it into their own work. If it is ever needed, it is a merge of pr/<N> into the ticket branch, with commits the app can separate, and that belongs after #350 ends the single-WIP rewrite.

Things to check on the way:

  • refs/pull/<N>/head is served by GitHub on origin for fork PRs; confirm the partial clone (--filter=blob:none) fetches it cleanly and what it costs on a slow connection.
  • The "Linked pull requests" list, the applied-layer banner (src/renderer/applied-layer.cjs) and appliedPatchSubmissionRefusal (src/main.js:1385) all read appliedPatch; each needs a branch-based replacement or removal.
  • A site on a legacy shallow clone (isLegacySite) cannot fetch the PR head; keep the refusal loud, as the other writes do.

Activity

  1. added
    enhancementNew feature or request
    area: patch-flowPatches, pull requests, Trac tickets
    real-world-gitCloser-to-real-Git experience: the interoperability contract (#350) and its pieces
    on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: patch-flowPatches, pull requests, Trac ticketsenhancementNew feature or requestreal-world-gitCloser-to-real-Git experience: the interoperability contract (#350) and its pieces

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions