1
+ use crate :: id:: CliId ;
1
2
use crate :: status:: assignment:: FileAssignment ;
3
+ use anyhow:: Result ;
2
4
use bstr:: { BString , ByteSlice } ;
3
- use but_api:: { commands:: diff, commands:: workspace, hex_hash:: HexHash } ;
5
+ use but_api:: { commands:: diff, commands:: virtual_branches , commands :: workspace, hex_hash:: HexHash } ;
4
6
use but_core:: ui:: TreeChange ;
5
7
use but_hunk_assignment:: HunkAssignment ;
6
8
use but_settings:: AppSettings ;
@@ -11,6 +13,127 @@ use std::collections::BTreeMap;
11
13
use std:: io:: { self , Write } ;
12
14
use std:: path:: Path ;
13
15
16
+ pub ( crate ) fn insert_blank_commit ( repo_path : & Path , _json : bool , target : & str ) -> Result < ( ) > {
17
+ let project = Project :: find_by_path ( repo_path) ?;
18
+ let mut ctx = CommandContext :: open ( & project, AppSettings :: load_from_default_path_creating ( ) ?) ?;
19
+
20
+ // Resolve the target ID
21
+ let cli_ids = CliId :: from_str ( & mut ctx, target) ?;
22
+
23
+ if cli_ids. is_empty ( ) {
24
+ anyhow:: bail!( "Target '{}' not found" , target) ;
25
+ }
26
+
27
+ if cli_ids. len ( ) > 1 {
28
+ anyhow:: bail!(
29
+ "Target '{}' is ambiguous. Found {} matches" ,
30
+ target,
31
+ cli_ids. len( )
32
+ ) ;
33
+ }
34
+
35
+ let cli_id = & cli_ids[ 0 ] ;
36
+
37
+ // Determine target commit ID and offset based on CLI ID type
38
+ let ( target_commit_id, offset, success_message) = match cli_id {
39
+ CliId :: Commit { oid } => {
40
+ // For commits, insert before (offset 0) and use the commit ID directly
41
+ (
42
+ * oid,
43
+ 0 ,
44
+ format ! (
45
+ "Created blank commit before commit {}" ,
46
+ & oid. to_string( ) [ ..7 ]
47
+ ) ,
48
+ )
49
+ }
50
+ CliId :: Branch { name } => {
51
+ // For branches, we need to find the branch and get its head commit
52
+ let head_commit_id = find_branch_head_commit ( project. id , name) ?;
53
+ (
54
+ head_commit_id,
55
+ -1 ,
56
+ format ! ( "Created blank commit at the top of stack '{name}'" ) ,
57
+ )
58
+ }
59
+ _ => {
60
+ anyhow:: bail!(
61
+ "Target must be a commit ID or branch name, not {}" ,
62
+ cli_id. kind( )
63
+ ) ;
64
+ }
65
+ } ;
66
+
67
+ // Find the stack containing the target commit and insert blank commit
68
+ let stack_id = find_stack_containing_commit ( project. id , target_commit_id) ?;
69
+ virtual_branches:: insert_blank_commit (
70
+ project. id ,
71
+ stack_id,
72
+ Some ( target_commit_id. to_string ( ) ) ,
73
+ offset,
74
+ ) ?;
75
+ println ! ( "{success_message}" ) ;
76
+ Ok ( ( ) )
77
+ }
78
+
79
+ fn find_branch_head_commit (
80
+ project_id : gitbutler_project:: ProjectId ,
81
+ branch_name : & str ,
82
+ ) -> Result < gix:: ObjectId > {
83
+ let stack_entries = workspace:: stacks ( project_id, None ) ?;
84
+
85
+ for stack_entry in & stack_entries {
86
+ if let Some ( stack_id) = stack_entry. id {
87
+ let stack_details = workspace:: stack_details ( project_id, Some ( stack_id) ) ?;
88
+
89
+ if let Some ( branch_details) = stack_details
90
+ . branch_details
91
+ . iter ( )
92
+ . find ( |b| b. name == branch_name)
93
+ {
94
+ // Get the head commit of this branch (prefer regular commits over upstream)
95
+ return if let Some ( commit) = branch_details. commits . first ( ) {
96
+ Ok ( commit. id )
97
+ } else if let Some ( commit) = branch_details. upstream_commits . first ( ) {
98
+ Ok ( commit. id )
99
+ } else {
100
+ anyhow:: bail!( "Branch '{}' has no commits" , branch_name) ;
101
+ } ;
102
+ }
103
+ }
104
+ }
105
+
106
+ anyhow:: bail!( "Branch '{}' not found in any stack" , branch_name) ;
107
+ }
108
+
109
+ fn find_stack_containing_commit (
110
+ project_id : gitbutler_project:: ProjectId ,
111
+ commit_id : gix:: ObjectId ,
112
+ ) -> Result < but_workspace:: StackId > {
113
+ let stack_entries = workspace:: stacks ( project_id, None ) ?;
114
+
115
+ for stack_entry in & stack_entries {
116
+ if let Some ( stack_id) = stack_entry. id {
117
+ let stack_details = workspace:: stack_details ( project_id, Some ( stack_id) ) ?;
118
+
119
+ // Check if this commit exists in any branch of this stack
120
+ for branch_details in & stack_details. branch_details {
121
+ // Check both regular commits and upstream commits
122
+ if branch_details. commits . iter ( ) . any ( |c| c. id == commit_id)
123
+ || branch_details
124
+ . upstream_commits
125
+ . iter ( )
126
+ . any ( |c| c. id == commit_id)
127
+ {
128
+ return Ok ( stack_id) ;
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ anyhow:: bail!( "Commit {} not found in any stack" , commit_id) ;
135
+ }
136
+
14
137
pub ( crate ) fn commit (
15
138
repo_path : & Path ,
16
139
_json : bool ,
0 commit comments