Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions crates/but-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ pub struct Args {
pub enum Subcommands {
/// Commit or amend all worktree changes to a new commit.
Commit {
/// The repo-relative path to the changed file to commit.
#[clap(requires_if(clap::builder::ArgPredicate::IsPresent, "hunk_headers"))]
current_path: Option<PathBuf>,
/// If the change is a rename, identify the repo-relative path of the source.
previous_path: Option<PathBuf>,
/// The 1-based pairs of 4 numbers equivalent to '(old_start,old_lines,new_start,new_lines)'
#[clap(
long,
requires_if(clap::builder::ArgPredicate::IsPresent, "current_path"),
num_args = 4,
value_names = ["old-start", "old-lines", "new-start", "new-lines"])
]
hunk_headers: Vec<u32>,
/// The message of the new commit.
#[clap(long, short = 'm')]
message: Option<String>,
Expand Down
31 changes: 29 additions & 2 deletions crates/but-cli/src/command/commit.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use crate::command::debug_print;
use crate::command::discard_change::IndicesOrHeaders;
use crate::command::{debug_print, indices_or_headers_to_hunk_headers, path_to_rela_path};
use anyhow::bail;
use but_core::TreeChange;
use but_workspace::commit_engine::{
DiffSpec, ReferenceFrame, StackSegmentId, create_commit_and_update_refs,
};
use gitbutler_project::Project;
use gitbutler_stack::{VirtualBranchesHandle, VirtualBranchesState};
use std::path::Path;

#[allow(clippy::too_many_arguments)]
pub fn commit(
repo: gix::Repository,
project: Option<Project>,
Expand All @@ -15,6 +18,9 @@ pub fn commit(
parent_revspec: Option<&str>,
stack_segment_ref: Option<&str>,
workspace_tip: Option<&str>,
current_rela_path: Option<&Path>,
previous_rela_path: Option<&Path>,
headers: Option<&[u32]>,
) -> anyhow::Result<()> {
if message.is_none() && !amend {
bail!("Need a message when creating a new commit");
Expand All @@ -24,7 +30,28 @@ pub fn commit(
.unwrap_or_else(|| Ok(repo.head_id()?))?
.detach();

let changes = to_whole_file_diffspec(but_core::diff::worktree_changes(&repo)?.changes);
let changes = match (current_rela_path, previous_rela_path, headers) {
(None, None, None) => {
to_whole_file_diffspec(but_core::diff::worktree_changes(&repo)?.changes)
}
(Some(current_path), previous_path, Some(headers)) => {
let path = path_to_rela_path(current_path)?;
let previous_path = previous_path.map(path_to_rela_path).transpose()?;
let hunk_headers = indices_or_headers_to_hunk_headers(
&repo,
Some(IndicesOrHeaders::Headers(headers)),
&path,
previous_path.as_ref(),
)?;

vec![DiffSpec {
previous_path,
path,
hunk_headers,
}]
}
_ => unreachable!("BUG: specifying this shouldn't be possible"),
};
if let Some(project) = project.as_ref() {
let destination = if amend {
if message.is_some() {
Expand Down
48 changes: 32 additions & 16 deletions crates/but-cli/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fn project_controller(
}

mod commit;
use crate::command::discard_change::IndicesOrHeaders;
pub use commit::commit;

pub mod diff;
Expand Down Expand Up @@ -150,13 +151,37 @@ pub(crate) fn discard_change(
cwd: &Path,
current_rela_path: &Path,
previous_rela_path: Option<&Path>,
indices_or_headers: Option<discard_change::IndicesOrHeaders>,
indices_or_headers: Option<discard_change::IndicesOrHeaders<'_>>,
) -> anyhow::Result<()> {
let repo = gix::discover(cwd)?;
let repo = configured_repo(gix::discover(cwd)?, RepositoryOpenMode::Merge)?;

let previous_path = previous_rela_path.map(path_to_rela_path).transpose()?;
let path = path_to_rela_path(current_rela_path)?;
let hunk_headers = match indices_or_headers {
let hunk_headers = indices_or_headers_to_hunk_headers(
&repo,
indices_or_headers,
&path,
previous_path.as_ref(),
)?;
let spec = but_workspace::commit_engine::DiffSpec {
previous_path,
path,
hunk_headers,
};
debug_print(but_workspace::discard_workspace_changes(
&repo,
Some(spec.into()),
UI_CONTEXT_LINES,
)?)
}

fn indices_or_headers_to_hunk_headers(
repo: &gix::Repository,
indices_or_headers: Option<IndicesOrHeaders<'_>>,
path: &BString,
previous_path: Option<&BString>,
) -> anyhow::Result<Vec<HunkHeader>> {
let headers = match indices_or_headers {
None => vec![],
Some(discard_change::IndicesOrHeaders::Headers(headers)) => headers
.windows(4)
Expand All @@ -168,15 +193,15 @@ pub(crate) fn discard_change(
})
.collect(),
Some(discard_change::IndicesOrHeaders::Indices(hunk_indices)) => {
let worktree_changes = but_core::diff::worktree_changes(&repo)?
let worktree_changes = but_core::diff::worktree_changes(repo)?
.changes
.into_iter()
.find(|change| {
change.path == path
change.path == *path
&& change.previous_path() == previous_path.as_ref().map(|p| p.as_bstr())
}).with_context(|| format!("Couldn't find worktree change for file at '{path}' (previous-path: {previous_path:?}"))?;
let UnifiedDiff::Patch { hunks, .. } =
worktree_changes.unified_diff(&repo, UI_CONTEXT_LINES)?
worktree_changes.unified_diff(repo, UI_CONTEXT_LINES)?
else {
bail!("No hunks available for given '{path}'")
};
Expand All @@ -194,16 +219,7 @@ pub(crate) fn discard_change(
.collect::<Result<Vec<HunkHeader>, _>>()?
}
};
let spec = but_workspace::commit_engine::DiffSpec {
previous_path,
path,
hunk_headers,
};
debug_print(but_workspace::discard_workspace_changes(
&repo,
Some(spec.into()),
UI_CONTEXT_LINES,
)?)
Ok(headers)
}

fn path_to_rela_path(path: &Path) -> anyhow::Result<BString> {
Expand Down
11 changes: 11 additions & 0 deletions crates/but-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! A debug-CLI for making `but`-crates functionality available in real-world repositories.
#![deny(rust_2018_idioms)]
use anyhow::Result;

mod args;
Expand Down Expand Up @@ -38,6 +39,9 @@ fn main() -> Result<()> {
},
),
args::Subcommands::Commit {
current_path,
previous_path,
hunk_headers,
message,
amend,
parent,
Expand All @@ -53,6 +57,13 @@ fn main() -> Result<()> {
parent.as_deref(),
stack_segment_ref.as_deref(),
workspace_tip.as_deref(),
current_path.as_deref(),
previous_path.as_deref(),
if !hunk_headers.is_empty() {
Some(hunk_headers)
} else {
None
},
)
}
args::Subcommands::HunkDependency => command::diff::locks(&args.current_dir),
Expand Down
8 changes: 7 additions & 1 deletion crates/but-core/src/unified_diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use gix::diff::blob::unified_diff::ContextSize;
use serde::Serialize;

/// A hunk as used in a [UnifiedDiff], which also contains all added and removed lines.
#[derive(Debug, Clone, Serialize)]
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DiffHunk {
/// The 1-based line number at which the previous version of the file started.
Expand Down Expand Up @@ -34,6 +34,12 @@ pub struct DiffHunk {
pub diff: BString,
}

impl std::fmt::Debug for DiffHunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#"DiffHunk("{}")"#, self.diff)
}
}

impl UnifiedDiff {
/// Determine how resources are converted to their form used for diffing.
///
Expand Down
Loading
Loading