Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0e55fbb

Browse files
committed
frame for traversing tree entries (#301)
1 parent d16821a commit 0e55fbb

7 files changed

Lines changed: 311 additions & 89 deletions

File tree

git-repository/examples/stats.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
#![allow(unused)]
2-
3-
use git_odb::FindExt;
41
use git_repository as git;
52
use git_repository::Reference;
63

74
fn main() -> Result<(), Box<dyn std::error::Error>> {
8-
let repo = git::discover(".")?;
5+
let repo = git::discover(".")?.apply_environment();
96
println!(
107
"Repo: {}",
118
repo.work_tree().as_deref().unwrap_or(repo.git_dir()).display()
@@ -63,8 +60,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
6360
}
6461

6562
mod visit {
66-
use std::process::id;
67-
6863
use git_hash::oid;
6964
use git_object::{bstr::BStr, tree::EntryRef};
7065
use git_repository as git;
@@ -102,13 +97,13 @@ mod visit {
10297
impl git_traverse::tree::Visit for Tree {
10398
fn pop_front_tracked_path_and_set_current(&mut self) {}
10499

105-
fn push_back_tracked_path_component(&mut self, component: &BStr) {}
100+
fn push_back_tracked_path_component(&mut self, _component: &BStr) {}
106101

107-
fn push_path_component(&mut self, component: &BStr) {}
102+
fn push_path_component(&mut self, _component: &BStr) {}
108103

109104
fn pop_path_component(&mut self) {}
110105

111-
fn visit_tree(&mut self, entry: &EntryRef<'_>) -> Action {
106+
fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> Action {
112107
self.num_trees += 1;
113108
Action::Continue
114109
}

git-repository/src/object/tree.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,44 @@ impl<'a, 'repo> Traversal<'a, 'repo> {
107107
)
108108
}
109109
}
110+
111+
pub use iter::EntryRef;
112+
113+
///
114+
mod iter {
115+
use super::Tree;
116+
use crate::Repository;
117+
118+
/// An entry within a tree
119+
pub struct EntryRef<'repo, 'a> {
120+
/// The actual entry ref we are wrapping.
121+
pub inner: git_object::tree::EntryRef<'a>,
122+
123+
repo: &'repo Repository,
124+
}
125+
126+
impl<'repo, 'a> EntryRef<'repo, 'a> {
127+
/// The kind of object to which [`id()`][Self::id()] is pointing.
128+
pub fn mode(&self) -> git_object::tree::EntryMode {
129+
self.inner.mode
130+
}
131+
132+
/// The name of the file in the parent tree.
133+
pub fn filename(&self) -> &git_object::bstr::BStr {
134+
self.inner.filename
135+
}
136+
137+
/// Return the entries id, connected to the underlying repository.
138+
pub fn id(&self) -> crate::Id<'repo> {
139+
crate::Id::from_id(self.inner.oid, self.repo)
140+
}
141+
}
142+
143+
impl<'repo> Tree<'repo> {
144+
/// Return an iterator over tree entries.
145+
pub fn iter(&self) -> impl Iterator<Item = Result<EntryRef<'repo, '_>, git_object::decode::Error>> {
146+
let repo = self.repo;
147+
git_object::TreeRefIter::from_bytes(&self.data).map(move |e| e.map(|entry| EntryRef { inner: entry, repo }))
148+
}
149+
}
150+
}

gitoxide-core/src/repository.rs

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -7,78 +7,6 @@ pub fn init(directory: Option<PathBuf>) -> Result<git_repository::Path> {
77
.with_context(|| "Repository initialization failed")
88
}
99

10-
pub mod verify {
11-
use std::{path::PathBuf, sync::atomic::AtomicBool};
10+
pub mod tree;
1211

13-
use git_repository as git;
14-
use git_repository::Progress;
15-
16-
use crate::{pack, OutputFormat};
17-
18-
/// A general purpose context for many operations provided here
19-
pub struct Context {
20-
/// If set, provide statistics to `out` in the given format
21-
pub output_statistics: Option<OutputFormat>,
22-
/// If set, don't use more than this amount of threads.
23-
/// Otherwise, usually use as many threads as there are logical cores.
24-
/// A value of 0 is interpreted as no-limit
25-
pub thread_limit: Option<usize>,
26-
pub verify_mode: pack::verify::Mode,
27-
pub algorithm: pack::verify::Algorithm,
28-
}
29-
30-
pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3;
31-
32-
pub fn integrity(
33-
repo: PathBuf,
34-
mut out: impl std::io::Write,
35-
progress: impl Progress,
36-
should_interrupt: &AtomicBool,
37-
Context {
38-
output_statistics,
39-
thread_limit,
40-
verify_mode,
41-
algorithm,
42-
}: Context,
43-
) -> anyhow::Result<()> {
44-
let repo = git_repository::open(repo)?;
45-
#[cfg_attr(not(feature = "serde1"), allow(unused))]
46-
let mut outcome = repo.objects.store_ref().verify_integrity(
47-
progress,
48-
should_interrupt,
49-
git_repository::odb::pack::index::verify::integrity::Options {
50-
verify_mode,
51-
traversal: algorithm.into(),
52-
thread_limit,
53-
// TODO: a way to get the pack cache from a handle
54-
make_pack_lookup_cache: || git_repository::odb::pack::cache::Never,
55-
},
56-
)?;
57-
// TODO: make this work for indices in multiple workspaces, once we have workspace support
58-
if let Some(index) = repo.load_index().transpose()? {
59-
index.verify_integrity()?;
60-
index.verify_entries()?;
61-
index.verify_extensions(true, {
62-
use git::odb::FindExt;
63-
let objects = repo.objects;
64-
move |oid, buf: &mut Vec<u8>| objects.find_tree_iter(oid, buf).ok()
65-
})?;
66-
outcome.progress.info(format!("Index at '{}' OK", index.path.display()));
67-
}
68-
match output_statistics {
69-
Some(OutputFormat::Human) => writeln!(out, "Human output is currently unsupported, use JSON instead")?,
70-
#[cfg(feature = "serde1")]
71-
Some(OutputFormat::Json) => {
72-
serde_json::to_writer_pretty(
73-
out,
74-
&serde_json::json!({
75-
"index_statistics" : outcome.index_statistics,
76-
"loose_object-stores" : outcome.loose_object_stores
77-
}),
78-
)?;
79-
}
80-
None => {}
81-
}
82-
Ok(())
83-
}
84-
}
12+
pub mod verify;
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use anyhow::bail;
2+
use std::io;
3+
use std::path::PathBuf;
4+
5+
use crate::OutputFormat;
6+
use git_repository as git;
7+
use git_repository::prelude::ObjectIdExt;
8+
9+
mod entries {
10+
use git_repository as git;
11+
12+
use git::hash::oid;
13+
use git::objs::{bstr::BStr, tree::EntryRef};
14+
use git::traverse::tree::visit::Action;
15+
16+
pub struct Traverse {
17+
pub num_trees: usize,
18+
pub num_links: usize,
19+
pub num_blobs: usize,
20+
pub num_blobs_exec: usize,
21+
pub num_submodules: usize,
22+
pub num_bytes: u64,
23+
pub repo: git::Repository,
24+
}
25+
26+
impl Traverse {
27+
pub fn new(repo: git::Repository) -> Self {
28+
Traverse {
29+
num_trees: 0,
30+
num_links: 0,
31+
num_blobs: 0,
32+
num_blobs_exec: 0,
33+
num_submodules: 0,
34+
num_bytes: 0,
35+
repo,
36+
}
37+
}
38+
39+
pub(crate) fn count_bytes(&mut self, oid: &oid) {
40+
if let Ok(obj) = self.repo.find_object(oid) {
41+
self.num_bytes += obj.data.len() as u64;
42+
}
43+
}
44+
}
45+
46+
impl git::traverse::tree::Visit for Traverse {
47+
fn pop_front_tracked_path_and_set_current(&mut self) {}
48+
49+
fn push_back_tracked_path_component(&mut self, _component: &BStr) {}
50+
51+
fn push_path_component(&mut self, _component: &BStr) {}
52+
53+
fn pop_path_component(&mut self) {}
54+
55+
fn visit_tree(&mut self, _entry: &EntryRef<'_>) -> Action {
56+
self.num_trees += 1;
57+
Action::Continue
58+
}
59+
60+
fn visit_nontree(&mut self, entry: &EntryRef<'_>) -> Action {
61+
use git::objs::tree::EntryMode::*;
62+
match entry.mode {
63+
Commit => self.num_submodules += 1,
64+
Blob => {
65+
self.count_bytes(entry.oid);
66+
self.num_blobs += 1
67+
}
68+
BlobExecutable => {
69+
self.count_bytes(entry.oid);
70+
self.num_blobs_exec += 1
71+
}
72+
Link => self.num_links += 1,
73+
Tree => unreachable!("BUG"),
74+
}
75+
Action::Continue
76+
}
77+
}
78+
}
79+
80+
pub fn entries(
81+
repository: PathBuf,
82+
treeish: Option<&str>,
83+
recursive: bool,
84+
extended: bool,
85+
format: OutputFormat,
86+
out: &mut dyn io::Write,
87+
_err: &mut dyn io::Write,
88+
) -> anyhow::Result<()> {
89+
if format == OutputFormat::Json {
90+
bail!("Only human output format is supported at the moment");
91+
}
92+
93+
let tree_repo = git::open(repository)?;
94+
let mut repo = tree_repo.clone().apply_environment();
95+
repo.object_cache_size(128 * 1024);
96+
97+
let tree = match treeish {
98+
Some(hex) => git::hash::ObjectId::from_hex(hex.as_bytes())
99+
.map(|id| id.attach(&repo))?
100+
.object()?
101+
.try_into_tree()?,
102+
None => repo.head()?.peel_to_commit_in_place()?.tree()?,
103+
};
104+
105+
if recursive {
106+
} else {
107+
for entry in tree.iter() {
108+
let entry = entry?;
109+
format_entry(
110+
&mut *out,
111+
&entry.inner,
112+
extended
113+
.then(|| entry.id().object().map(|o| o.data.len()))
114+
.transpose()?,
115+
)?;
116+
}
117+
}
118+
119+
let mut delegate = entries::Traverse::new(tree_repo);
120+
tree.traverse().breadthfirst(&mut delegate)?;
121+
Ok(())
122+
}
123+
124+
fn format_entry(
125+
mut _out: impl io::Write,
126+
_entry: &git::objs::tree::EntryRef<'_>,
127+
_size: Option<usize>,
128+
) -> std::io::Result<()> {
129+
todo!()
130+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use std::{path::PathBuf, sync::atomic::AtomicBool};
2+
3+
use git_repository as git;
4+
use git_repository::Progress;
5+
6+
use crate::{pack, OutputFormat};
7+
8+
/// A general purpose context for many operations provided here
9+
pub struct Context {
10+
/// If set, provide statistics to `out` in the given format
11+
pub output_statistics: Option<OutputFormat>,
12+
/// If set, don't use more than this amount of threads.
13+
/// Otherwise, usually use as many threads as there are logical cores.
14+
/// A value of 0 is interpreted as no-limit
15+
pub thread_limit: Option<usize>,
16+
pub verify_mode: pack::verify::Mode,
17+
pub algorithm: pack::verify::Algorithm,
18+
}
19+
20+
pub const PROGRESS_RANGE: std::ops::RangeInclusive<u8> = 1..=3;
21+
22+
pub fn integrity(
23+
repo: PathBuf,
24+
mut out: impl std::io::Write,
25+
progress: impl Progress,
26+
should_interrupt: &AtomicBool,
27+
Context {
28+
output_statistics,
29+
thread_limit,
30+
verify_mode,
31+
algorithm,
32+
}: Context,
33+
) -> anyhow::Result<()> {
34+
let repo = git_repository::open(repo)?;
35+
#[cfg_attr(not(feature = "serde1"), allow(unused))]
36+
let mut outcome = repo.objects.store_ref().verify_integrity(
37+
progress,
38+
should_interrupt,
39+
git_repository::odb::pack::index::verify::integrity::Options {
40+
verify_mode,
41+
traversal: algorithm.into(),
42+
thread_limit,
43+
// TODO: a way to get the pack cache from a handle
44+
make_pack_lookup_cache: || git_repository::odb::pack::cache::Never,
45+
},
46+
)?;
47+
// TODO: make this work for indices in multiple workspaces, once we have workspace support
48+
if let Some(index) = repo.load_index().transpose()? {
49+
index.verify_integrity()?;
50+
index.verify_entries()?;
51+
index.verify_extensions(true, {
52+
use git::odb::FindExt;
53+
let objects = repo.objects;
54+
move |oid, buf: &mut Vec<u8>| objects.find_tree_iter(oid, buf).ok()
55+
})?;
56+
outcome.progress.info(format!("Index at '{}' OK", index.path.display()));
57+
}
58+
match output_statistics {
59+
Some(OutputFormat::Human) => writeln!(out, "Human output is currently unsupported, use JSON instead")?,
60+
#[cfg(feature = "serde1")]
61+
Some(OutputFormat::Json) => {
62+
serde_json::to_writer_pretty(
63+
out,
64+
&serde_json::json!({
65+
"index_statistics" : outcome.index_statistics,
66+
"loose_object-stores" : outcome.loose_object_stores
67+
}),
68+
)?;
69+
}
70+
None => {}
71+
}
72+
Ok(())
73+
}

0 commit comments

Comments
 (0)