Struct git_repository::Object
source · pub struct Object<'repo> {
pub id: ObjectId,
pub kind: Kind,
pub data: Vec<u8>,
/* private fields */
}
Expand description
A decoded object with a reference to its owning repository.
Fields§
§id: ObjectId
The id of the object
kind: Kind
The kind of the object
data: Vec<u8>
The fully decoded object data
Implementations§
source§impl<'repo> Object<'repo>
impl<'repo> Object<'repo>
sourcepub fn peel_to_kind(self, kind: Kind) -> Result<Self, Error>
pub fn peel_to_kind(self, kind: Kind) -> Result<Self, Error>
Follow tags to their target and commits to trees until the given kind
of object is encountered.
Note that this object doesn’t necessarily have to be the end of the chain.
Typical values are Kind::Commit
or Kind::Tree
.
Examples found in repository?
More examples
sourcepub fn peel_to_tree(self) -> Result<Tree<'repo>, Error>
pub fn peel_to_tree(self) -> Result<Tree<'repo>, Error>
Peel this object into a tree and return it, if this is possible.
Examples found in repository?
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
pub fn main_worktree(
&mut self,
mut progress: impl crate::Progress,
should_interrupt: &AtomicBool,
) -> Result<(Repository, git_worktree::index::checkout::Outcome), Error> {
let repo = self
.repo
.as_ref()
.expect("still present as we never succeeded the worktree checkout yet");
let workdir = repo.work_dir().ok_or_else(|| Error::BareRepository {
git_dir: repo.git_dir().to_owned(),
})?;
let root_tree = match repo.head()?.peel_to_id_in_place().transpose()? {
Some(id) => id.object().expect("downloaded from remote").peel_to_tree()?.id,
None => {
return Ok((
self.repo.take().expect("still present"),
git_worktree::index::checkout::Outcome::default(),
))
}
};
let index = git_index::State::from_tree(&root_tree, |oid, buf| repo.objects.find_tree_iter(oid, buf).ok())
.map_err(|err| Error::IndexFromTree {
id: root_tree,
source: err,
})?;
let mut index = git_index::File::from_state(index, repo.index_path());
let mut opts = repo.config.checkout_options(repo.git_dir())?;
opts.destination_is_initially_empty = true;
let mut files = progress.add_child_with_id("checkout", *b"CLCF"); /* CLone Checkout Files */
let mut bytes = progress.add_child_with_id("writing", *b"CLCB") /* CLone Checkout Bytes */;
files.init(Some(index.entries().len()), crate::progress::count("files"));
bytes.init(None, crate::progress::bytes());
let start = std::time::Instant::now();
let outcome = git_worktree::index::checkout(
&mut index,
workdir,
{
let objects = repo.objects.clone().into_arc()?;
move |oid, buf| objects.find_blob(oid, buf)
},
&mut files,
&mut bytes,
should_interrupt,
opts,
)?;
files.show_throughput(start);
bytes.show_throughput(start);
index.write(Default::default())?;
Ok((self.repo.take().expect("still present"), outcome))
}
Follow all tag object targets until a commit, tree or blob is reached.
Note that this method is different from peel_to_kind(…)
as it won’t
peel commits to their tree, but handles tags only.
Examples found in repository?
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
pub fn peel_to_id_in_place(&mut self) -> Option<Result<crate::Id<'repo>, Error>> {
Some(match &mut self.kind {
Kind::Unborn(_name) => return None,
Kind::Detached {
peeled: Some(peeled), ..
} => Ok((*peeled).attach(self.repo)),
Kind::Detached { peeled: None, target } => {
match target
.attach(self.repo)
.object()
.map_err(Into::into)
.and_then(|obj| obj.peel_tags_to_end().map_err(Into::into))
.map(|peeled| peeled.id)
{
Ok(peeled) => {
self.kind = Kind::Detached {
peeled: Some(peeled),
target: *target,
};
Ok(peeled.attach(self.repo))
}
Err(err) => Err(err),
}
}
Kind::Symbolic(r) => {
let mut nr = r.clone().attach(self.repo);
let peeled = nr.peel_to_id_in_place().map_err(Into::into);
*r = nr.detach();
peeled
}
})
}
// TODO: tests
// TODO: something similar in `crate::Reference`
/// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no
/// more object to follow, transform the id into a commit if possible and return that.
///
/// Returns an error if the head is unborn or if it doesn't point to a commit.
pub fn peel_to_commit_in_place(&mut self) -> Result<crate::Commit<'repo>, to_commit::Error> {
let id = self.peel_to_id_in_place().ok_or_else(|| to_commit::Error::Unborn {
name: self.referent_name().expect("unborn").to_owned(),
})??;
id.object()
.map_err(|err| to_commit::Error::Peel(Error::FindExistingObject(err)))
.and_then(|object| object.try_into_commit().map_err(Into::into))
}
/// Consume this instance and transform it into the final object that it points to, or `None` if the `HEAD`
/// reference is yet to be born.
pub fn into_fully_peeled_id(self) -> Option<Result<crate::Id<'repo>, Error>> {
Some(match self.kind {
Kind::Unborn(_name) => return None,
Kind::Detached {
peeled: Some(peeled), ..
} => Ok(peeled.attach(self.repo)),
Kind::Detached { peeled: None, target } => target
.attach(self.repo)
.object()
.map_err(Into::into)
.and_then(|obj| obj.peel_tags_to_end().map_err(Into::into))
.map(|obj| obj.id.attach(self.repo)),
Kind::Symbolic(r) => r.attach(self.repo).peel_to_id_in_place().map_err(Into::into),
})
}
More examples
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()> {
self.unset_disambiguate_call();
self.follow_refs_to_objects_if_needed()?;
let mut replacements = Replacements::default();
let mut errors = Vec::new();
let objs = self.objs[self.idx].as_mut()?;
let repo = self.repo;
match kind {
PeelTo::ValidObject => {
for obj in objs.iter() {
match repo.find_object(*obj) {
Ok(_) => {}
Err(err) => {
errors.push((*obj, err.into()));
}
};
}
}
PeelTo::ObjectKind(kind) => {
let peel = |obj| peel(repo, obj, kind);
for obj in objs.iter() {
match peel(obj) {
Ok(replace) => replacements.push((*obj, replace)),
Err(err) => errors.push((*obj, err)),
}
}
}
PeelTo::Path(path) => {
let lookup_path = |obj: &ObjectId| {
let tree_id = peel(repo, obj, git_object::Kind::Tree)?;
if path.is_empty() {
return Ok(tree_id);
}
let tree = repo.find_object(tree_id)?.into_tree();
let entry =
tree.lookup_entry_by_path(git_path::from_bstr(path))?
.ok_or_else(|| Error::PathNotFound {
path: path.into(),
object: obj.attach(repo).shorten_or_id(),
tree: tree_id.attach(repo).shorten_or_id(),
})?;
Ok(entry.object_id())
};
for obj in objs.iter() {
match lookup_path(obj) {
Ok(replace) => replacements.push((*obj, replace)),
Err(err) => errors.push((*obj, err)),
}
}
}
PeelTo::RecursiveTagObject => {
for oid in objs.iter() {
match oid.attach(repo).object().and_then(|obj| obj.peel_tags_to_end()) {
Ok(obj) => replacements.push((*oid, obj.id)),
Err(err) => errors.push((*oid, err.into())),
}
}
}
}
handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements)
}
source§impl<'repo> Object<'repo>
impl<'repo> Object<'repo>
Consuming conversions to attached object kinds.
sourcepub fn into_tree(self) -> Tree<'repo>
pub fn into_tree(self) -> Tree<'repo>
Transform this object into a tree, or panic if it is none.
Examples found in repository?
More examples
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()> {
self.unset_disambiguate_call();
self.follow_refs_to_objects_if_needed()?;
let mut replacements = Replacements::default();
let mut errors = Vec::new();
let objs = self.objs[self.idx].as_mut()?;
let repo = self.repo;
match kind {
PeelTo::ValidObject => {
for obj in objs.iter() {
match repo.find_object(*obj) {
Ok(_) => {}
Err(err) => {
errors.push((*obj, err.into()));
}
};
}
}
PeelTo::ObjectKind(kind) => {
let peel = |obj| peel(repo, obj, kind);
for obj in objs.iter() {
match peel(obj) {
Ok(replace) => replacements.push((*obj, replace)),
Err(err) => errors.push((*obj, err)),
}
}
}
PeelTo::Path(path) => {
let lookup_path = |obj: &ObjectId| {
let tree_id = peel(repo, obj, git_object::Kind::Tree)?;
if path.is_empty() {
return Ok(tree_id);
}
let tree = repo.find_object(tree_id)?.into_tree();
let entry =
tree.lookup_entry_by_path(git_path::from_bstr(path))?
.ok_or_else(|| Error::PathNotFound {
path: path.into(),
object: obj.attach(repo).shorten_or_id(),
tree: tree_id.attach(repo).shorten_or_id(),
})?;
Ok(entry.object_id())
};
for obj in objs.iter() {
match lookup_path(obj) {
Ok(replace) => replacements.push((*obj, replace)),
Err(err) => errors.push((*obj, err)),
}
}
}
PeelTo::RecursiveTagObject => {
for oid in objs.iter() {
match oid.attach(repo).object().and_then(|obj| obj.peel_tags_to_end()) {
Ok(obj) => replacements.push((*oid, obj.id)),
Err(err) => errors.push((*oid, err.into())),
}
}
}
}
handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements)
}
sourcepub fn into_commit(self) -> Commit<'repo>
pub fn into_commit(self) -> Commit<'repo>
Transform this object into a commit, or panic if it is none.
Examples found in repository?
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
fn find(&mut self, regex: &BStr, negated: bool) -> Option<()> {
self.unset_disambiguate_call();
self.follow_refs_to_objects_if_needed()?;
#[cfg(not(feature = "regex"))]
let matches = |message: &BStr| -> bool { message.contains_str(regex) ^ negated };
#[cfg(feature = "regex")]
let matches = match regex::bytes::Regex::new(regex.to_str_lossy().as_ref()) {
Ok(compiled) => {
let needs_regex = regex::escape(compiled.as_str()) != regex;
move |message: &BStr| -> bool {
if needs_regex {
compiled.is_match(message) ^ negated
} else {
message.contains_str(regex) ^ negated
}
}
}
Err(err) => {
self.err.push(err.into());
return None;
}
};
match self.objs[self.idx].as_mut() {
Some(objs) => {
let repo = self.repo;
let mut errors = Vec::new();
let mut replacements = Replacements::default();
for oid in objs.iter() {
match oid
.attach(repo)
.ancestors()
.sorting(Sorting::ByCommitTimeNewestFirst)
.all()
{
Ok(iter) => {
let mut matched = false;
let mut count = 0;
let commits = iter.map(|res| {
res.map_err(Error::from).and_then(|commit_id| {
commit_id.object().map_err(Error::from).map(|obj| obj.into_commit())
})
});
for commit in commits {
count += 1;
match commit {
Ok(commit) => {
if matches(commit.message_raw_sloppy()) {
replacements.push((*oid, commit.id));
matched = true;
break;
}
}
Err(err) => errors.push((*oid, err)),
}
}
if !matched {
errors.push((
*oid,
Error::NoRegexMatch {
regex: regex.into(),
commits_searched: count,
oid: oid.attach(repo).shorten_or_id(),
},
))
}
}
Err(err) => errors.push((*oid, err.into())),
}
}
handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements)
}
None => match self.repo.references() {
Ok(references) => match references.all() {
Ok(references) => {
match self
.repo
.rev_walk(
references
.peeled()
.filter_map(Result::ok)
.filter(|r| {
r.id()
.object()
.ok()
.map(|obj| obj.kind == git_object::Kind::Commit)
.unwrap_or(false)
})
.filter_map(|r| r.detach().peeled),
)
.sorting(Sorting::ByCommitTimeNewestFirst)
.all()
{
Ok(iter) => {
let mut matched = false;
let mut count = 0;
let commits = iter.map(|res| {
res.map_err(Error::from).and_then(|commit_id| {
commit_id.object().map_err(Error::from).map(|obj| obj.into_commit())
})
});
for commit in commits {
count += 1;
match commit {
Ok(commit) => {
if matches(commit.message_raw_sloppy()) {
self.objs[self.idx]
.get_or_insert_with(HashSet::default)
.insert(commit.id);
matched = true;
break;
}
}
Err(err) => self.err.push(err),
}
}
if matched {
Some(())
} else {
self.err.push(Error::NoRegexMatchAllRefs {
regex: regex.into(),
commits_searched: count,
});
None
}
}
Err(err) => {
self.err.push(err.into());
None
}
}
}
Err(err) => {
self.err.push(err.into());
None
}
},
Err(err) => {
self.err.push(err.into());
None
}
},
}
}
sourcepub fn try_into_commit(self) -> Result<Commit<'repo>, Error>
pub fn try_into_commit(self) -> Result<Commit<'repo>, Error>
Transform this object into a commit, or return it as part of the Err
if it is no commit.
Examples found in repository?
More examples
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
fn traverse(&mut self, kind: Traversal) -> Option<()> {
self.unset_disambiguate_call();
self.follow_refs_to_objects_if_needed()?;
let mut replacements = Replacements::default();
let mut errors = Vec::new();
let objs = self.objs[self.idx].as_mut()?;
let repo = self.repo;
for obj in objs.iter() {
match kind {
Traversal::NthParent(num) => {
match self.repo.find_object(*obj).map_err(Error::from).and_then(|obj| {
obj.try_into_commit().map_err(|err| {
let object::try_into::Error { actual, expected, id } = err;
Error::ObjectKind {
oid: id.attach(repo).shorten_or_id(),
actual,
expected,
}
})
}) {
Ok(commit) => match commit.parent_ids().nth(num.saturating_sub(1)) {
Some(id) => replacements.push((commit.id, id.detach())),
None => errors.push((
commit.id,
Error::ParentOutOfRange {
oid: commit.id().shorten_or_id(),
desired: num,
available: commit.parent_ids().count(),
},
)),
},
Err(err) => errors.push((*obj, err)),
}
}
Traversal::NthAncestor(num) => {
let id = obj.attach(repo);
match id
.ancestors()
.first_parent_only()
.all()
.expect("cannot fail without sorting")
.skip(num)
.filter_map(Result::ok)
.next()
{
Some(id) => replacements.push((*obj, id.detach())),
None => errors.push((
*obj,
Error::AncestorOutOfRange {
oid: id.shorten_or_id(),
desired: num,
available: id
.ancestors()
.first_parent_only()
.all()
.expect("cannot fail without sorting")
.skip(1)
.count(),
},
)),
}
}
}
}
handle_errors_and_replacements(&mut self.err, objs, errors, &mut replacements)
}
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
fn names(&self, repo: &Repository) -> Result<HashMap<ObjectId, Cow<'static, BStr>>, Error> {
let platform = repo.references()?;
Ok(match self {
SelectRef::AllTags | SelectRef::AllRefs => {
let mut refs: Vec<_> = match self {
SelectRef::AllRefs => platform.all()?,
SelectRef::AllTags => platform.tags()?,
_ => unreachable!(),
}
.filter_map(Result::ok)
.filter_map(|mut r: crate::Reference<'_>| {
let target_id = r.target().try_id().map(ToOwned::to_owned);
let peeled_id = r.peel_to_id_in_place().ok()?;
let (prio, tag_time) = match target_id {
Some(target_id) if peeled_id != *target_id => {
let tag = repo.find_object(target_id).ok()?.try_into_tag().ok()?;
(1, tag.tagger().ok()??.time.seconds_since_unix_epoch)
}
_ => (0, 0),
};
(
peeled_id.inner,
prio,
tag_time,
Cow::from(r.inner.name.shorten().to_owned()),
)
.into()
})
.collect();
// By priority, then by time ascending, then lexicographically.
// More recent entries overwrite older ones due to collection into hashmap.
refs.sort_by(
|(_a_peeled_id, a_prio, a_time, a_name), (_b_peeled_id, b_prio, b_time, b_name)| {
a_prio
.cmp(b_prio)
.then_with(|| a_time.cmp(b_time))
.then_with(|| b_name.cmp(a_name))
},
);
refs.into_iter().map(|(a, _, _, b)| (a, b)).collect()
}
SelectRef::AnnotatedTags => {
let mut peeled_commits_and_tag_date: Vec<_> = platform
.tags()?
.filter_map(Result::ok)
.filter_map(|r: crate::Reference<'_>| {
// TODO: we assume direct refs for tags, which is the common case, but it doesn't have to be
// so rather follow symrefs till the first object and then peel tags after the first object was found.
let tag = r.try_id()?.object().ok()?.try_into_tag().ok()?;
let tag_time = tag
.tagger()
.ok()
.and_then(|s| s.map(|s| s.time.seconds_since_unix_epoch))
.unwrap_or(0);
let commit_id = tag.target_id().ok()?.object().ok()?.try_into_commit().ok()?.id;
Some((commit_id, tag_time, Cow::<BStr>::from(r.name().shorten().to_owned())))
})
.collect();
// Sort by time ascending, then lexicographically.
// More recent entries overwrite older ones due to collection into hashmap.
peeled_commits_and_tag_date.sort_by(|(_a_id, a_time, a_name), (_b_id, b_time, b_name)| {
a_time.cmp(b_time).then_with(|| b_name.cmp(a_name))
});
peeled_commits_and_tag_date
.into_iter()
.map(|(a, _, c)| (a, c))
.collect()
}
})
}
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
pub(crate) fn update(
repo: &Repository,
message: RefLogMessage,
mappings: &[fetch::Mapping],
refspecs: &[git_refspec::RefSpec],
extra_refspecs: &[git_refspec::RefSpec],
fetch_tags: fetch::Tags,
dry_run: fetch::DryRun,
write_packed_refs: fetch::WritePackedRefs,
) -> Result<update::Outcome, update::Error> {
let mut edits = Vec::new();
let mut updates = Vec::new();
let implicit_tag_refspec = fetch_tags
.to_refspec()
.filter(|_| matches!(fetch_tags, crate::remote::fetch::Tags::Included));
for (remote, local, spec, is_implicit_tag) in mappings.iter().filter_map(
|fetch::Mapping {
remote,
local,
spec_index,
}| {
spec_index.get(refspecs, extra_refspecs).map(|spec| {
(
remote,
local,
spec,
implicit_tag_refspec.map_or(false, |tag_spec| spec.to_ref() == tag_spec),
)
})
},
) {
let remote_id = match remote.as_id() {
Some(id) => id,
None => continue,
};
if dry_run == fetch::DryRun::No && !repo.objects.contains(remote_id) {
let update = if is_implicit_tag {
update::Mode::ImplicitTagNotSentByRemote.into()
} else {
update::Mode::RejectedSourceObjectNotFound { id: remote_id.into() }.into()
};
updates.push(update);
continue;
}
let checked_out_branches = worktree_branches(repo)?;
let (mode, edit_index) = match local {
Some(name) => {
let (mode, reflog_message, name, previous_value) = match repo.try_find_reference(name)? {
Some(existing) => {
if let Some(wt_dir) = checked_out_branches.get(existing.name()) {
let mode = update::Mode::RejectedCurrentlyCheckedOut {
worktree_dir: wt_dir.to_owned(),
};
updates.push(mode.into());
continue;
}
match existing.target() {
TargetRef::Symbolic(_) => {
updates.push(update::Mode::RejectedSymbolic.into());
continue;
}
TargetRef::Peeled(local_id) => {
let previous_value =
PreviousValue::MustExistAndMatch(Target::Peeled(local_id.to_owned()));
let (mode, reflog_message) = if local_id == remote_id {
(update::Mode::NoChangeNeeded, "no update will be performed")
} else if let Some(git_ref::Category::Tag) = existing.name().category() {
if spec.allow_non_fast_forward() {
(update::Mode::Forced, "updating tag")
} else {
updates.push(update::Mode::RejectedTagUpdate.into());
continue;
}
} else {
let mut force = spec.allow_non_fast_forward();
let is_fast_forward = match dry_run {
fetch::DryRun::No => {
let ancestors = repo
.find_object(local_id)?
.try_into_commit()
.map_err(|_| ())
.and_then(|c| {
c.committer().map(|a| a.time.seconds_since_unix_epoch).map_err(|_| ())
}).and_then(|local_commit_time|
remote_id
.to_owned()
.ancestors(|id, buf| repo.objects.find_commit_iter(id, buf))
.sorting(
git_traverse::commit::Sorting::ByCommitTimeNewestFirstCutoffOlderThan {
time_in_seconds_since_epoch: local_commit_time
},
)
.map_err(|_| ())
);
match ancestors {
Ok(mut ancestors) => {
ancestors.any(|cid| cid.map_or(false, |cid| cid == local_id))
}
Err(_) => {
force = true;
false
}
}
}
fetch::DryRun::Yes => true,
};
if is_fast_forward {
(
update::Mode::FastForward,
matches!(dry_run, fetch::DryRun::Yes)
.then(|| "fast-forward (guessed in dry-run)")
.unwrap_or("fast-forward"),
)
} else if force {
(update::Mode::Forced, "forced-update")
} else {
updates.push(update::Mode::RejectedNonFastForward.into());
continue;
}
};
(mode, reflog_message, existing.name().to_owned(), previous_value)
}
}
}
None => {
let name: git_ref::FullName = name.try_into()?;
let reflog_msg = match name.category() {
Some(git_ref::Category::Tag) => "storing tag",
Some(git_ref::Category::LocalBranch) => "storing head",
_ => "storing ref",
};
(
update::Mode::New,
reflog_msg,
name,
PreviousValue::ExistingMustMatch(Target::Peeled(remote_id.to_owned())),
)
}
};
let edit = RefEdit {
change: Change::Update {
log: LogChange {
mode: RefLog::AndReference,
force_create_reflog: false,
message: message.compose(reflog_message),
},
expected: previous_value,
new: if let Source::Ref(git_protocol::handshake::Ref::Symbolic { target, .. }) = &remote {
match mappings.iter().find_map(|m| {
m.remote.as_name().and_then(|name| {
(name == target)
.then(|| m.local.as_ref().and_then(|local| local.try_into().ok()))
.flatten()
})
}) {
Some(local_branch) => {
// This is always safe because…
// - the reference may exist already
// - if it doesn't exist it will be created - we are here because it's in the list of mappings after all
// - if it exists and is updated, and the update is rejected due to non-fastforward for instance, the
// target reference still exists and we can point to it.
Target::Symbolic(local_branch)
}
None => Target::Peeled(remote_id.into()),
}
} else {
Target::Peeled(remote_id.into())
},
},
name,
deref: false,
};
let edit_index = edits.len();
edits.push(edit);
(mode, Some(edit_index))
}
None => (update::Mode::NoChangeNeeded, None),
};
updates.push(Update { mode, edit_index })
}
let edits = match dry_run {
fetch::DryRun::No => {
let (file_lock_fail, packed_refs_lock_fail) = repo
.config
.lock_timeout()
.map_err(crate::reference::edit::Error::from)?;
repo.refs
.transaction()
.packed_refs(
match write_packed_refs {
fetch::WritePackedRefs::Only => {
git_ref::file::transaction::PackedRefs::DeletionsAndNonSymbolicUpdatesRemoveLooseSourceReference(Box::new(|oid, buf| {
repo.objects
.try_find(oid, buf)
.map(|obj| obj.map(|obj| obj.kind))
.map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>)
}))},
fetch::WritePackedRefs::Never => git_ref::file::transaction::PackedRefs::DeletionsOnly
}
)
.prepare(edits, file_lock_fail, packed_refs_lock_fail)
.map_err(crate::reference::edit::Error::from)?
.commit(repo.committer_or_default())
.map_err(crate::reference::edit::Error::from)?
}
fetch::DryRun::Yes => edits,
};
Ok(update::Outcome { edits, updates })
}
sourcepub fn try_into_tag(self) -> Result<Tag<'repo>, Error>
pub fn try_into_tag(self) -> Result<Tag<'repo>, Error>
Transform this object into a tag, or return it as part of the Err
if it is no commit.
Examples found in repository?
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
fn names(&self, repo: &Repository) -> Result<HashMap<ObjectId, Cow<'static, BStr>>, Error> {
let platform = repo.references()?;
Ok(match self {
SelectRef::AllTags | SelectRef::AllRefs => {
let mut refs: Vec<_> = match self {
SelectRef::AllRefs => platform.all()?,
SelectRef::AllTags => platform.tags()?,
_ => unreachable!(),
}
.filter_map(Result::ok)
.filter_map(|mut r: crate::Reference<'_>| {
let target_id = r.target().try_id().map(ToOwned::to_owned);
let peeled_id = r.peel_to_id_in_place().ok()?;
let (prio, tag_time) = match target_id {
Some(target_id) if peeled_id != *target_id => {
let tag = repo.find_object(target_id).ok()?.try_into_tag().ok()?;
(1, tag.tagger().ok()??.time.seconds_since_unix_epoch)
}
_ => (0, 0),
};
(
peeled_id.inner,
prio,
tag_time,
Cow::from(r.inner.name.shorten().to_owned()),
)
.into()
})
.collect();
// By priority, then by time ascending, then lexicographically.
// More recent entries overwrite older ones due to collection into hashmap.
refs.sort_by(
|(_a_peeled_id, a_prio, a_time, a_name), (_b_peeled_id, b_prio, b_time, b_name)| {
a_prio
.cmp(b_prio)
.then_with(|| a_time.cmp(b_time))
.then_with(|| b_name.cmp(a_name))
},
);
refs.into_iter().map(|(a, _, _, b)| (a, b)).collect()
}
SelectRef::AnnotatedTags => {
let mut peeled_commits_and_tag_date: Vec<_> = platform
.tags()?
.filter_map(Result::ok)
.filter_map(|r: crate::Reference<'_>| {
// TODO: we assume direct refs for tags, which is the common case, but it doesn't have to be
// so rather follow symrefs till the first object and then peel tags after the first object was found.
let tag = r.try_id()?.object().ok()?.try_into_tag().ok()?;
let tag_time = tag
.tagger()
.ok()
.and_then(|s| s.map(|s| s.time.seconds_since_unix_epoch))
.unwrap_or(0);
let commit_id = tag.target_id().ok()?.object().ok()?.try_into_commit().ok()?.id;
Some((commit_id, tag_time, Cow::<BStr>::from(r.name().shorten().to_owned())))
})
.collect();
// Sort by time ascending, then lexicographically.
// More recent entries overwrite older ones due to collection into hashmap.
peeled_commits_and_tag_date.sort_by(|(_a_id, a_time, a_name), (_b_id, b_time, b_name)| {
a_time.cmp(b_time).then_with(|| b_name.cmp(a_name))
});
peeled_commits_and_tag_date
.into_iter()
.map(|(a, _, c)| (a, c))
.collect()
}
})
}
sourcepub fn try_into_tree(self) -> Result<Tree<'repo>, Error>
pub fn try_into_tree(self) -> Result<Tree<'repo>, Error>
Transform this object into a tree, or return it as part of the Err
if it is no tree.
Examples found in repository?
More examples
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
pub fn lookup_entry<I, P>(mut self, path: I) -> Result<Option<Entry<'repo>>, find::existing::Error>
where
I: IntoIterator<Item = P>,
P: PartialEq<BStr>,
{
let mut path = path.into_iter().peekable();
while let Some(component) = path.next() {
match TreeRefIter::from_bytes(&self.data)
.filter_map(Result::ok)
.find(|entry| component.eq(entry.filename))
{
Some(entry) => {
if path.peek().is_none() {
return Ok(Some(Entry {
inner: entry.into(),
repo: self.repo,
}));
} else {
let next_id = entry.oid.to_owned();
let repo = self.repo;
drop(self);
self = match repo.find_object(next_id)?.try_into_tree() {
Ok(tree) => tree,
Err(_) => return Ok(None),
};
}
}
None => return Ok(None),
}
}
Ok(None)
}
source§impl<'repo> Object<'repo>
impl<'repo> Object<'repo>
sourcepub fn detached(&self) -> ObjectDetached
pub fn detached(&self) -> ObjectDetached
Create an owned instance of this object, copying our data in the process.
sourcepub fn detach(self) -> ObjectDetached
pub fn detach(self) -> ObjectDetached
Sever the connection to the Repository
and turn this instance into a standalone object.
source§impl<'repo> Object<'repo>
impl<'repo> Object<'repo>
Conversions to detached, lower-level object types.
sourcepub fn to_commit_ref(&self) -> CommitRef<'_>
pub fn to_commit_ref(&self) -> CommitRef<'_>
Obtain a fully parsed commit whose fields reference our data buffer,
Panic
- this object is not a commit
- the commit could not be decoded
Examples found in repository?
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
pub(crate) fn ambiguous(candidates: HashSet<ObjectId>, prefix: git_hash::Prefix, repo: &Repository) -> Self {
#[derive(PartialOrd, Ord, Eq, PartialEq, Copy, Clone)]
enum Order {
Tag,
Commit,
Tree,
Blob,
Invalid,
}
let candidates = {
let mut c: Vec<_> = candidates
.into_iter()
.map(|oid| {
let obj = repo.find_object(oid);
let order = match &obj {
Err(_) => Order::Invalid,
Ok(obj) => match obj.kind {
git_object::Kind::Tag => Order::Tag,
git_object::Kind::Commit => Order::Commit,
git_object::Kind::Tree => Order::Tree,
git_object::Kind::Blob => Order::Blob,
},
};
(oid, obj, order)
})
.collect();
c.sort_by(|lhs, rhs| lhs.2.cmp(&rhs.2).then_with(|| lhs.0.cmp(&rhs.0)));
c
};
Error::AmbiguousPrefix {
prefix,
info: candidates
.into_iter()
.map(|(oid, find_result, _)| {
let info = match find_result {
Ok(obj) => match obj.kind {
git_object::Kind::Tree | git_object::Kind::Blob => CandidateInfo::Object { kind: obj.kind },
git_object::Kind::Tag => {
let tag = obj.to_tag_ref();
CandidateInfo::Tag { name: tag.name.into() }
}
git_object::Kind::Commit => {
use bstr::ByteSlice;
let commit = obj.to_commit_ref();
CandidateInfo::Commit {
date: commit.committer().time,
title: commit.message().title.trim().into(),
}
}
},
Err(err) => CandidateInfo::FindError { source: err },
};
(oid.attach(repo).shorten().unwrap_or_else(|_| oid.into()), info)
})
.collect(),
}
}
sourcepub fn try_to_commit_ref(&self) -> Result<CommitRef<'_>, Error>
pub fn try_to_commit_ref(&self) -> Result<CommitRef<'_>, Error>
Obtain a fully parsed commit whose fields reference our data buffer.
sourcepub fn to_commit_ref_iter(&self) -> CommitRefIter<'_>
pub fn to_commit_ref_iter(&self) -> CommitRefIter<'_>
sourcepub fn try_to_commit_ref_iter(&self) -> Option<CommitRefIter<'_>>
pub fn try_to_commit_ref_iter(&self) -> Option<CommitRefIter<'_>>
Obtain a commit token iterator from the data in this instance, if it is a commit.
Examples found in repository?
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
pub fn peel_to_kind(mut self, kind: Kind) -> Result<Self, peel::to_kind::Error> {
loop {
match self.kind {
our_kind if kind == our_kind => {
return Ok(self);
}
Kind::Commit => {
let tree_id = self
.try_to_commit_ref_iter()
.expect("commit")
.tree_id()
.expect("valid commit");
let repo = self.repo;
drop(self);
self = repo.find_object(tree_id)?;
}
Kind::Tag => {
let target_id = self.to_tag_ref_iter().target_id().expect("valid tag");
let repo = self.repo;
drop(self);
self = repo.find_object(target_id)?;
}
Kind::Tree | Kind::Blob => {
return Err(peel::to_kind::Error::NotFound {
oid: self.id().shorten().unwrap_or_else(|_| self.id.into()),
actual: self.kind,
expected: kind,
})
}
}
}
}
sourcepub fn to_tag_ref_iter(&self) -> TagRefIter<'_>
pub fn to_tag_ref_iter(&self) -> TagRefIter<'_>
Examples found in repository?
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
pub fn peel_to_kind(mut self, kind: Kind) -> Result<Self, peel::to_kind::Error> {
loop {
match self.kind {
our_kind if kind == our_kind => {
return Ok(self);
}
Kind::Commit => {
let tree_id = self
.try_to_commit_ref_iter()
.expect("commit")
.tree_id()
.expect("valid commit");
let repo = self.repo;
drop(self);
self = repo.find_object(tree_id)?;
}
Kind::Tag => {
let target_id = self.to_tag_ref_iter().target_id().expect("valid tag");
let repo = self.repo;
drop(self);
self = repo.find_object(target_id)?;
}
Kind::Tree | Kind::Blob => {
return Err(peel::to_kind::Error::NotFound {
oid: self.id().shorten().unwrap_or_else(|_| self.id.into()),
actual: self.kind,
expected: kind,
})
}
}
}
}
/// Peel this object into a tree and return it, if this is possible.
pub fn peel_to_tree(self) -> Result<Tree<'repo>, peel::to_kind::Error> {
Ok(self.peel_to_kind(git_object::Kind::Tree)?.into_tree())
}
// TODO: tests
/// Follow all tag object targets until a commit, tree or blob is reached.
///
/// Note that this method is different from [`peel_to_kind(…)`][Object::peel_to_kind()] as it won't
/// peel commits to their tree, but handles tags only.
pub fn peel_tags_to_end(mut self) -> Result<Self, object::find::existing::Error> {
loop {
match self.kind {
Kind::Commit | Kind::Tree | Kind::Blob => break Ok(self),
Kind::Tag => {
let target_id = self.to_tag_ref_iter().target_id().expect("valid tag");
let repo = self.repo;
drop(self);
self = repo.find_object(target_id)?;
}
}
}
}
sourcepub fn try_to_tag_ref_iter(&self) -> Option<TagRefIter<'_>>
pub fn try_to_tag_ref_iter(&self) -> Option<TagRefIter<'_>>
sourcepub fn to_tag_ref(&self) -> TagRef<'_>
pub fn to_tag_ref(&self) -> TagRef<'_>
Obtain a tag object from the data in this instance.
Panic
- this object is not a tag
- the tag could not be decoded
Examples found in repository?
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
pub(crate) fn ambiguous(candidates: HashSet<ObjectId>, prefix: git_hash::Prefix, repo: &Repository) -> Self {
#[derive(PartialOrd, Ord, Eq, PartialEq, Copy, Clone)]
enum Order {
Tag,
Commit,
Tree,
Blob,
Invalid,
}
let candidates = {
let mut c: Vec<_> = candidates
.into_iter()
.map(|oid| {
let obj = repo.find_object(oid);
let order = match &obj {
Err(_) => Order::Invalid,
Ok(obj) => match obj.kind {
git_object::Kind::Tag => Order::Tag,
git_object::Kind::Commit => Order::Commit,
git_object::Kind::Tree => Order::Tree,
git_object::Kind::Blob => Order::Blob,
},
};
(oid, obj, order)
})
.collect();
c.sort_by(|lhs, rhs| lhs.2.cmp(&rhs.2).then_with(|| lhs.0.cmp(&rhs.0)));
c
};
Error::AmbiguousPrefix {
prefix,
info: candidates
.into_iter()
.map(|(oid, find_result, _)| {
let info = match find_result {
Ok(obj) => match obj.kind {
git_object::Kind::Tree | git_object::Kind::Blob => CandidateInfo::Object { kind: obj.kind },
git_object::Kind::Tag => {
let tag = obj.to_tag_ref();
CandidateInfo::Tag { name: tag.name.into() }
}
git_object::Kind::Commit => {
use bstr::ByteSlice;
let commit = obj.to_commit_ref();
CandidateInfo::Commit {
date: commit.committer().time,
title: commit.message().title.trim().into(),
}
}
},
Err(err) => CandidateInfo::FindError { source: err },
};
(oid.attach(repo).shorten().unwrap_or_else(|_| oid.into()), info)
})
.collect(),
}
}
sourcepub fn try_to_tag_ref(&self) -> Result<TagRef<'_>, Error>
pub fn try_to_tag_ref(&self) -> Result<TagRef<'_>, Error>
Obtain a fully parsed tag object whose fields reference our data buffer.
sourcepub fn id(&self) -> Id<'repo>
pub fn id(&self) -> Id<'repo>
Return the attached id of this object.
Examples found in repository?
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
pub fn peel_to_kind(mut self, kind: Kind) -> Result<Self, peel::to_kind::Error> {
loop {
match self.kind {
our_kind if kind == our_kind => {
return Ok(self);
}
Kind::Commit => {
let tree_id = self
.try_to_commit_ref_iter()
.expect("commit")
.tree_id()
.expect("valid commit");
let repo = self.repo;
drop(self);
self = repo.find_object(tree_id)?;
}
Kind::Tag => {
let target_id = self.to_tag_ref_iter().target_id().expect("valid tag");
let repo = self.repo;
drop(self);
self = repo.find_object(target_id)?;
}
Kind::Tree | Kind::Blob => {
return Err(peel::to_kind::Error::NotFound {
oid: self.id().shorten().unwrap_or_else(|_| self.id.into()),
actual: self.kind,
expected: kind,
})
}
}
}
}
Trait Implementations§
source§impl<'repo> From<Object<'repo>> for ObjectDetached
impl<'repo> From<Object<'repo>> for ObjectDetached
source§impl<'repo> PartialEq<Object<'repo>> for Id<'repo>
impl<'repo> PartialEq<Object<'repo>> for Id<'repo>
Auto Trait Implementations§
impl<'repo> !RefUnwindSafe for Object<'repo>
impl<'repo> !Send for Object<'repo>
impl<'repo> !Sync for Object<'repo>
impl<'repo> Unpin for Object<'repo>
impl<'repo> !UnwindSafe for Object<'repo>
Blanket Implementations§
§impl<T> Pointable for T
impl<T> Pointable for T
source§impl<T> ToHex for Twhere
T: AsRef<[u8]>,
impl<T> ToHex for Twhere
T: AsRef<[u8]>,
source§fn encode_hex<U>(&self) -> Uwhere
U: FromIterator<char>,
fn encode_hex<U>(&self) -> Uwhere
U: FromIterator<char>,
self
into the result. Lower case
letters are used (e.g. f9b4ca
)source§fn encode_hex_upper<U>(&self) -> Uwhere
U: FromIterator<char>,
fn encode_hex_upper<U>(&self) -> Uwhere
U: FromIterator<char>,
self
into the result. Upper case
letters are used (e.g. F9B4CA
)