Skip to content

discard changes: hunks #7677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 21, 2025
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
116 changes: 58 additions & 58 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "2"
[workspace.dependencies]
bstr = "1.11.1"
# Add the `tracing` or `tracing-detail` features to see more of gitoxide in the logs. Useful to see which programs it invokes.
gix = { git = "https://github.com/GitoxideLabs/gitoxide", rev = "0bf1d5b9f0b0971b9f25a8e44b7818e37c78d68e", default-features = false, features = [
gix = { git = "https://github.com/GitoxideLabs/gitoxide", rev = "7255a5fc0aa790b54e3176e8ecf066457acd9eef", default-features = false, features = [
] }
gix-testtools = "0.15.0"
insta = "1.41.1"
Expand Down
3 changes: 3 additions & 0 deletions crates/but-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ pub enum Subcommands {
},
/// List all uncommitted working tree changes.
Status {
/// Also compute unified diffs for each tree-change.
#[clap(long, short = 'c', default_value_t = 3)]
context_lines: u32,
/// Also compute unified diffs for each tree-change.
#[clap(long, short = 'd')]
unified_diff: bool,
Expand Down
11 changes: 6 additions & 5 deletions crates/but-cli/src/command/diff.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::command::{debug_print, project_from_path, project_repo};
use crate::command::{UI_CONTEXT_LINES, debug_print, project_from_path, project_repo};
use gix::bstr::BString;
use itertools::Itertools;
use std::path::Path;
Expand All @@ -18,18 +18,18 @@ pub fn commit_changes(
but_core::diff::commit_changes(&repo, previous_commit.map(Into::into), commit.into())?;

if unified_diff {
debug_print(unified_diff_for_changes(&repo, changes)?)
debug_print(unified_diff_for_changes(&repo, changes, UI_CONTEXT_LINES)?)
} else {
debug_print(changes)
}
}

pub fn status(current_dir: &Path, unified_diff: bool) -> anyhow::Result<()> {
pub fn status(current_dir: &Path, unified_diff: bool, context_lines: u32) -> anyhow::Result<()> {
let repo = project_repo(current_dir)?;
let worktree = but_core::diff::worktree_changes(&repo)?;
if unified_diff {
debug_print((
unified_diff_for_changes(&repo, worktree.changes)?,
unified_diff_for_changes(&repo, worktree.changes, context_lines)?,
worktree.ignored_changes,
))
} else {
Expand Down Expand Up @@ -57,12 +57,13 @@ pub fn locks(current_dir: &Path) -> anyhow::Result<()> {
fn unified_diff_for_changes(
repo: &gix::Repository,
changes: Vec<but_core::TreeChange>,
context_lines: u32,
) -> anyhow::Result<Vec<(but_core::TreeChange, but_core::UnifiedDiff)>> {
changes
.into_iter()
.map(|tree_change| {
tree_change
.unified_diff(repo, 3)
.unified_diff(repo, context_lines)
.map(|diff| (tree_change, diff))
})
.collect::<Result<Vec<_>, _>>()
Expand Down
2 changes: 2 additions & 0 deletions crates/but-cli/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use gitbutler_project::Project;
use gix::bstr::BString;
use std::path::Path;

const UI_CONTEXT_LINES: u32 = 3;

pub fn project_from_path(path: &Path) -> anyhow::Result<Project> {
Project::from_path(path)
}
Expand Down
7 changes: 4 additions & 3 deletions crates/but-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ fn main() -> Result<()> {
)
}
args::Subcommands::HunkDependency => command::diff::locks(&args.current_dir),
args::Subcommands::Status { unified_diff } => {
command::diff::status(&args.current_dir, *unified_diff)
}
args::Subcommands::Status {
unified_diff,
context_lines,
} => command::diff::status(&args.current_dir, *unified_diff, *context_lines),
args::Subcommands::CommitChanges {
unified_diff,
current_commit,
Expand Down
122 changes: 122 additions & 0 deletions crates/but-core/src/diff/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,130 @@
pub(crate) mod commit;

use bstr::{BStr, ByteSlice};
pub use commit::commit_changes;

mod worktree;
use crate::{ChangeState, ModeFlags, TreeChange, TreeStatus, TreeStatusKind};
pub use worktree::worktree_changes;

/// conversion functions for use in the UI
pub mod ui;

impl TreeStatus {
/// Learn what kind of status this is, useful if only this information is needed.
pub fn kind(&self) -> TreeStatusKind {
match self {
TreeStatus::Addition { .. } => TreeStatusKind::Addition,
TreeStatus::Deletion { .. } => TreeStatusKind::Deletion,
TreeStatus::Modification { .. } => TreeStatusKind::Modification,
TreeStatus::Rename { .. } => TreeStatusKind::Rename,
}
}

/// Return the state in which the change is currently. May be `None` if there is no current state after a deletion.
pub fn state(&self) -> Option<ChangeState> {
match self {
TreeStatus::Addition { state, .. }
| TreeStatus::Rename { state, .. }
| TreeStatus::Modification { state, .. } => Some(*state),
TreeStatus::Deletion { .. } => None,
}
}

/// Return the previous state that the change originated from. May be `None` if there is no previous state, for instance after an addition.
/// Also provide the path from which the state was possibly obtained.
pub fn previous_state_and_path(&self) -> Option<(ChangeState, Option<&BStr>)> {
match self {
TreeStatus::Addition { .. } => None,
TreeStatus::Rename {
previous_state,
previous_path,
..
} => Some((*previous_state, Some(previous_path.as_bstr()))),
TreeStatus::Modification { previous_state, .. }
| TreeStatus::Deletion { previous_state, .. } => Some((*previous_state, None)),
}
}
}

impl TreeChange {
/// Return the path at which this directory entry was previously located, if it was renamed.
pub fn previous_path(&self) -> Option<&BStr> {
match &self.status {
TreeStatus::Addition { .. }
| TreeStatus::Deletion { .. }
| TreeStatus::Modification { .. } => None,
TreeStatus::Rename { previous_path, .. } => Some(previous_path.as_ref()),
}
}
}

impl ModeFlags {
fn calculate(old: &ChangeState, new: &ChangeState) -> Option<Self> {
Self::calculate_inner(old.kind, new.kind)
}

fn calculate_inner(
old: gix::object::tree::EntryKind,
new: gix::object::tree::EntryKind,
) -> Option<Self> {
use gix::object::tree::EntryKind as E;
Some(match (old, new) {
(E::Blob, E::BlobExecutable) => ModeFlags::ExecutableBitAdded,
(E::BlobExecutable, E::Blob) => ModeFlags::ExecutableBitRemoved,
(E::Blob | E::BlobExecutable, E::Link) => ModeFlags::TypeChangeFileToLink,
(E::Link, E::Blob | E::BlobExecutable) => ModeFlags::TypeChangeLinkToFile,
(a, b) if a != b => ModeFlags::TypeChange,
_ => return None,
})
}
}

#[cfg(test)]
mod tests {
mod flags {
use crate::ModeFlags;
use gix::objs::tree::EntryKind;

#[test]
fn calculate() {
for ((old, new), expected) in [
((EntryKind::Blob, EntryKind::Blob), None),
(
(EntryKind::Blob, EntryKind::BlobExecutable),
Some(ModeFlags::ExecutableBitAdded),
),
(
(EntryKind::BlobExecutable, EntryKind::Blob),
Some(ModeFlags::ExecutableBitRemoved),
),
(
(EntryKind::BlobExecutable, EntryKind::Link),
Some(ModeFlags::TypeChangeFileToLink),
),
(
(EntryKind::Blob, EntryKind::Link),
Some(ModeFlags::TypeChangeFileToLink),
),
(
(EntryKind::Link, EntryKind::BlobExecutable),
Some(ModeFlags::TypeChangeLinkToFile),
),
(
(EntryKind::Link, EntryKind::Blob),
Some(ModeFlags::TypeChangeLinkToFile),
),
(
(EntryKind::Commit, EntryKind::Blob),
Some(ModeFlags::TypeChange),
),
(
(EntryKind::Blob, EntryKind::Commit),
Some(ModeFlags::TypeChange),
),
] {
assert_eq!(ModeFlags::calculate_inner(old, new), expected);
}
}
}
}
Loading
Loading