Struct git_repository::Head
source · pub struct Head<'repo> {
pub kind: Kind,
/* private fields */
}
Expand description
The head reference, as created from looking at .git/HEAD
, able to represent all of its possible states.
Note that like Reference
, this type’s data is snapshot of persisted state on disk.
Fields§
§kind: Kind
One of various possible states for the HEAD reference
Implementations§
source§impl<'repo> Head<'repo>
impl<'repo> Head<'repo>
Remote
sourcepub fn into_remote(
self,
direction: Direction
) -> Option<Result<Remote<'repo>, Error>>
pub fn into_remote(
self,
direction: Direction
) -> Option<Result<Remote<'repo>, Error>>
Return the remote with which the currently checked our reference can be handled as configured by branch.<name>.remote|pushRemote
or fall back to the non-branch specific remote configuration. None
is returned if the head is detached or unborn, so there is
no branch specific remote.
This is equivalent to calling Reference::remote(…)
and
Repository::remote_default_name()
in order.
Combine it with find_default_remote()
as fallback to handle detached heads,
i.e. obtain a remote even in case of detached heads.
source§impl<'repo> Head<'repo>
impl<'repo> Head<'repo>
sourcepub fn log_iter(&self) -> Platform<'static, 'repo>
pub fn log_iter(&self) -> Platform<'static, 'repo>
Return a platform for obtaining iterators on the reference log associated with the HEAD
reference.
Examples found in repository?
23 24 25 26 27 28 29 30 31 32 33 34
pub fn prior_checked_out_branches(&self) -> std::io::Result<Option<Vec<(BString, ObjectId)>>> {
Ok(self.log_iter().all()?.map(|log| {
log.filter_map(Result::ok)
.filter_map(|line| {
line.message
.strip_prefix(b"checkout: moving from ")
.and_then(|from_to| from_to.find(" to ").map(|pos| &from_to[..pos]))
.map(|from_branch| (from_branch.as_bstr().to_owned(), line.previous_oid()))
})
.collect()
}))
}
More examples
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
fn nth_checked_out_branch(&mut self, branch_no: usize) -> Option<()> {
self.unset_disambiguate_call();
fn prior_checkouts_iter<'a>(
platform: &'a mut git_ref::file::log::iter::Platform<'static, '_>,
) -> Result<impl Iterator<Item = (BString, ObjectId)> + 'a, Error> {
match platform.rev().ok().flatten() {
Some(log) => Ok(log.filter_map(Result::ok).filter_map(|line| {
line.message
.strip_prefix(b"checkout: moving from ")
.and_then(|from_to| from_to.find(" to ").map(|pos| &from_to[..pos]))
.map(|from_branch| (from_branch.into(), line.previous_oid))
})),
None => Err(Error::MissingRefLog {
reference: "HEAD".into(),
action: "search prior checked out branch",
}),
}
}
let head = match self.repo.head() {
Ok(head) => head,
Err(err) => {
self.err.push(err.into());
return None;
}
};
match prior_checkouts_iter(&mut head.log_iter()).map(|mut it| it.nth(branch_no.saturating_sub(1))) {
Ok(Some((ref_name, id))) => {
let id = match self.repo.find_reference(ref_name.as_bstr()) {
Ok(mut r) => {
let id = r.peel_to_id_in_place().map(|id| id.detach()).unwrap_or(id);
self.refs[self.idx] = Some(r.detach());
id
}
Err(_) => id,
};
self.objs[self.idx].get_or_insert_with(HashSet::default).insert(id);
Some(())
}
Ok(None) => {
self.err.push(Error::PriorCheckoutOutOfRange {
desired: branch_no,
available: prior_checkouts_iter(&mut head.log_iter())
.map(|it| it.count())
.unwrap_or(0),
});
None
}
Err(err) => {
self.err.push(err);
None
}
}
}
sourcepub fn prior_checked_out_branches(
&self
) -> Result<Option<Vec<(BString, ObjectId)>>>
pub fn prior_checked_out_branches(
&self
) -> Result<Option<Vec<(BString, ObjectId)>>>
Return a list of all branch names that were previously checked out with the first-ever checked out branch being the first entry of the list, and the most recent is the last, along with the commit they were pointing to at the time.
source§impl<'repo> Head<'repo>
impl<'repo> Head<'repo>
sourcepub fn peeled(self) -> Result<Self, Error>
pub fn peeled(self) -> Result<Self, Error>
Peel this instance to make obtaining its final target id possible, while returning an error on unborn heads.
sourcepub fn peel_to_id_in_place(&mut self) -> Option<Result<Id<'repo>, Error>>
pub fn peel_to_id_in_place(&mut self) -> Option<Result<Id<'repo>, Error>>
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, and return that object id.
Returns None
if the head is unborn.
Examples found in repository?
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 93 94 95 96 97 98 99 100
pub fn peeled(mut self) -> Result<Self, Error> {
self.peel_to_id_in_place().transpose()?;
Ok(self)
}
// TODO: tests
/// 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, and return that object id.
///
/// Returns `None` if the head is unborn.
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))
}
More examples
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))
}
sourcepub fn peel_to_commit_in_place(&mut self) -> Result<Commit<'repo>, Error>
pub fn peel_to_commit_in_place(&mut self) -> Result<Commit<'repo>, Error>
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.
Examples found in repository?
More examples
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 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
pub fn open_mailmap_into(&self, target: &mut git_mailmap::Snapshot) -> Result<(), crate::mailmap::load::Error> {
let mut err = None::<crate::mailmap::load::Error>;
let mut buf = Vec::new();
let mut blob_id = self
.config
.resolved
.raw_value("mailmap", None, "blob")
.ok()
.and_then(|spec| {
// TODO: actually resolve this as spec (once we can do that)
git_hash::ObjectId::from_hex(spec.as_ref())
.map_err(|e| err.get_or_insert(e.into()))
.ok()
});
match self.work_dir() {
None => {
// TODO: replace with ref-spec `HEAD:.mailmap` for less verbose way of getting the blob id
blob_id = blob_id.or_else(|| {
self.head().ok().and_then(|mut head| {
let commit = head.peel_to_commit_in_place().ok()?;
let tree = commit.tree().ok()?;
tree.lookup_entry(Some(".mailmap")).ok()?.map(|e| e.object_id())
})
});
}
Some(root) => {
if let Ok(mut file) = git_features::fs::open_options_no_follow()
.read(true)
.open(root.join(".mailmap"))
.map_err(|e| {
if e.kind() != std::io::ErrorKind::NotFound {
err.get_or_insert(e.into());
}
})
{
buf.clear();
std::io::copy(&mut file, &mut buf)
.map_err(|e| err.get_or_insert(e.into()))
.ok();
target.merge(git_mailmap::parse_ignore_errors(&buf));
}
}
}
if let Some(blob) = blob_id.and_then(|id| self.find_object(id).map_err(|e| err.get_or_insert(e.into())).ok()) {
target.merge(git_mailmap::parse_ignore_errors(&blob.data));
}
let configured_path = self
.config
.resolved
.value::<git_config::Path<'_>>("mailmap", None, "file")
.ok()
.and_then(|path| {
let install_dir = self.install_dir().ok()?;
let home = self.config.home_dir();
match path.interpolate(git_config::path::interpolate::Context {
git_install_dir: Some(install_dir.as_path()),
home_dir: home.as_deref(),
home_for_user: if self.options.git_dir_trust.expect("trust is set") == git_sec::Trust::Full {
Some(git_config::path::interpolate::home_for_user)
} else {
None
},
}) {
Ok(path) => Some(path),
Err(e) => {
err.get_or_insert(e.into());
None
}
}
});
if let Some(mut file) =
configured_path.and_then(|path| std::fs::File::open(path).map_err(|e| err.get_or_insert(e.into())).ok())
{
buf.clear();
std::io::copy(&mut file, &mut buf)
.map_err(|e| err.get_or_insert(e.into()))
.ok();
target.merge(git_mailmap::parse_ignore_errors(&buf));
}
err.map(Err).unwrap_or(Ok(()))
}
source§impl<'repo> Head<'repo>
impl<'repo> Head<'repo>
Access
sourcepub fn name(&self) -> &'static FullNameRef
pub fn name(&self) -> &'static FullNameRef
Returns the name of this references, always HEAD
.
sourcepub fn referent_name(&self) -> Option<&FullNameRef>
pub fn referent_name(&self) -> Option<&FullNameRef>
Returns the full reference name of this head if it is not detached, or None
otherwise.
Examples found in repository?
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
pub fn head_id(&self) -> Result<crate::Id<'_>, reference::head_id::Error> {
let mut head = self.head()?;
head.peel_to_id_in_place()
.ok_or_else(|| reference::head_id::Error::Unborn {
name: head.referent_name().expect("unborn").to_owned(),
})?
.map_err(Into::into)
}
/// Return the name to the symbolic reference `HEAD` points to, or `None` if the head is detached.
///
/// The difference to [`head_ref()`][Self::head_ref()] is that the latter requires the reference to exist,
/// whereas here we merely return a the name of the possibly unborn reference.
pub fn head_name(&self) -> Result<Option<FullName>, reference::find::existing::Error> {
Ok(self.head()?.referent_name().map(|n| n.to_owned()))
}
More examples
sourcepub fn is_detached(&self) -> bool
pub fn is_detached(&self) -> bool
Returns true if this instance is detached, and points to an object directly.
sourcepub fn is_unborn(&self) -> bool
pub fn is_unborn(&self) -> bool
Returns true if this instance is not yet born, hence it points to a ref that doesn’t exist yet.
This is the case in a newly initialized repository.
sourcepub fn id(&self) -> Option<Id<'repo>>
pub fn id(&self) -> Option<Id<'repo>>
Returns the id the head points to, which isn’t possible on unborn heads.
sourcepub fn try_into_referent(self) -> Option<Reference<'repo>>
pub fn try_into_referent(self) -> Option<Reference<'repo>>
Try to transform this instance into the symbolic reference that it points to, or return None
if head is detached or unborn.
Examples found in repository?
More examples
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
fn reflog(&mut self, query: ReflogLookup) -> Option<()> {
self.unset_disambiguate_call();
match query {
ReflogLookup::Date(_date) => {
self.err.push(Error::Planned {
dependency: "remote handling and ref-specs are fleshed out more",
});
None
}
ReflogLookup::Entry(no) => {
let r = match &mut self.refs[self.idx] {
Some(r) => r.clone().attach(self.repo),
val @ None => match self.repo.head().map(|head| head.try_into_referent()) {
Ok(Some(r)) => {
*val = Some(r.clone().detach());
r
}
Ok(None) => {
self.err.push(Error::UnbornHeadsHaveNoRefLog);
return None;
}
Err(err) => {
self.err.push(err.into());
return None;
}
},
};
let mut platform = r.log_iter();
match platform.rev().ok().flatten() {
Some(mut it) => match it.nth(no).and_then(Result::ok) {
Some(line) => {
self.objs[self.idx]
.get_or_insert_with(HashSet::default)
.insert(line.new_oid);
Some(())
}
None => {
let available = platform.rev().ok().flatten().map_or(0, |it| it.count());
self.err.push(Error::RefLogEntryOutOfRange {
reference: r.detach(),
desired: no,
available,
});
None
}
},
None => {
self.err.push(Error::MissingRefLog {
reference: r.name().as_bstr().into(),
action: "lookup entry",
});
None
}
}
}
}
}