11use anyhow:: { Context as _, Result } ;
22use bstr:: BStr ;
3+ use bstr:: ByteSlice ;
34use but_core:: { DiffSpec , diff:: tree_changes} ;
45use but_ctx:: Context ;
56use gitbutler_branch_actions:: update_workspace_commit;
@@ -13,18 +14,7 @@ pub fn commited_file_to_another_commit(
1314 target_id : gix:: ObjectId ,
1415 out : & mut OutputChannel ,
1516) -> Result < ( ) > {
16- let relevant_changes = {
17- let repo = ctx. repo . get ( ) ?;
18- let source_commit = repo. find_commit ( source_id) ?;
19- let source_commit_parent_id = source_commit. parent_ids ( ) . next ( ) . context ( "First parent" ) ?;
20-
21- let tree_changes = tree_changes ( & repo, Some ( source_commit_parent_id. detach ( ) ) , source_id) ?;
22- tree_changes
23- . into_iter ( )
24- . filter ( |tc| tc. path == path)
25- . map ( Into :: into)
26- . collect :: < Vec < DiffSpec > > ( )
27- } ;
17+ let relevant_changes = changes_for_path_in_commit ( ctx, path, source_id) ?;
2818
2919 but_api:: commit:: move_changes:: commit_move_changes_between_only (
3020 ctx,
@@ -53,20 +43,7 @@ pub fn uncommit_file(
5343) -> Result < ( ) > {
5444 // Convert target_branch to StackId if provided (for hunk assignment after uncommit)
5545 let assign_to = find_stack_id_for_branch ( ctx, target_branch) ?;
56-
57- let relevant_changes = {
58- let repo = ctx. repo . get ( ) ?;
59-
60- let source_commit = repo. find_commit ( source_id) ?;
61- let source_commit_parent_id = source_commit. parent_ids ( ) . next ( ) . context ( "First parent" ) ?;
62-
63- let tree_changes = tree_changes ( & repo, Some ( source_commit_parent_id. detach ( ) ) , source_id) ?;
64- tree_changes
65- . into_iter ( )
66- . filter ( |tc| tc. path == path)
67- . map ( Into :: into)
68- . collect :: < Vec < DiffSpec > > ( )
69- } ;
46+ let relevant_changes = changes_for_path_in_commit ( ctx, path, source_id) ?;
7047
7148 but_api:: commit:: uncommit:: commit_uncommit_changes_only (
7249 ctx,
@@ -86,6 +63,66 @@ pub fn uncommit_file(
8663 Ok ( ( ) )
8764}
8865
66+ pub fn uncommit_file_and_discard (
67+ ctx : & mut Context ,
68+ path : & BStr ,
69+ source_id : gix:: ObjectId ,
70+ out : & mut OutputChannel ,
71+ emit_output : bool ,
72+ ) -> Result < ( ) > {
73+ let relevant_changes = changes_for_path_in_commit ( ctx, path, source_id) ?;
74+
75+ let context_lines = ctx. settings . context_lines ;
76+ but_api:: commit:: uncommit:: commit_uncommit_changes (
77+ ctx,
78+ source_id,
79+ relevant_changes. clone ( ) ,
80+ None ,
81+ ) ?;
82+
83+ let repo = ctx. repo . get ( ) ?;
84+ let dropped = but_workspace:: discard_workspace_changes ( & repo, relevant_changes, context_lines) ?;
85+
86+ update_workspace_commit ( ctx, false ) ?;
87+
88+ if emit_output {
89+ if let Some ( out) = out. for_human ( ) {
90+ if !dropped. is_empty ( ) {
91+ writeln ! (
92+ out,
93+ "Warning: Some changes could not be discarded (possibly already discarded or modified):"
94+ ) ?;
95+ for spec in & dropped {
96+ writeln ! ( out, " {}" , spec. path. as_bstr( ) ) ?;
97+ }
98+ }
99+ writeln ! ( out, "Discarded committed changes" ) ?;
100+ } else if let Some ( out) = out. for_json ( ) {
101+ out. write_value ( serde_json:: json!( { "ok" : true } ) ) ?;
102+ }
103+ }
104+
105+ Ok ( ( ) )
106+ }
107+
108+ fn changes_for_path_in_commit (
109+ ctx : & Context ,
110+ path : & BStr ,
111+ source_id : gix:: ObjectId ,
112+ ) -> Result < Vec < DiffSpec > > {
113+ let repo = ctx. repo . get ( ) ?;
114+
115+ let source_commit = repo. find_commit ( source_id) ?;
116+ let source_commit_parent_id = source_commit. parent_ids ( ) . next ( ) . context ( "First parent" ) ?;
117+
118+ let tree_changes = tree_changes ( & repo, Some ( source_commit_parent_id. detach ( ) ) , source_id) ?;
119+ Ok ( tree_changes
120+ . into_iter ( )
121+ . filter ( |tc| tc. path == path)
122+ . map ( Into :: into)
123+ . collect ( ) )
124+ }
125+
89126/// Determine which stack contains the target branch, if any.
90127fn find_stack_id_for_branch (
91128 ctx : & Context ,
0 commit comments