Skip to content

Commit 87321f4

Browse files
committed
Add hunk-indices and hunk-headers support to commiting in but-cli
1 parent 6353f0a commit 87321f4

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;
@@ -148,13 +149,37 @@ pub(crate) fn discard_change(
148149
cwd: &Path,
149150
current_rela_path: &Path,
150151
previous_rela_path: Option<&Path>,
151-
indices_or_headers: Option<discard_change::IndicesOrHeaders>,
152+
indices_or_headers: Option<discard_change::IndicesOrHeaders<'_>>,
152153
) -> anyhow::Result<()> {
153-
let repo = gix::discover(cwd)?;
154+
let repo = configured_repo(gix::discover(cwd)?, RepositoryOpenMode::Merge)?;
154155

155156
let previous_path = previous_rela_path.map(path_to_rela_path).transpose()?;
156157
let path = path_to_rela_path(current_rela_path)?;
157-
let hunk_headers = match indices_or_headers {
158+
let hunk_headers = indices_or_headers_to_hunk_headers(
159+
&repo,
160+
indices_or_headers,
161+
&path,
162+
previous_path.as_ref(),
163+
)?;
164+
let spec = but_workspace::commit_engine::DiffSpec {
165+
previous_path,
166+
path,
167+
hunk_headers,
168+
};
169+
debug_print(but_workspace::discard_workspace_changes(
170+
&repo,
171+
Some(spec.into()),
172+
UI_CONTEXT_LINES,
173+
)?)
174+
}
175+
176+
fn indices_or_headers_to_hunk_headers(
177+
repo: &gix::Repository,
178+
indices_or_headers: Option<IndicesOrHeaders<'_>>,
179+
path: &BString,
180+
previous_path: Option<&BString>,
181+
) -> anyhow::Result<Vec<HunkHeader>> {
182+
let headers = match indices_or_headers {
158183
None => vec![],
159184
Some(discard_change::IndicesOrHeaders::Headers(headers)) => headers
160185
.windows(4)
@@ -166,15 +191,15 @@ pub(crate) fn discard_change(
166191
})
167192
.collect(),
168193
Some(discard_change::IndicesOrHeaders::Indices(hunk_indices)) => {
169-
let worktree_changes = but_core::diff::worktree_changes(&repo)?
194+
let worktree_changes = but_core::diff::worktree_changes(repo)?
170195
.changes
171196
.into_iter()
172197
.find(|change| {
173-
change.path == path
198+
change.path == *path
174199
&& change.previous_path() == previous_path.as_ref().map(|p| p.as_bstr())
175200
}).with_context(|| format!("Couldn't find worktree change for file at '{path}' (previous-path: {previous_path:?}"))?;
176201
let UnifiedDiff::Patch { hunks, .. } =
177-
worktree_changes.unified_diff(&repo, UI_CONTEXT_LINES)?
202+
worktree_changes.unified_diff(repo, UI_CONTEXT_LINES)?
178203
else {
179204
bail!("No hunks available for given '{path}'")
180205
};
@@ -192,16 +217,7 @@ pub(crate) fn discard_change(
192217
.collect::<Result<Vec<HunkHeader>, _>>()?
193218
}
194219
};
195-
let spec = but_workspace::commit_engine::DiffSpec {
196-
previous_path,
197-
path,
198-
hunk_headers,
199-
};
200-
debug_print(but_workspace::discard_workspace_changes(
201-
&repo,
202-
Some(spec.into()),
203-
UI_CONTEXT_LINES,
204-
)?)
220+
Ok(headers)
205221
}
206222

207223
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)