Skip to content

Commit 100ed6a

Browse files
Caleb-T-Owensschacon
authored andcommitted
Added uncommitting committed file into stack
1 parent 9ac8f72 commit 100ed6a

File tree

2 files changed

+92
-6
lines changed

2 files changed

+92
-6
lines changed

crates/but/src/rub/commits.rs

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
use std::collections::HashSet;
2+
13
use anyhow::{Context, Result};
24
use bstr::ByteSlice;
35
use but_core::diff::tree_changes;
6+
use but_hunk_assignment::HunkAssignmentRequest;
47
use but_workspace::DiffSpec;
58
use gitbutler_branch_actions::update_workspace_commit;
69
use gitbutler_command_context::CommandContext;
710
use gitbutler_stack::VirtualBranchesHandle;
811

9-
use crate::rub::undo::stack_id_by_commit_id;
12+
use crate::rub::{assign::branch_name_to_stack_id, undo::stack_id_by_commit_id};
1013

1114
pub fn commited_file_to_another_commit(
1215
ctx: &mut CommandContext,
@@ -45,3 +48,80 @@ pub fn commited_file_to_another_commit(
4548

4649
Ok(())
4750
}
51+
52+
pub fn commited_file_to_unassigned_stack(
53+
ctx: &mut CommandContext,
54+
path: &str,
55+
source_id: gix::ObjectId,
56+
target_branch: &str,
57+
) -> Result<()> {
58+
let source_stack = stack_id_by_commit_id(ctx, &source_id)?;
59+
let target_stack = branch_name_to_stack_id(ctx, Some(target_branch))?;
60+
61+
let repo = ctx.gix_repo()?;
62+
63+
let source_commit = repo.find_commit(source_id)?;
64+
let source_commit_parent_id = source_commit.parent_ids().next().context("First parent")?;
65+
66+
let (tree_changes, _) = tree_changes(&repo, Some(source_commit_parent_id.detach()), source_id)?;
67+
let relevant_changes = tree_changes
68+
.into_iter()
69+
.filter(|tc| tc.path.to_str_lossy() == path)
70+
.map(Into::into)
71+
.collect::<Vec<DiffSpec>>();
72+
73+
// If we want to assign the changes after uncommitting, we could try to do
74+
// something with the hunk headers, but this is not precise as the hunk
75+
// headers might have changed from what they were like when they were
76+
// committed.
77+
//
78+
// As such, we take all the old assignments, and all the new assignments from after the
79+
// uncommit, and find the ones that are not present in the old assignments.
80+
// We then convert those into assignment requests for the given stack.
81+
let before_assignments = but_hunk_assignment::assignments_with_fallback(
82+
ctx,
83+
false,
84+
None::<Vec<but_core::TreeChange>>,
85+
None,
86+
)?
87+
.0;
88+
89+
but_workspace::remove_changes_from_commit_in_stack(
90+
&ctx,
91+
source_stack,
92+
source_id,
93+
relevant_changes,
94+
ctx.app_settings().context_lines,
95+
)?;
96+
97+
let vb_state = VirtualBranchesHandle::new(ctx.project().gb_dir());
98+
update_workspace_commit(&vb_state, ctx)?;
99+
100+
let (after_assignments, _) = but_hunk_assignment::assignments_with_fallback(
101+
ctx,
102+
false,
103+
None::<Vec<but_core::TreeChange>>,
104+
None,
105+
)?;
106+
107+
let before_assignments = before_assignments
108+
.into_iter()
109+
.filter_map(|a| a.id)
110+
.collect::<HashSet<_>>();
111+
112+
let to_assign = after_assignments
113+
.into_iter()
114+
.filter(|a| a.id.is_some_and(|id| !before_assignments.contains(&id)))
115+
.map(|a| HunkAssignmentRequest {
116+
hunk_header: a.hunk_header,
117+
path_bytes: a.path_bytes,
118+
stack_id: target_stack,
119+
})
120+
.collect::<Vec<_>>();
121+
122+
but_hunk_assignment::assign(ctx, to_assign, None)?;
123+
124+
println!("Uncommitted changes");
125+
126+
Ok(())
127+
}

crates/but/src/rub/mod.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,17 @@ pub(crate) fn handle(
5858
create_snapshot(ctx, &project, OperationKind::FileChanges);
5959
uncommit::file_from_commit(ctx, path, commit_oid)?;
6060
}
61-
(CliId::CommittedFile { .. }, CliId::Branch { .. }) => {
62-
// Extract file from commit to branch - for now, not implemented
63-
bail!(
64-
"Extracting files from commits is not yet supported. Use git commands to extract file changes."
65-
)
61+
(
62+
CliId::CommittedFile {
63+
path,
64+
commit_oid: source_id,
65+
},
66+
CliId::Branch {
67+
name: target_branch,
68+
},
69+
) => {
70+
create_snapshot(ctx, &project, OperationKind::FileChanges);
71+
commits::commited_file_to_unassigned_stack(ctx, path, *source_id, target_branch)?;
6672
}
6773
(
6874
CliId::CommittedFile {

0 commit comments

Comments
 (0)