Move conflicted commit detection from headers to commit message markers#13247
Move conflicted commit detection from headers to commit message markers#13247
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
There was a problem hiding this comment.
Pull request overview
This PR changes GitButler’s “conflicted commit” detection from relying on a custom commit header (gitbutler-conflicted, which can be lost during external rebases) to commit-message markers ([conflict] prefix + GitButler-Conflict: true footer), while keeping legacy header fallback for existing commits.
Changes:
- Add/strip/check conflict markers in commit messages and update conflict detection to prefer message markers with legacy-header fallback.
- Update rebase/cherry-pick/edit-mode flows to add markers when creating conflicted commits and strip markers when conflicts are cleared.
- Adjust UI/workspace projections and snapshots/tests to hide markers in displayed messages while preserving
has_conflicts.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/gitbutler-edit-mode/src/lib.rs | Strips conflict markers when saving edited commits back to workspace. |
| crates/gitbutler-commit/src/commit_ext.rs | Updates is_conflicted() to check message markers first, then legacy headers. |
| crates/gitbutler-branch-actions/tests/branch-actions/reorder.rs | Normalizes commit-message comparisons by stripping markers in tests. |
| crates/but/tests/but/command/rub.rs | Updates snapshot due to changed CLI IDs in output. |
| crates/but-workspace/tests/workspace/commit/squash_commits.rs | Updates snapshots to reflect [conflict] prefix on conflicted commits. |
| crates/but-workspace/src/ref_info.rs | Strips markers before exposing commit messages to UI models when conflicted. |
| crates/but-workspace/src/branch_details.rs | Strips markers from local commit messages while keeping has_conflicts. |
| crates/but-rebase/tests/rebase/main.rs | Updates snapshots for new message-marker format and removal of CONFLICT-README.txt. |
| crates/but-rebase/tests/rebase/graph_rebase/conflictable_restriction.rs | Updates snapshots for new message-marker format. |
| crates/but-rebase/tests/rebase/graph_rebase/cherry_pick.rs | Updates snapshots for new message-marker format and tree layout. |
| crates/but-rebase/src/lib.rs | Preserves markers on reword/squash operations when commit remains conflicted. |
| crates/but-rebase/src/graph_rebase/cherry_pick.rs | Adds markers on conflicted commit creation; strips markers/legacy header when unconflicting. |
| crates/but-rebase/src/cherry_pick.rs | Same as graph_rebase path; removes legacy conflicted-count writing helpers. |
| crates/but-core/src/commit.rs | Implements marker constants + add/strip/detect helpers; updates Commit::is_conflicted(). |
71220ae to
102930a
Compare
|
@krlvi this is what we spoke about earlier, let me know what you think? |
102930a to
fce7789
Compare
fce7789 to
e23f921
Compare
e23f921 to
303e711
Compare
303e711 to
91e3de1
Compare
|
I like it! Just a question regarding the format:
|
559abb4 to
486271d
Compare
|
Cool cool, I'm afk for most of the day but I'll pick this back up sometime soon. |
53c95aa to
632c2c1
Compare
Absolutely! Thank you. |
d116a18 to
d081a27
Compare
Tasks
|
d081a27 to
c7477ed
Compare
c7477ed to
d081a27
Compare
The `gitbutler-conflicted` extra header is stripped when commits are
rebased outside of GitButler, silently losing conflict state. Replace
it with commit message markers that survive rebasing and are visible
to developers in `git log`:
- A `[conflict] ` prefix on the subject line for fast visual
detection
- A `GitButler-Conflict:` multi-line git trailer whose value
explains the conflict tree layout
Using a standard git trailer means the description is embedded as the
trailer value with indented continuation lines, so
`git interpret-trailers` and other standard tools can parse and
manipulate it. The resulting commit message looks like:
[conflict] <original subject>
<original body>
<existing trailers, if any>
GitButler-Conflict: This is a GitButler-managed conflicted
commit. Files are auto-resolved using the "ours" side.
The commit tree contains additional directories:
.conflict-side-0 — our tree
.conflict-side-1 — their tree
...
To manually resolve, check out this commit, remove the
directories listed above, resolve the conflicts, and
amend the commit.
The trailer is appended after any existing trailers in the message,
preserving Change-Id, Signed-off-by, and similar lines. When stripping
conflict markers (on resolution, display, or commit matching), the
prefix, trailer, and all continuation lines are removed to restore the
original message.
New commits only write message markers (no header). Reading checks the
message first, then falls back to the header for backward compatibility
with existing conflicted commits.
The CONFLICT-README.txt tree blob is removed since the trailer now
serves the same explanatory purpose with better visibility.
The conflicted file count (`Option<u64>`) stored in the old header was
only ever used as a boolean, so the new approach uses a simple presence
check with no count.
d081a27 to
4bc0630
Compare
Byron
left a comment
There was a problem hiding this comment.
I am force-pushing based on the most recent changes that were , erm, force-pushed on top of mine.
The changes in @mtsgrd commit are only the whitespace that was added to Cargo.toml for no reason. Maybe it's a formatter thing.
My refactors are on top and I will merge (if I can) in fear of another force-push 😅.
@Caleb-T-Owens To my mind, the changes to but-rebase are Ok but you might want to take another look nonetheless. It would also be good to find a way to tackle the 'persist synthetic change-ids' story that is still lingering.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4bc063068d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
4bc0630 to
3c42d96
Compare
|
Hitting auto-merge as I spent some time with it and think it's good. It's notable that for GUI purposes, we strip the |
3c42d96 to
9514792
Compare
9514792 to
6c8301c
Compare
Co-authored-by: Sebastian Thiel <sebastian.thiel@icloud.com>
6c8301c to
79ac145
Compare
Summary
The
gitbutler-conflictedextra header is stripped when commits are rebased outside of GitButler, silently losing conflict state. This replaces it with commit message markers that survive rebasing and are visible to developers ingit log:[conflict]prefix on the subject line for fast visual detectionGitButler-Conflict:multi-line git trailer whose value explains the conflict tree layoutUsing a standard git trailer means the description is embedded as the trailer value with indented continuation lines, so
git interpret-trailersand other standard tools can parse and manipulate it. The resulting commit message looks like:The trailer is appended after any existing trailers in the message, preserving
Change-Id,Signed-off-by, and similar lines. When stripping conflict markers (on resolution, display, or commit matching), the prefix, trailer, and all continuation lines are removed to restore the original message.New commits only write message markers (no header). Reading checks the message first, then falls back to the header for backward compatibility with existing conflicted commits.
The
CONFLICT-README.txttree blob is removed since the trailer now serves the same explanatory purpose with better visibility.The conflicted file count (
Option<u64>) stored in the old header was only ever used as a boolean, so the new approach uses a simple presence check with no count.Test plan
cargo clippy --workspace --all-targets -- -D warningscleancargo fmt --allcleanChange-Id,Signed-off-by) preserved in correct order🤖 Generated with Claude Code