Skip to content

Commit 540ba4a

Browse files
committed
Add hunk-indices and hunk-headers support to commiting in but-cli
1 parent 3d0830c commit 540ba4a

File tree

4 files changed

+85
-18
lines changed

4 files changed

+85
-18
lines changed

crates/but-cli/src/args.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ pub struct Args {
2828
pub enum Subcommands {
2929
/// Commit or amend all worktree changes to a new commit.
3030
Commit {
31+
/// The repo-relative path to the changed file to commit.
32+
#[clap(requires_if(clap::builder::ArgPredicate::IsPresent, "hunk_headers"))]
33+
current_path: Option<PathBuf>,
34+
/// If the change is a rename, identify the repo-relative path of the source.
35+
previous_path: Option<PathBuf>,
36+
/// The 1-based pairs of 4 numbers equivalent to '(old_start,old_lines,new_start,new_lines)'
37+
#[clap(
38+
long,
39+
requires_if(clap::builder::ArgPredicate::IsPresent, "current_path"),
40+
num_args = 4,
41+
value_names = ["old-start", "old-lines", "new-start", "new-lines"])
42+
]
43+
hunk_headers: Vec<u32>,
3144
/// The message of the new commit.
3245
#[clap(long, short = 'm')]
3346
message: Option<String>,

crates/but-cli/src/command/commit.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
use crate::command::debug_print;
1+
use crate::command::discard_change::IndicesOrHeaders;
2+
use crate::command::{debug_print, indices_or_headers_to_hunk_headers, path_to_rela_path};
23
use anyhow::bail;
34
use but_core::TreeChange;
45
use but_workspace::commit_engine::{
56
DiffSpec, ReferenceFrame, StackSegmentId, create_commit_and_update_refs,
67
};
78
use gitbutler_project::Project;
89
use gitbutler_stack::{VirtualBranchesHandle, VirtualBranchesState};
10+
use std::path::Path;
911

12+
#[allow(clippy::too_many_arguments)]
1013
pub fn commit(
1114
repo: gix::Repository,
1215
project: Option<Project>,
@@ -15,6 +18,9 @@ pub fn commit(
1518
parent_revspec: Option<&str>,
1619
stack_segment_ref: Option<&str>,
1720
workspace_tip: Option<&str>,
21+
current_rela_path: Option<&Path>,
22+
previous_rela_path: Option<&Path>,
23+
headers: Option<&[u32]>,
1824
) -> anyhow::Result<()> {
1925
if message.is_none() && !amend {
2026
bail!("Need a message when creating a new commit");
@@ -24,7 +30,28 @@ pub fn commit(
2430
.unwrap_or_else(|| Ok(repo.head_id()?))?
2531
.detach();
2632

27-
let changes = to_whole_file_diffspec(but_core::diff::worktree_changes(&repo)?.changes);
33+
let changes = match (current_rela_path, previous_rela_path, headers) {
34+
(None, None, None) => {
35+
to_whole_file_diffspec(but_core::diff::worktree_changes(&repo)?.changes)
36+
}
37+
(Some(current_path), previous_path, Some(headers)) => {
38+
let path = path_to_rela_path(current_path)?;
39+
let previous_path = previous_path.map(path_to_rela_path).transpose()?;
40+
let hunk_headers = indices_or_headers_to_hunk_headers(
41+
&repo,
42+
Some(IndicesOrHeaders::Headers(headers)),
43+
&path,
44+
previous_path.as_ref(),
45+
)?;
46+
47+
vec![DiffSpec {
48+
previous_path,
49+
path,
50+
hunk_headers,
51+
}]
52+
}
53+
_ => unreachable!("BUG: specifying this shouldn't be possible"),
54+
};
2855
if let Some(project) = project.as_ref() {
2956
let destination = if amend {
3057
if message.is_some() {

crates/but-cli/src/command/mod.rs

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ fn project_controller(
9999
}
100100

101101
mod commit;
102+
use crate::command::discard_change::IndicesOrHeaders;
102103
pub use commit::commit;
103104

104105
pub mod diff;
@@ -150,13 +151,37 @@ pub(crate) fn discard_change(
150151
cwd: &Path,
151152
current_rela_path: &Path,
152153
previous_rela_path: Option<&Path>,
153-
indices_or_headers: Option<discard_change::IndicesOrHeaders>,
154+
indices_or_headers: Option<discard_change::IndicesOrHeaders<'_>>,
154155
) -> anyhow::Result<()> {
155-
let repo = gix::discover(cwd)?;
156+
let repo = configured_repo(gix::discover(cwd)?, RepositoryOpenMode::Merge)?;
156157

157158
let previous_path = previous_rela_path.map(path_to_rela_path).transpose()?;
158159
let path = path_to_rela_path(current_rela_path)?;
159-
let hunk_headers = match indices_or_headers {
160+
let hunk_headers = indices_or_headers_to_hunk_headers(
161+
&repo,
162+
indices_or_headers,
163+
&path,
164+
previous_path.as_ref(),
165+
)?;
166+
let spec = but_workspace::commit_engine::DiffSpec {
167+
previous_path,
168+
path,
169+
hunk_headers,
170+
};
171+
debug_print(but_workspace::discard_workspace_changes(
172+
&repo,
173+
Some(spec.into()),
174+
UI_CONTEXT_LINES,
175+
)?)
176+
}
177+
178+
fn indices_or_headers_to_hunk_headers(
179+
repo: &gix::Repository,
180+
indices_or_headers: Option<IndicesOrHeaders<'_>>,
181+
path: &BString,
182+
previous_path: Option<&BString>,
183+
) -> anyhow::Result<Vec<HunkHeader>> {
184+
let headers = match indices_or_headers {
160185
None => vec![],
161186
Some(discard_change::IndicesOrHeaders::Headers(headers)) => headers
162187
.windows(4)
@@ -168,15 +193,15 @@ pub(crate) fn discard_change(
168193
})
169194
.collect(),
170195
Some(discard_change::IndicesOrHeaders::Indices(hunk_indices)) => {
171-
let worktree_changes = but_core::diff::worktree_changes(&repo)?
196+
let worktree_changes = but_core::diff::worktree_changes(repo)?
172197
.changes
173198
.into_iter()
174199
.find(|change| {
175-
change.path == path
200+
change.path == *path
176201
&& change.previous_path() == previous_path.as_ref().map(|p| p.as_bstr())
177202
}).with_context(|| format!("Couldn't find worktree change for file at '{path}' (previous-path: {previous_path:?}"))?;
178203
let UnifiedDiff::Patch { hunks, .. } =
179-
worktree_changes.unified_diff(&repo, UI_CONTEXT_LINES)?
204+
worktree_changes.unified_diff(repo, UI_CONTEXT_LINES)?
180205
else {
181206
bail!("No hunks available for given '{path}'")
182207
};
@@ -194,16 +219,7 @@ pub(crate) fn discard_change(
194219
.collect::<Result<Vec<HunkHeader>, _>>()?
195220
}
196221
};
197-
let spec = but_workspace::commit_engine::DiffSpec {
198-
previous_path,
199-
path,
200-
hunk_headers,
201-
};
202-
debug_print(but_workspace::discard_workspace_changes(
203-
&repo,
204-
Some(spec.into()),
205-
UI_CONTEXT_LINES,
206-
)?)
222+
Ok(headers)
207223
}
208224

209225
fn path_to_rela_path(path: &Path) -> anyhow::Result<BString> {

crates/but-cli/src/main.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! A debug-CLI for making `but`-crates functionality available in real-world repositories.
2+
#![deny(rust_2018_idioms)]
23
use anyhow::Result;
34

45
mod args;
@@ -38,6 +39,9 @@ fn main() -> Result<()> {
3839
},
3940
),
4041
args::Subcommands::Commit {
42+
current_path,
43+
previous_path,
44+
hunk_headers,
4145
message,
4246
amend,
4347
parent,
@@ -53,6 +57,13 @@ fn main() -> Result<()> {
5357
parent.as_deref(),
5458
stack_segment_ref.as_deref(),
5559
workspace_tip.as_deref(),
60+
current_path.as_deref(),
61+
previous_path.as_deref(),
62+
if !hunk_headers.is_empty() {
63+
Some(hunk_headers)
64+
} else {
65+
None
66+
},
5667
)
5768
}
5869
args::Subcommands::HunkDependency => command::diff::locks(&args.current_dir),

0 commit comments

Comments
 (0)