Detect and reject individual no-effect changes after cherry-pick#13665
Detect and reject individual no-effect changes after cherry-pick#13665
Conversation
134fd40 to
6231351
Compare
There was a problem hiding this comment.
Pull request overview
This PR improves commit creation when applying worktree changes onto a different target tree (cherry-pick/merge-trees step), by detecting and rejecting individual change requests that become no-ops after the cherry-pick—rather than only rejecting when the overall resulting tree is unchanged.
Changes:
- Add post–cherry-pick per-path checks to flag individual
DiffSpecs asNoEffectiveChangeswhen the target and result tree entries match. - Add a new fixture with two stacks where one stack introduces an extra file.
- Add a regression test ensuring a deletion of a file that only exists in another stack is rejected while other valid changes still create a commit.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| crates/but-core/src/tree/mod.rs | Adds per-change post–cherry-pick no-op detection and an early-exit when all changes are rejected (without cherry-pick conflicts). |
| crates/but-workspace/tests/workspace/commit_engine/new_commit.rs | Adds regression test for per-change rejection of a no-op deletion after cherry-pick. |
| crates/but-workspace/tests/fixtures/scenario/two-stacks-with-extra-file.sh | Adds fixture scenario with two stacks where only one stack contains an extra file. |
Problem:
When committing a mix of changes to a branch, some changes could be
silently dropped — the commit succeeded, but the caller received no
rejection feedback for the omitted changes.
Example: deleting a file that only exists in another stack's workspace
tree. The deletion has no effect on this branch's tree, but was not
reported as rejected.
Root cause:
The post-cherry-pick check only compared entire trees:
if tree_with_changes == target_tree { reject all }
When at least one change was effective the trees differed, so
individual no-ops slipped through undetected.
Fix:
After the cherry-pick, check each change individually via an
`entries_match` helper that compares both mode and object_id at a
given path between the target and result trees:
for each change:
if entries_match(target, result, change.path)
&& entries_match(target, result, change.previous_path):
reject as NoEffectiveChanges
Comparing mode (not just object_id) catches mode-only changes like
executable bit toggles. Checking previous_path catches renames where
only the source removal was effective.
Test:
A new `two-stacks-with-extra-file` fixture verifies that committing a
file deletion from another stack alongside a valid modification:
- rejects the deletion as `NoEffectiveChanges`
- still produces a commit for the modification
6231351 to
10de214
Compare
Byron
left a comment
There was a problem hiding this comment.
Thanks a lot!
I took a close look and think this is sound, despite this only being an armchair review.
Please see my one comment that might warrant a change, but it's more of a nit.
As a general note, these lower-level internal functions tend to not be well documented on as a function (along their parameters), and I think it's always worth fixing this while one is at it.
These days, I treat the documentation as a big part of specifying intent, with the code being replaceable.
| // After the cherry-pick, some changes may have had no effect on the target tree | ||
| // (e.g. deleting a file that only exists in another stack's tree, not in this | ||
| // branch). Check each change individually by comparing its path entry between | ||
| // the target tree and the result tree — if they match, the change was a no-op. | ||
| // We compare both object_id and mode to catch mode-only changes (e.g. executable bit). | ||
| // For renames, we also check that the previous_path was actually removed. | ||
| if needs_cherry_pick { |
There was a problem hiding this comment.
I think it makes sense to put this into the previous if needs_cherry_pick branch, instead of opening a new one.
Problem
When committing a mix of changes to a branch, some changes could be silently dropped — the commit succeeded, but the caller received no rejection feedback for the omitted changes.
Example: deleting a file that only exists in another stack's workspace tree. The deletion has no effect on this branch's tree, but was not reported as rejected.
Root cause
The post-cherry-pick check only compared entire trees:
When at least one change was effective the trees differed, so individual no-ops slipped through undetected.
Fix
After the cherry-pick, check each change individually via an
entries_matchhelper that compares both mode and object_id at a given path between the target and result trees:Comparing mode (not just object_id) catches mode-only changes like executable bit toggles. Checking
previous_pathcatches renames where only the source removal was effective.Test
A new
two-stacks-with-extra-filefixture verifies that committing a file deletion from another stack alongside a valid modification:NoEffectiveChanges