Skip to content

Commit f186c23

Browse files
authored
Merge pull request #1626 from cruessler/add-gix-diff
Add gix diff tree
2 parents 4cff32c + 6777ecb commit f186c23

File tree

4 files changed

+156
-3
lines changed

4 files changed

+156
-3
lines changed

gitoxide-core/src/repository/diff.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use gix::bstr::{BString, ByteSlice};
2+
use gix::objs::tree::EntryMode;
3+
use gix::prelude::ObjectIdExt;
4+
5+
pub fn tree(
6+
mut repo: gix::Repository,
7+
out: &mut dyn std::io::Write,
8+
old_treeish: BString,
9+
new_treeish: BString,
10+
) -> anyhow::Result<()> {
11+
repo.object_cache_size_if_unset(repo.compute_object_cache_size_for_tree_diffs(&**repo.index_or_empty()?));
12+
13+
let old_tree_id = repo.rev_parse_single(old_treeish.as_bstr())?;
14+
let new_tree_id = repo.rev_parse_single(new_treeish.as_bstr())?;
15+
16+
let old_tree = old_tree_id.object()?.peel_to_tree()?;
17+
let new_tree = new_tree_id.object()?.peel_to_tree()?;
18+
19+
let changes = repo.diff_tree_to_tree(&old_tree, &new_tree, None)?;
20+
21+
writeln!(
22+
out,
23+
"Diffing trees `{old_treeish}` ({old_tree_id}) -> `{new_treeish}` ({new_tree_id})\n"
24+
)?;
25+
write_changes(&repo, out, changes)?;
26+
27+
Ok(())
28+
}
29+
30+
fn write_changes(
31+
repo: &gix::Repository,
32+
mut out: impl std::io::Write,
33+
changes: Vec<gix::diff::tree_with_rewrites::Change>,
34+
) -> Result<(), std::io::Error> {
35+
for change in changes {
36+
match change {
37+
gix::diff::tree_with_rewrites::Change::Addition {
38+
location,
39+
id,
40+
entry_mode,
41+
..
42+
} => {
43+
writeln!(out, "A: {}", typed_location(location, entry_mode))?;
44+
writeln!(out, " {}", id.attach(repo).shorten_or_id())?;
45+
writeln!(out, " -> {:o}", entry_mode.0)?;
46+
}
47+
gix::diff::tree_with_rewrites::Change::Deletion {
48+
location,
49+
id,
50+
entry_mode,
51+
..
52+
} => {
53+
writeln!(out, "D: {}", typed_location(location, entry_mode))?;
54+
writeln!(out, " {}", id.attach(repo).shorten_or_id())?;
55+
writeln!(out, " {:o} ->", entry_mode.0)?;
56+
}
57+
gix::diff::tree_with_rewrites::Change::Modification {
58+
location,
59+
previous_id,
60+
id,
61+
previous_entry_mode,
62+
entry_mode,
63+
} => {
64+
writeln!(out, "M: {}", typed_location(location, entry_mode))?;
65+
writeln!(
66+
out,
67+
" {previous_id} -> {id}",
68+
previous_id = previous_id.attach(repo).shorten_or_id(),
69+
id = id.attach(repo).shorten_or_id()
70+
)?;
71+
if previous_entry_mode != entry_mode {
72+
writeln!(out, " {:o} -> {:o}", previous_entry_mode.0, entry_mode.0)?;
73+
}
74+
}
75+
gix::diff::tree_with_rewrites::Change::Rewrite {
76+
source_location,
77+
source_id,
78+
id,
79+
location,
80+
source_entry_mode,
81+
entry_mode,
82+
..
83+
} => {
84+
writeln!(
85+
out,
86+
"R: {source} -> {dest}",
87+
source = typed_location(source_location, source_entry_mode),
88+
dest = typed_location(location, entry_mode)
89+
)?;
90+
writeln!(
91+
out,
92+
" {source_id} -> {id}",
93+
source_id = source_id.attach(repo).shorten_or_id(),
94+
id = id.attach(repo).shorten_or_id()
95+
)?;
96+
if source_entry_mode != entry_mode {
97+
writeln!(out, " {:o} -> {:o}", source_entry_mode.0, entry_mode.0)?;
98+
}
99+
}
100+
};
101+
}
102+
103+
Ok(())
104+
}
105+
106+
fn typed_location(mut location: BString, mode: EntryMode) -> BString {
107+
if mode.is_tree() {
108+
location.push(b'/');
109+
}
110+
location
111+
}

gitoxide-core/src/repository/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub use credential::function as credential;
2828
pub mod attributes;
2929
#[cfg(feature = "clean")]
3030
pub mod clean;
31+
pub mod diff;
3132
pub mod dirty;
3233
#[cfg(feature = "clean")]
3334
pub use clean::function::clean;

src/plumbing/main.rs

+16
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,22 @@ pub fn main() -> Result<()> {
189189
core::repository::merge_base(repository(Mode::Lenient)?, first, others, out, format)
190190
},
191191
),
192+
Subcommands::Diff(crate::plumbing::options::diff::Platform { cmd }) => match cmd {
193+
crate::plumbing::options::diff::SubCommands::Tree {
194+
old_treeish,
195+
new_treeish,
196+
} => prepare_and_run(
197+
"diff-tree",
198+
trace,
199+
verbose,
200+
progress,
201+
progress_keep_open,
202+
None,
203+
move |_progress, out, _err| {
204+
core::repository::diff::tree(repository(Mode::Lenient)?, out, old_treeish, new_treeish)
205+
},
206+
),
207+
},
192208
Subcommands::Worktree(crate::plumbing::options::worktree::Platform { cmd }) => match cmd {
193209
crate::plumbing::options::worktree::SubCommands::List => prepare_and_run(
194210
"worktree-list",

src/plumbing/options/mod.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub enum Subcommands {
145145
Corpus(corpus::Platform),
146146
MergeBase(merge_base::Command),
147147
Merge(merge::Platform),
148+
Diff(diff::Platform),
148149
Worktree(worktree::Platform),
149150
/// Subcommands that need no git repository to run.
150151
#[clap(subcommand)]
@@ -371,19 +372,43 @@ pub mod merge {
371372
#[clap(long, short = 'c')]
372373
resolve_with: Option<ResolveWith>,
373374

374-
/// A path or revspec to our file
375+
/// A path or revspec to our file.
375376
#[clap(value_name = "OURS", value_parser = crate::shared::AsBString)]
376377
ours: BString,
377-
/// A path or revspec to the base for both ours and theirs
378+
/// A path or revspec to the base for both ours and theirs.
378379
#[clap(value_name = "BASE", value_parser = crate::shared::AsBString)]
379380
base: BString,
380-
/// A path or revspec to their file
381+
/// A path or revspec to their file.
381382
#[clap(value_name = "OURS", value_parser = crate::shared::AsBString)]
382383
theirs: BString,
383384
},
384385
}
385386
}
386387

388+
pub mod diff {
389+
use gix::bstr::BString;
390+
391+
/// Print all changes between two objects
392+
#[derive(Debug, clap::Parser)]
393+
pub struct Platform {
394+
#[clap(subcommand)]
395+
pub cmd: SubCommands,
396+
}
397+
398+
#[derive(Debug, clap::Subcommand)]
399+
pub enum SubCommands {
400+
/// Diff two trees.
401+
Tree {
402+
/// A rev-spec representing the 'before' or old tree.
403+
#[clap(value_parser = crate::shared::AsBString)]
404+
old_treeish: BString,
405+
/// A rev-spec representing the 'after' or new tree.
406+
#[clap(value_parser = crate::shared::AsBString)]
407+
new_treeish: BString,
408+
},
409+
}
410+
}
411+
387412
pub mod config {
388413
use gix::bstr::BString;
389414

0 commit comments

Comments
 (0)