Skip to content

Commit be9af32

Browse files
committed
Create new gix-status crate to capture git-status like functionality
This includes the various diffs git can do between different representations of the repository state, like * index and working tree * index and tree * find untracked files * check if the working tree is dirty (quickly)
1 parent 7766cf9 commit be9af32

31 files changed

+1028
-8
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ members = [
235235
"gix-index",
236236
"gix-bitmap",
237237
"gix-worktree",
238+
"gix-status",
238239
"gix-revision",
239240
"gix-packetline",
240241
"gix-packetline-blocking",

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ is usable to some extent.
9595
* [gix-worktree-stream](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-worktree-stream)
9696
* [gix-archive](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-archive)
9797
* [gix-submodule](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-submodule)
98+
* [gix-status](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-status)
9899
* `gitoxide-core`
99100
* **very early** _(possibly without any documentation and many rough edges)_
100101
* [gix-date](https://github.com/Byron/gitoxide/blob/main/crate-status.md#gix-date)

crate-status.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,12 @@ Make it the best-performing implementation and the most convenient one.
453453
### gix-glob
454454
* [x] parse pattern
455455
* [x] a type for pattern matching of paths and non-paths, optionally case-insensitively.
456+
457+
### gix-status
458+
* [x] differences between index and worktree to turn index into worktree
459+
* [ ] differences between tree and index to turn tree into index
460+
* [ ] untracked files
461+
* [ ] fast answer to 'is it dirty'.
456462

457463
### gix-worktree
458464
* handle the working **tree/checkout**
@@ -463,18 +469,17 @@ Make it the best-performing implementation and the most convenient one.
463469
- [ ] handle sparse index
464470
- [x] linear scaling with multi-threading up to IO saturation
465471
- supported attributes to affect working tree and index contents
466-
- [ ] eol
467-
- [ ] working-tree-encoding
472+
- [x] eol
473+
- [x] working-tree-encoding
468474
- …more
469475
- **filtering**
470-
- [ ] `text`
471-
- [ ] `ident`
472-
- [ ] filter processes
473-
- [ ] single-invocation clean/smudge filters
474-
* manage multiple worktrees
476+
- [x] `text`
477+
- [x] `ident`
478+
- [x] filter processes
479+
- [x] single-invocation clean/smudge filters
475480
* access to per-path information, like `.gitignore` and `.gitattributes` in a manner well suited for efficient lookups
476481
* [x] _exclude_ information
477-
* [ ] attributes
482+
* [x] attributes
478483

479484
### gix-revision
480485
* [x] `describe()` (similar to `git name-rev`)
File renamed without changes.

gix-status/Cargo.toml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[package]
2+
name = "gix-status"
3+
version = "0.1.0"
4+
repository = "https://github.com/Byron/gitoxide"
5+
license = "MIT OR Apache-2.0"
6+
description = "A crate of the gitoxide project dealing with 'git status'-like functionality"
7+
authors = ["Sebastian Thiel <[email protected]>", "Pascal Kuthe <[email protected]>"]
8+
edition = "2021"
9+
include = ["src/**/*", "LICENSE-*", "CHANGELOG.md"]
10+
rust-version = "1.65"
11+
12+
[lib]
13+
doctest = false
14+
15+
[[test]]
16+
name = "multi-threaded"
17+
path = "tests/status-multi-threaded.rs"
18+
required-features = ["internal-testing-gix-features-parallel"]
19+
20+
[features]
21+
internal-testing-gix-features-parallel = ["gix-features/parallel"]
22+
23+
[dependencies]
24+
gix-index = { version = "^0.22.0", path = "../gix-index" }
25+
gix-fs = { version = "^0.4.1", path = "../gix-fs" }
26+
gix-hash = { version = "^0.11.4", path = "../gix-hash" }
27+
gix-object = { version = "^0.34.0", path = "../gix-object" }
28+
gix-path = { version = "^0.8.4", path = "../gix-path" }
29+
gix-features = { version = "^0.32.1", path = "../gix-features" }
30+
31+
thiserror = "1.0.26"
32+
filetime = "0.2.15"
33+
bstr = { version = "1.3.0", default-features = false }
34+
35+
[dev-dependencies]
36+
gix-testtools = { path = "../tests/tools" }
37+

gix-status/LICENSE-APACHE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-APACHE

gix-status/LICENSE-MIT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../LICENSE-MIT
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use gix_hash::ObjectId;
2+
use gix_index as index;
3+
use index::Entry;
4+
5+
/// Compares the content of two blobs in some way.
6+
pub trait CompareBlobs {
7+
/// Output data produced by [`compare_blobs()`][CompareBlobs::compare_blobs()].
8+
type Output;
9+
10+
/// Providing the underlying index `entry`, allow comparing a file in the worktree of size `worktree_blob_size`
11+
/// and allow reading its bytes using `worktree_blob`.
12+
/// If this function returns `None` the `entry` and the `worktree_blob` are assumed to be identical.
13+
/// Use `entry_blob` to obtain the data for the blob referred to by `entry`, allowing comparisons of the data itself.
14+
fn compare_blobs<'a, E>(
15+
&mut self,
16+
entry: &'a gix_index::Entry,
17+
worktree_blob_size: usize,
18+
worktree_blob: impl ReadDataOnce<'a, E>,
19+
entry_blob: impl ReadDataOnce<'a, E>,
20+
) -> Result<Option<Self::Output>, E>;
21+
}
22+
23+
/// Lazy borrowed access to blob data.
24+
pub trait ReadDataOnce<'a, E> {
25+
/// Returns the contents of this blob.
26+
///
27+
/// This potentially performs IO and other expensive operations
28+
/// and should only be called when necessary.
29+
fn read_data(self) -> Result<&'a [u8], E>;
30+
}
31+
32+
/// Compares to blobs by comparing their size and oid, and only looks at the file if
33+
/// the size matches, therefore it's very fast.
34+
#[derive(Clone)]
35+
pub struct FastEq;
36+
37+
impl CompareBlobs for FastEq {
38+
type Output = ();
39+
40+
fn compare_blobs<'a, E>(
41+
&mut self,
42+
entry: &'a Entry,
43+
worktree_blob_size: usize,
44+
worktree_blob: impl ReadDataOnce<'a, E>,
45+
_entry_blob: impl ReadDataOnce<'a, E>,
46+
) -> Result<Option<Self::Output>, E> {
47+
// make sure to account for racily smudged entries here so that they don't always keep
48+
// showing up as modified even after their contents have changed again, to a potentially
49+
// unmodified state. That means that we want to ignore stat.size == 0 for non_empty_blobs.
50+
if entry.stat.size as usize != worktree_blob_size && (entry.id.is_empty_blob() || entry.stat.size != 0) {
51+
return Ok(Some(()));
52+
}
53+
let blob = worktree_blob.read_data()?;
54+
let file_hash = gix_object::compute_hash(entry.id.kind(), gix_object::Kind::Blob, blob);
55+
Ok((entry.id != file_hash).then_some(()))
56+
}
57+
}
58+
59+
/// Compares files to blobs by *always* comparing their hashes.
60+
///
61+
/// Same as [`FastEq`] but does not contain a fast path for files with mismatched files and
62+
/// therefore always returns an OID that can be reused later.
63+
#[derive(Clone)]
64+
pub struct HashEq;
65+
66+
impl CompareBlobs for HashEq {
67+
type Output = ObjectId;
68+
69+
fn compare_blobs<'a, E>(
70+
&mut self,
71+
entry: &'a Entry,
72+
_worktree_blob_size: usize,
73+
worktree_blob: impl ReadDataOnce<'a, E>,
74+
_entry_blob: impl ReadDataOnce<'a, E>,
75+
) -> Result<Option<Self::Output>, E> {
76+
let blob = worktree_blob.read_data()?;
77+
let file_hash = gix_object::compute_hash(entry.id.kind(), gix_object::Kind::Blob, blob);
78+
Ok((entry.id != file_hash).then_some(file_hash))
79+
}
80+
}

0 commit comments

Comments
 (0)