@@ -33,6 +33,10 @@ use std::path::{Path, PathBuf};
3333use std:: sync:: Arc ;
3434use 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 ) ]
3842pub 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