-
Notifications
You must be signed in to change notification settings - Fork 63
feat(branch): implement safe delete with -d flag (#168) #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(branch): implement safe delete with -d flag (#168) #170
Conversation
Add branch -d option to safely delete branches by checking if they are fully merged into HEAD before deletion, preventing accidental loss of unmerged work. Closes web3infra-foundation#168 Signed-off-by: yuanzhiliu <[email protected]> Signed-off-by: Nook001 <[email protected]>
abde9f1 to
3a3dc93
Compare
Signed-off-by: Nook001 <[email protected]>
|
@codex review |
|
Claude finished @genedna's task in 2m 35s —— View job Code Review Complete ✅I've reviewed the implementation of ✅ Strengths
🔍 Issues Found1. Critical: Incorrect Merge Detection Algorithm
|
| Category | Rating | Notes |
|---|---|---|
| Correctness | Logic is overly complex but should work (issue #1) | |
| Performance | Unnecessary double traversal (issue #2) | |
| Code Quality | ✅ | Clean and readable |
| Test Coverage | ✅ | Good basic coverage, but missing edge cases (issue #4) |
| Documentation | ✅ | README updated, but missing function docs (issue #5) |
| Security | ✅ | No security concerns |
📋 Recommended Actions
Must Fix (before merge):
- Simplify the merge detection logic (Issue move mercury and libra #1) - while the current logic should work, it's overly complex
- Remove unnecessary
branch_reachabletraversal (Issue fix compilation #2) - performance optimization
Should Fix (before merge):
3. Consider using consistent error handling (Issue #3)
4. Add doc comment to delete_branch_safe() (Issue #5)
Nice to Have (can be follow-up):
5. Add more edge case tests (Issue #4)
6. Add success message to delete_branch() for consistency (Issue #6)
Great work overall! The implementation logic appears sound, but the merge detection can be significantly simplified and optimized. I've added inline comments on the specific code sections that need attention. Once issues #1 and #2 are addressed, this will be a solid addition to Libra. 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b049af729
ℹ️ 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".
src/command/branch.rs
Outdated
| #[clap(short = 'D', long, group = "sub")] | ||
| pub delete: Option<String>, | ||
|
|
||
| /// safe delete branch | ||
| #[clap(short = 'd', long, group = "sub")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align long flag so
--delete is safe delete
The new safe-delete option is declared with #[clap(short = 'd', long)], which makes clap derive --delete-safe from the field name, while the force-delete option above still owns --delete. As a result, libra branch --delete <branch> continues to force-delete unmerged branches even though the README and Git semantics expect --delete to be safe, which can cause accidental data loss when users follow the docs. Consider moving --delete to the safe path and renaming the force delete long flag (e.g., --delete-force) to preserve expected behavior.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for branch -d (safe delete) to refuse deletion of unmerged branches, aligning Libra’s behavior with Git.
Changes:
- Extend
BranchArgsand thebranchcommand dispatcher to support a newdelete_safe(-d) mode that performs reachability checks before deleting a branch. - Implement
delete_branch_safeusinglog::get_reachable_commitsto ensure the target branch is fully merged into the current HEAD, including handling detached HEAD and current-branch cases. - Update branch-related integration tests (including a new
test_branch_delete_safe) and README compatibility documentation to cover the new behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/command/branch.rs |
Adds delete_safe flag handling and delete_branch_safe implementation using commit reachability to enforce safe branch deletion semantics. |
tests/command/branch_test.rs |
Updates existing BranchArgs constructions for the new field and adds test_branch_delete_safe to validate both unmerged and merged deletion scenarios. |
tests/command/reset_test.rs |
Updates helper BranchArgs initializations to include the new delete_safe field, keeping tests compiling and behavior unchanged. |
README.md |
Marks branch -d as implemented and updates the compatibility matrix to reflect safe delete support (with a minor mismatch in the documented long flag name). |
- Fix merge detection logic: only check branch HEAD commit instead of traversing all commits - Remove unnecessary get_reachable_commits() call for branch - Add documentation for delete_branch_safe function - Fix long flag naming: --delete is now safe delete, --delete-force is force delete - Improve performance by avoiding unnecessary commit traversal Addresses code review comments from PR web3infra-foundation#170 Signed-off-by: Nook001 <[email protected]>
Store ObjectHash types directly in the HashSet instead of converting to String, eliminating unnecessary memory allocations during merge checks. This improves performance for repositories with large commit histories. - Replace .map(|c| c.id.to_string()) with .map(|c| c.id) - Compare branch.commit directly without .to_string() conversion - Reduces O(n) string allocations to zero for merge detection Signed-off-by: Nook001 <[email protected]>
|
@codex review |
|
Claude finished @genedna's task in 1m 45s —— View job Code Review Complete ✅I've conducted a comprehensive review of the SummaryThis PR successfully implements the safe delete functionality for branches ( 🔴 Critical Issues1. CLI Flag Naming Inconsistency Creates Dangerous UXLocation: The current implementation has a critical UX issue: /// force delete branch
#[clap(short = 'D', long = "delete-force", group = "sub")]
pub delete: Option<String>,
/// safe delete branch (checks if merged before deletion)
#[clap(short = 'd', long = "delete", group = "sub")]
pub delete_safe: Option<String>,Problem:
This is actually correct behavior now! The implementation properly maps:
This matches Git's behavior exactly. The Codex bot's suggestion to change this is incorrect and should be ignored. Verdict: ✅ This is correct as implemented. No changes needed. 2. Merge Detection Algorithm is Correct but Simpler Alternative ExistsLocation: The current implementation correctly checks if the branch commit is reachable from HEAD: let head_commit_ids: std::collections::HashSet<_> =
head_reachable.iter().map(|c| c.id).collect();
if !head_commit_ids.contains(&branch.commit) {
eprintln!("error: The branch '{}' is not fully merged.", branch_name);
// ...
}Analysis: This is correct. The algorithm:
This matches Git's behavior. While the previous bot review suggested this was overly complex, it's actually the right approach and has already been optimized in recent commits. Verdict: ✅ Algorithm is correct. The recent optimization using
|
| Category | Rating | Notes |
|---|---|---|
| Correctness | ✅ Excellent | Algorithm is correct and well-implemented |
| Performance | ✅ Good | Recent optimization using ObjectHash is smart |
| Code Quality | ✅ Good | Clean, readable, well-documented |
| Test Coverage | Basic scenarios covered, but missing edge cases | |
| CLI/UX | ✅ Excellent | Properly matches Git's behavior |
| Documentation | ✅ Good | Function docs and README updated |
| Security | ✅ Good | No security concerns |
🎯 Recommendations
Before Merge (Nice to Have):
- ✅ Consider adding success message to
delete_branch()for consistency (Issue Add .github and docs #4) - 📝 Add edge case tests for same-commit, detached HEAD, and multiple unmerged commits scenarios (Issue remove gemini #5)
Future Improvements (Follow-up PR):
3. Consider refactoring error handling from panic! to Result types for both delete functions (Issue #3)
🎉 Conclusion
This is a solid implementation that correctly implements Git's branch -d behavior. The merge detection algorithm is correct, the CLI flags match Git's expectations, and the code is clean and well-documented.
Previous bot reviews had some incorrect suggestions (particularly around the CLI flag naming and merge detection "complexity"). The current implementation is correct and the recent optimizations show good attention to performance.
The main improvement opportunity is in test coverage - adding more edge case tests would increase confidence. The success message inconsistency is minor but worth fixing for better UX alignment with Git.
Recommendation: Approve with minor suggestions for enhanced test coverage and consistency improvements.
Great work on this feature! The implementation shows solid understanding of Git's merge detection logic and attention to both correctness and performance. 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 65ba205e60
ℹ️ 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".
| // Get all commits reachable from HEAD | ||
| let head_reachable = crate::command::log::get_reachable_commits(head_commit.to_string()).await; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider upstream when checking safe delete
The safe-delete check builds reachability only from the current HEAD. If a branch has an upstream configured and was merged into that upstream while the user is on a different branch, this logic will still refuse deletion because the upstream history isn’t consulted. That produces false negatives for safe deletes in common workflows (e.g., delete a merged feature branch while staying on another branch), forcing users to switch branches or use -D. Consider consulting the branch’s upstream when present and falling back to HEAD only when no upstream is configured.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| #[clap(short = 'D', long = "delete-force", group = "sub")] | ||
| pub delete: Option<String>, | ||
|
|
||
| /// safe delete branch (checks if merged before deletion) | ||
| #[clap(short = 'd', long = "delete", group = "sub")] |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing the long flag for force delete from the default --delete to --delete-force while also reusing --delete for safe delete introduces a breaking CLI change that isn’t documented and may surprise existing users or scripts that rely on libra branch --delete for force deletion. Consider either preserving --delete as the long form for force delete (and using a different long name for safe delete, such as --delete-safe), or supporting both long forms for force delete via clap aliases so that existing usage keeps working.
| #[clap(short = 'D', long = "delete-force", group = "sub")] | |
| pub delete: Option<String>, | |
| /// safe delete branch (checks if merged before deletion) | |
| #[clap(short = 'd', long = "delete", group = "sub")] | |
| #[clap(short = 'D', long = "delete", visible_alias = "delete-force", group = "sub")] | |
| pub delete: Option<String>, | |
| /// safe delete branch (checks if merged before deletion) | |
| #[clap(short = 'd', long = "delete-safe", group = "sub")] |
Issue
Closes #168
Summary
实现
branch -d选项,用于安全删除已合并的分支。branch -d会在删除前检查分支是否已完全合并到 HEAD,防止意外丢失未合并的代码。Changes
BranchArgs中使用delete_safe字段对应-d参数delete_branch_safe()函数,包含完整的合并检查逻辑log::get_reachable_commits()遍历提交图,判断分支是否已合并test_branch_delete_safe,覆盖已合并和未合并两种场景Implementation Details
合并检查算法
// 1. 获取 HEAD 的所有可达提交
let head_reachable = get_reachable_commits(head_commit).await;
// 2. 获取待删除分支的所有可达提交
let branch_reachable = get_reachable_commits(branch.commit).await;
// 3. 检查分支的每个提交是否都在 HEAD 的历史中
for commit in branch_reachable {
if !head_commit_ids.contains(&commit.id) {
// 未合并,拒绝删除
return;
}
}
// 4. 所有检查通过,执行删除
与 Git 行为对照
branch -dbranch -dbranch -DTesting
运行所有 branch 相关测试:
cargo test --test command_test branch_test -- --test-threads=1
结果:
✅ 8/8 测试通过
✅ 新增测试 test_branch_delete_safe 验证了两个场景:
拒绝删除未合并分支
成功删除已合并分支