Skip to content

Commit 5a08fee

Browse files
fix(git): tolerate short worktree commit ids (#255)
Co-authored-by: Andrew Briscoe <awb@presempathy.com>
1 parent 8f935d1 commit 5a08fee

1 file changed

Lines changed: 65 additions & 2 deletions

File tree

src/git/worktree.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ use std::path::{Path, PathBuf};
3333
use std::sync::Arc;
3434
use uuid::Uuid;
3535

36+
fn short_commit_id(commit: &str) -> &str {
37+
commit.get(0..8).unwrap_or(commit)
38+
}
39+
3640
/// Represents a worktree for a VersionedKvStore
3741
#[derive(Clone)]
3842
pub struct WorktreeInfo {
@@ -633,7 +637,7 @@ impl WorktreeManager {
633637

634638
Ok(format!(
635639
"Fast-forward merge completed (Git-level fallback). Main branch updated to {}",
636-
&source_commit[0..8]
640+
short_commit_id(&source_commit)
637641
))
638642
} else {
639643
Err(non_fast_forward_error(source_branch, "main"))
@@ -812,7 +816,7 @@ impl WorktreeManager {
812816
"Fast-forward merged {} into {} (Git-level fallback). Target branch updated to {}",
813817
source_branch,
814818
target_branch,
815-
&source_commit[0..8]
819+
short_commit_id(&source_commit)
816820
))
817821
} else {
818822
Err(non_fast_forward_error(source_branch, target_branch))
@@ -1432,6 +1436,65 @@ mod tests {
14321436
println!(" • Data integrity verification after merge");
14331437
}
14341438

1439+
#[test]
1440+
fn test_merge_to_main_rejects_short_commit_ids_without_panicking() {
1441+
let temp_dir = TempDir::new().unwrap();
1442+
let repo_path = temp_dir.path();
1443+
let _cwd = CwdGuard::set(repo_path);
1444+
1445+
init_test_git_repo(repo_path);
1446+
1447+
let mut manager = WorktreeManager::new(repo_path).unwrap();
1448+
let worktree_path = temp_dir.path().join("short_commit_workspace");
1449+
let feature_info = manager
1450+
.add_worktree(&worktree_path, "short-commit-branch", true)
1451+
.unwrap();
1452+
1453+
let refs_dir = repo_path.join(".git").join("refs").join("heads");
1454+
std::fs::write(refs_dir.join("main"), "a").unwrap();
1455+
std::fs::write(refs_dir.join("short-commit-branch"), "bc").unwrap();
1456+
1457+
let err = manager
1458+
.merge_to_main(&feature_info.id, "Merge short commit")
1459+
.expect_err("short commit IDs must be rejected");
1460+
assert!(err.to_string().contains("Invalid main commit"));
1461+
}
1462+
1463+
#[test]
1464+
fn test_merge_branch_rejects_short_commit_ids_without_panicking() {
1465+
let temp_dir = TempDir::new().unwrap();
1466+
let repo_path = temp_dir.path();
1467+
let _cwd = CwdGuard::set(repo_path);
1468+
1469+
init_test_git_repo(repo_path);
1470+
1471+
let mut manager = WorktreeManager::new(repo_path).unwrap();
1472+
let source_path = temp_dir.path().join("short_source_workspace");
1473+
let source_info = manager
1474+
.add_worktree(&source_path, "short-source", true)
1475+
.unwrap();
1476+
let target_path = temp_dir.path().join("short_target_workspace");
1477+
manager
1478+
.add_worktree(&target_path, "short-target", true)
1479+
.unwrap();
1480+
1481+
let refs_dir = repo_path.join(".git").join("refs").join("heads");
1482+
std::fs::write(refs_dir.join("short-source"), "xy").unwrap();
1483+
std::fs::write(refs_dir.join("short-target"), "z").unwrap();
1484+
1485+
let err = manager
1486+
.merge_branch(&source_info.id, "short-target", "Merge short branch")
1487+
.expect_err("short commit IDs must be rejected");
1488+
assert!(err.to_string().contains("Invalid main commit"));
1489+
}
1490+
1491+
#[test]
1492+
fn test_short_commit_id_handles_short_and_full_ids() {
1493+
assert_eq!(short_commit_id("a"), "a");
1494+
assert_eq!(short_commit_id("12345678"), "12345678");
1495+
assert_eq!(short_commit_id("1234567890"), "12345678");
1496+
}
1497+
14351498
#[test]
14361499
fn test_multi_agent_versioned_merge_integration() {
14371500
use crate::diff::{AgentPriorityResolver, SemanticMergeResolver, TimestampResolver};

0 commit comments

Comments
 (0)