feat: auto-update PR title/body for single-revision PRs during sync - #73
Draft
MingweiSamuel wants to merge 2 commits into
Draft
feat: auto-update PR title/body for single-revision PRs during sync#73MingweiSamuel wants to merge 2 commits into
MingweiSamuel wants to merge 2 commits into
Conversation
Implements GitHub issue #72: when a PR has exactly one revision (commit) associated with it, the `sync` command now automatically updates the PR's title and body on GitHub to match the commit's description. Changes: - src/gh.rs: Added `body` field to `GhPr` struct (with `#[serde(default)]` for backward compat with existing fixtures). Added `body` to the GraphQL query (`PR_NODE_FIELDS`). Added `edit_title_body()` function that calls `gh pr edit --title --body`. - src/pr_dag.rs: Added `SyncAction::UpdateDescription` variant with `new_title` and `new_body` fields. Added `title_body_from_description()` helper that derives title (first line) and body (remaining lines, excluding PR: and Co-authored-by: trailers) from a commit description. Added step 6 in `plan_sync` that checks single-commit open PRs and generates an `UpdateDescription` action when title/body don't match. Refactored `plan_create` to reuse `title_body_from_description()` instead of duplicating the logic. - src/tests.rs: Added three dedicated tests: - `sync_updates_description_single_commit`: verifies update is generated - `sync_no_update_description_multi_commit`: verifies no update for multi-commit PRs - `sync_no_update_description_already_matches`: verifies no update when already in sync Updated `gh_pr` helper to include `body` field. - Updated existing snapshots that now correctly include description update actions for single-commit PRs whose title differs from the commit description. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #73
…one actions Reworks the auto-update-description feature to reduce code complexity. Instead of fetching `body` from GitHub's GraphQL API and comparing it to decide when to update, we now blindly update the title/body for single-revision open PRs whenever they are being pushed. Key changes: - Removed `body` field from `GhPr` struct and GraphQL query (saves ~60KB payload on large repos like hydro-project/hydro) - Removed standalone `SyncAction::UpdateDescription` variant - Added `description_updates` field to `SyncAction::Push` — description updates are now bundled with the push action - During push execution, after pushing bookmarks, each single-revision PR gets its title/body updated via `gh pr edit` - Kept `title_body_from_description()` helper (shared with `plan_create`) - Updated all tests and snapshots to reflect the new behavior Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #73
MingweiSamuel
force-pushed
the
update-prs
branch
from
July 30, 2026 20:25
ed852a9 to
0e74a2b
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds support to sync for updating GitHub PR descriptions (title/body) based on the commit description when a PR corresponds to a single revision, and updates tests/snapshots accordingly.
Changes:
- Extend sync planning/execution so
Pushactions can also carry PR description (title/body) updates for single-revision open PRs. - Add
title_body_from_description()helper and reuse it for default PR title/body derivation inplan_create. - Add dedicated sync tests and update/add snapshot fixtures to reflect the new planned actions.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pr_dag.rs | Plans and executes PR title/body updates alongside pushes; adds commit-description parsing helper. |
| src/gh.rs | Adds edit_title_body() wrapper around gh pr edit --title/--body. |
| src/tests.rs | Adds tests covering single-commit vs multi-commit behavior and non-push behavior. |
| src/snapshots/jj_pr__tests__sync@stageleft-71.json.gz.snap | Snapshot metadata update (assertion line) and sync output adjustments. |
| src/snapshots/jj_pr__tests__sync@remade-pr-old-branch-deleted.json.gz.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__sync@remade-pr-new-pr-created.json.gz.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__sync@remade-pr-both-branches-exist.json.gz.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__sync@jj-pr.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@jj-pr-stale-trunk-1778002925.json.gz.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__sync@jj-pr-dump-1065390.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@jj-pr-conflict-1778014006.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@jj-pr-ambiguous.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@hydro-prs-approved-1778006800.json.gz.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__sync@hydro-merged-many.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@hydro-before.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@hydro-after.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@hydro-4.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@hydro-3.json.gz.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__sync@cargo-smart-release-73.json.gz.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__sync_updates_description_single_commit_on_push.snap | New snapshot for single-commit PR push including a planned title update. |
| src/snapshots/jj_pr__tests__sync_no_description_update_multi_commit_on_push.snap | New snapshot for multi-commit PR push with no planned description update. |
| src/snapshots/jj_pr__tests__sync_no_description_update_when_not_pushing.snap | New snapshot for “no push” case with no planned description update. |
| src/snapshots/jj_pr__tests__stacked_prs_sync.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__nothing_to_sync.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__merged_parent_sync.snap | Snapshot metadata update (assertion line) and sync output includes title update lines. |
| src/snapshots/jj_pr__tests__foreign_pr_collision_sync.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__create_intermediate_pr_sync_after.snap | Snapshot assertion line update and sync output includes title update line. |
| src/snapshots/jj_pr__tests__closed_pr_base_mismatch_sync.snap | Snapshot metadata update (assertion line). |
| src/snapshots/jj_pr__tests__base_mismatch_sync.snap | Snapshot metadata update (assertion line) and sync output includes title update line. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1344
to
+1348
| // For single-revision open PRs, also update the PR title/body. | ||
| if gh_pr.state == gh::PrState::Open { | ||
| let node_commit_count = jj_entries | ||
| .iter() | ||
| .filter(|e| state.commit_node.get(&*e.commit.commit_id) == Some(&nk)) |
Comment on lines
+1192
to
+1206
| fn title_body_from_description(description: &str) -> (String, String) { | ||
| let title = description.lines().next().unwrap_or("untitled").to_owned(); | ||
| let body = description | ||
| .lines() | ||
| .skip(1) | ||
| .filter(|line| { | ||
| let l = line.trim_start().to_ascii_lowercase(); | ||
| !l.starts_with("co-authored-by:") && !l.starts_with("pr:") | ||
| }) | ||
| .collect::<Vec<_>>() | ||
| .join("\n") | ||
| .trim() | ||
| .to_owned(); | ||
| (title, body) | ||
| } |
Comment on lines
1167
to
+1171
| let details: Vec<_> = bookmarks.iter().map(|(pr, bm)| format!("{pr} ({bm})")).collect(); | ||
| write!(f, "push: {}", details.join(", ")) | ||
| write!(f, "push: {}", details.join(", "))?; | ||
| for (pr, title, _body) in description_updates { | ||
| write!(f, "\n update {pr} title -> \"{title}\"")?; | ||
| } |
Comment on lines
+1472
to
+1475
| for (pr, title, body) in description_updates { | ||
| eprintln!("Updating {} title -> \"{}\"", crate::style::pr_num(*pr, None), title,); | ||
| gh::edit_title_body(pr.get(), title, body)?; | ||
| } |
Comment on lines
+2
to
+4
| source: src/tests.rs | ||
| assertion_line: 1085 | ||
| expression: plan_sync(&f) |
Comment on lines
+2
to
+4
| source: src/tests.rs | ||
| assertion_line: 1104 | ||
| expression: plan_sync(&f) |
Comment on lines
+2
to
+4
| source: src/tests.rs | ||
| assertion_line: 1121 | ||
| expression: plan_sync(&f) |
Comment on lines
+1069
to
+1073
| #[test] | ||
| fn sync_updates_description_single_commit_on_push() { | ||
| // Single-commit PR that needs pushing — description update should be included with push. | ||
| let f = fixture( | ||
| vec![ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements GitHub issue #72: when a PR has exactly one revision (commit)
associated with it, the
synccommand now automatically updates the PR'stitle and body on GitHub to match the commit's description.
Changes:
src/gh.rs: Added
bodyfield toGhPrstruct (with#[serde(default)]for backward compat with existing fixtures). Added
bodyto the GraphQLquery (
PR_NODE_FIELDS). Addededit_title_body()function that callsgh pr edit --title --body.src/pr_dag.rs: Added
SyncAction::UpdateDescriptionvariant withnew_titleandnew_bodyfields. Addedtitle_body_from_description()helper that derives title (first line) and body (remaining lines, excluding
in
plan_syncthat checks single-commit open PRs and generates anUpdateDescriptionaction when title/body don't match. Refactoredplan_createto reusetitle_body_from_description()instead ofduplicating the logic.
src/tests.rs: Added three dedicated tests:
sync_updates_description_single_commit: verifies update is generatedsync_no_update_description_multi_commit: verifies no update for multi-commit PRssync_no_update_description_already_matches: verifies no update when already in syncUpdated
gh_prhelper to includebodyfield.Updated existing snapshots that now correctly include description update
actions for single-commit PRs whose title differs from the commit description.