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§

Remote

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.

Return a platform for obtaining iterators on the reference log associated with the HEAD reference.

Examples found in repository?
src/head/log.rs (line 24)
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
Hide additional examples
src/revision/spec/parse/delegate/revision.rs (line 189)
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
            }
        }
    }

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.

Peel this instance to make obtaining its final target id possible, while returning an error on unborn heads.

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?
src/head/peel.rs (line 45)
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
Hide additional examples
src/repository/reference.rs (line 171)
169
170
171
172
173
174
175
176
    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)
    }
src/clone/checkout.rs (line 60)
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 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?
src/repository/reference.rs (line 197)
196
197
198
    pub fn head_commit(&self) -> Result<crate::Commit<'_>, reference::head_commit::Error> {
        Ok(self.head()?.peel_to_commit_in_place()?)
    }
More examples
Hide additional examples
src/repository/snapshots.rs (line 43)
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(()))
    }

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.

Access

Returns the name of this references, always HEAD.

Returns the full reference name of this head if it is not detached, or None otherwise.

Examples found in repository?
src/repository/reference.rs (line 173)
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
Hide additional examples
src/head/peel.rs (line 95)
93
94
95
96
97
98
99
100
    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))
    }

Returns true if this instance is detached, and points to an object directly.

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.

Returns the id the head points to, which isn’t possible on unborn heads.

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?
src/repository/reference.rs (line 188)
187
188
189
    pub fn head_ref(&self) -> Result<Option<Reference<'_>>, reference::find::existing::Error> {
        Ok(self.head()?.try_into_referent())
    }
More examples
Hide additional examples
src/head/mod.rs (line 111)
106
107
108
109
110
111
112
113
114
        pub fn into_remote(
            self,
            direction: remote::Direction,
        ) -> Option<Result<Remote<'repo>, remote::find::existing::Error>> {
            let repo = self.repo;
            self.try_into_referent()?
                .remote(direction)
                .or_else(|| repo.find_default_remote(direction))
        }
src/revision/spec/parse/delegate/revision.rs (line 117)
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
                    }
                }
            }
        }
    }

Trait Implementations§

Returns a copy of the value. Read more
Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.
The type for initializers.
Initializes a with the given initializer. Read more
Dereferences the given pointer. Read more
Mutably dereferences the given pointer. Read more
Drops the object pointed to by the given pointer. Read more
Should always be Self
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.