Skip to content

Commit c042813

Browse files
authored
Merge pull request #1833 from GitoxideLabs/improvements
various improvements
2 parents 7190b4f + 70ebd5f commit c042813

File tree

4 files changed

+143
-7
lines changed

4 files changed

+143
-7
lines changed

gix/src/filter.rs

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub mod pipeline {
1919
pub mod options {
2020
use crate::{bstr::BString, config};
2121

22-
/// The error returned by [Pipeline::options()][crate::filter::Pipeline::options()].
22+
/// The error returned by [Pipeline::options()](crate::filter::Pipeline::options()).
2323
#[derive(Debug, thiserror::Error)]
2424
#[allow(missing_docs)]
2525
pub enum Error {
@@ -39,7 +39,7 @@ pub mod pipeline {
3939

4040
///
4141
pub mod convert_to_git {
42-
/// The error returned by [Pipeline::convert_to_git()][crate::filter::Pipeline::convert_to_git()].
42+
/// The error returned by [Pipeline::convert_to_git()](crate::filter::Pipeline::convert_to_git()).
4343
#[derive(Debug, thiserror::Error)]
4444
#[allow(missing_docs)]
4545
pub enum Error {
@@ -52,7 +52,7 @@ pub mod pipeline {
5252

5353
///
5454
pub mod convert_to_worktree {
55-
/// The error returned by [Pipeline::convert_to_worktree()][crate::filter::Pipeline::convert_to_worktree()].
55+
/// The error returned by [Pipeline::convert_to_worktree()](crate::filter::Pipeline::convert_to_worktree()).
5656
#[derive(Debug, thiserror::Error)]
5757
#[allow(missing_docs)]
5858
pub enum Error {
@@ -62,6 +62,25 @@ pub mod pipeline {
6262
Convert(#[from] gix_filter::pipeline::convert::to_worktree::Error),
6363
}
6464
}
65+
66+
///
67+
pub mod worktree_file_to_object {
68+
use std::path::PathBuf;
69+
70+
/// The error returned by [Pipeline::worktree_file_to_object()](crate::filter::Pipeline::worktree_file_to_object()).
71+
#[derive(Debug, thiserror::Error)]
72+
#[allow(missing_docs)]
73+
pub enum Error {
74+
#[error("Cannot add worktree files in bare repositories")]
75+
MissingWorktree,
76+
#[error("Failed to perform IO for object creation for '{}'", path.display())]
77+
IO { source: std::io::Error, path: PathBuf },
78+
#[error(transparent)]
79+
WriteBlob(#[from] crate::object::write::Error),
80+
#[error(transparent)]
81+
ConvertToGit(#[from] crate::filter::pipeline::convert_to_git::Error),
82+
}
83+
}
6584
}
6685

6786
/// A git pipeline for transforming data *to-git* and *to-worktree*, based
@@ -70,7 +89,8 @@ pub mod pipeline {
7089
pub struct Pipeline<'repo> {
7190
inner: gix_filter::Pipeline,
7291
cache: gix_worktree::Stack,
73-
repo: &'repo Repository,
92+
/// The repository this pipeline is associated with.
93+
pub repo: &'repo Repository,
7494
}
7595

7696
/// Lifecycle
@@ -132,7 +152,7 @@ impl Pipeline<'_> {
132152
/// Convert a `src` stream (to be found at `rela_path`, a repo-relative path) to a representation suitable for storage in `git`
133153
/// by using all attributes at `rela_path` and configuration of the repository to know exactly which filters apply.
134154
/// `index` is used in particularly rare cases where the CRLF filter in auto-mode tries to determine whether to apply itself,
135-
/// and it should match the state used when [instantiating this instance][Self::new()].
155+
/// and it should match the state used when [instantiating this instance](Self::new()).
136156
/// Note that the return-type implements [`std::io::Read`].
137157
pub fn convert_to_git<R>(
138158
&mut self,
@@ -186,6 +206,65 @@ impl Pipeline<'_> {
186206
)?)
187207
}
188208

209+
/// Add the worktree file at `rela_path` to the object database and return its `(id, entry, symlink_metadata)` for use in a tree or in the index, for instance.
210+
///
211+
/// `index` is used in particularly rare cases where the CRLF filter in auto-mode tries to determine whether to apply itself,
212+
/// and it should match the state used when [instantiating this instance](Self::new()).
213+
///
214+
/// Return `Ok(None)` the file didn't exist in the worktree, or if it was of an untrackable type.
215+
pub fn worktree_file_to_object(
216+
&mut self,
217+
rela_path: &BStr,
218+
index: &gix_index::State,
219+
) -> Result<
220+
Option<(gix_hash::ObjectId, gix_object::tree::EntryKind, std::fs::Metadata)>,
221+
pipeline::worktree_file_to_object::Error,
222+
> {
223+
use pipeline::worktree_file_to_object::Error;
224+
225+
let rela_path_as_path = gix_path::from_bstr(rela_path);
226+
let repo = self.repo;
227+
let worktree_dir = repo.work_dir().ok_or(Error::MissingWorktree)?;
228+
let path = worktree_dir.join(&rela_path_as_path);
229+
let md = match std::fs::symlink_metadata(&path) {
230+
Ok(md) => md,
231+
Err(err) => {
232+
if gix_fs::io_err::is_not_found(err.kind(), err.raw_os_error()) {
233+
return Ok(None);
234+
} else {
235+
return Err(Error::IO { source: err, path });
236+
}
237+
}
238+
};
239+
let (id, kind) = if md.is_symlink() {
240+
let target = std::fs::read_link(&path).map_err(|source| Error::IO { source, path })?;
241+
let id = repo.write_blob(gix_path::into_bstr(target).as_ref())?;
242+
(id, gix_object::tree::EntryKind::Link)
243+
} else if md.is_file() {
244+
use gix_filter::pipeline::convert::ToGitOutcome;
245+
246+
let file = std::fs::File::open(&path).map_err(|source| Error::IO { source, path })?;
247+
let file_for_git = self.convert_to_git(file, rela_path_as_path.as_ref(), index)?;
248+
let id = match file_for_git {
249+
ToGitOutcome::Unchanged(mut file) => repo.write_blob_stream(&mut file)?,
250+
ToGitOutcome::Buffer(buf) => repo.write_blob(buf)?,
251+
ToGitOutcome::Process(mut read) => repo.write_blob_stream(&mut read)?,
252+
};
253+
254+
let kind = if gix_fs::is_executable(&md) {
255+
gix_object::tree::EntryKind::BlobExecutable
256+
} else {
257+
gix_object::tree::EntryKind::Blob
258+
};
259+
(id, kind)
260+
} else {
261+
// This is probably a type-change to something we can't track.
262+
return Ok(None);
263+
};
264+
265+
Ok(Some((id.detach(), kind, md)))
266+
}
267+
189268
/// Retrieve the static context that is made available to the process filters.
190269
///
191270
/// The context set here is relevant for the [`convert_to_git()`][Self::convert_to_git()] and

gix/tests/fixtures/generated-archives/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
/make_core_worktree_repo.tar
88
/make_signatures_repo.tar
99
/make_diff_repos.tar
10-
/make_submodule_with_worktree.tar
10+
/make_submodule_with_worktree.tar
11+
/repo_with_untracked_files.tar
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
set -eu -o pipefail
3+
4+
git init -q
5+
echo content >file
6+
ln -s file link
7+
8+
echo binary >exe && chmod +x exe
9+
mkfifo fifo

gix/tests/gix/repository/filter.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fn pipeline_in_nonbare_repo_without_index() -> crate::Result {
1010
use gix::bstr::ByteSlice;
1111
use gix_filter::driver::apply::Delay;
1212

13-
use crate::util::{named_repo, named_subrepo_opts};
13+
use crate::util::{hex_to_id, named_repo, named_subrepo_opts};
1414

1515
#[test]
1616
fn pipeline_in_repo_without_special_options() -> crate::Result {
@@ -31,6 +31,53 @@ fn pipeline_in_repo_without_special_options() -> crate::Result {
3131
Ok(())
3232
}
3333

34+
#[test]
35+
#[cfg(unix)]
36+
fn pipeline_worktree_file_to_object() -> crate::Result {
37+
let repo = named_repo("repo_with_untracked_files.sh")?;
38+
let (mut pipe, index) = repo.filter_pipeline(None)?;
39+
fn take_two<A, B, C>(t: Option<(A, B, C)>) -> Option<(A, B)> {
40+
t.map(|t| (t.0, t.1))
41+
}
42+
43+
assert_eq!(
44+
take_two(pipe.worktree_file_to_object("file".into(), &index)?),
45+
Some((
46+
hex_to_id("d95f3ad14dee633a758d2e331151e950dd13e4ed"),
47+
gix::object::tree::EntryKind::Blob
48+
))
49+
);
50+
assert_eq!(
51+
take_two(pipe.worktree_file_to_object("link".into(), &index)?),
52+
Some((
53+
hex_to_id("1a010b1c0f081b2e8901d55307a15c29ff30af0e"),
54+
gix::object::tree::EntryKind::Link
55+
))
56+
);
57+
assert_eq!(
58+
take_two(pipe.worktree_file_to_object("exe".into(), &index)?),
59+
Some((
60+
hex_to_id("a9128c283485202893f5af379dd9beccb6e79486"),
61+
gix::object::tree::EntryKind::BlobExecutable
62+
))
63+
);
64+
assert_eq!(
65+
take_two(pipe.worktree_file_to_object("missing".into(), &index)?),
66+
None,
67+
"Missing files are specifically typed and no error"
68+
);
69+
assert!(
70+
repo.work_dir().expect("non-bare").join("fifo").exists(),
71+
"there is a fifo"
72+
);
73+
assert_eq!(
74+
take_two(pipe.worktree_file_to_object("fifo".into(), &index)?),
75+
None,
76+
"untrackable entries are just ignored as if they didn't exist"
77+
);
78+
Ok(())
79+
}
80+
3481
#[test]
3582
fn pipeline_with_autocrlf() -> crate::Result {
3683
let repo = named_repo("make_config_repo.sh")?;

0 commit comments

Comments
 (0)