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§

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?
src/object/peel.rs (line 72)
71
72
73
    pub fn peel_to_tree(self) -> Result<Tree<'repo>, peel::to_kind::Error> {
        Ok(self.peel_to_kind(git_object::Kind::Tree)?.into_tree())
    }
More examples
Hide additional examples
src/revision/spec/parse/delegate/mod.rs (line 228)
226
227
228
229
230
231
fn peel(repo: &Repository, obj: &git_hash::oid, kind: git_object::Kind) -> Result<ObjectId, Error> {
    let mut obj = repo.find_object(obj)?;
    obj = obj.peel_to_kind(kind)?;
    debug_assert_eq!(obj.kind, kind, "bug in Object::peel_to_kind() which didn't deliver");
    Ok(obj.id)
}

Peel this object into a tree and return it, if this is possible.

Examples found in repository?
src/clone/checkout.rs (line 61)
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?
src/head/peel.rs (line 65)
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
Hide additional examples
src/revision/spec/parse/delegate/navigate.rs (line 145)
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)
    }

Consuming conversions to attached object kinds.

Transform this object into a tree, or panic if it is none.

Examples found in repository?
src/object/peel.rs (line 72)
71
72
73
    pub fn peel_to_tree(self) -> Result<Tree<'repo>, peel::to_kind::Error> {
        Ok(self.peel_to_kind(git_object::Kind::Tree)?.into_tree())
    }
More examples
Hide additional examples
src/repository/object.rs (line 211)
208
209
210
211
212
    pub fn empty_tree(&self) -> Tree<'_> {
        self.find_object(git_hash::ObjectId::empty_tree(self.object_hash()))
            .expect("always present")
            .into_tree()
    }
src/revision/spec/parse/delegate/navigate.rs (line 126)
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)
    }

Transform this object into a commit, or panic if it is none.

Examples found in repository?
src/revision/spec/parse/delegate/navigate.rs (line 197)
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
                }
            },
        }
    }

Transform this object into a commit, or return it as part of the Err if it is no commit.

Examples found in repository?
src/head/peel.rs (line 99)
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))
    }
More examples
Hide additional examples
src/revision/spec/parse/delegate/navigate.rs (line 34)
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)
    }
src/commit.rs (line 125)
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()
                }
            })
        }
src/remote/connection/fetch/update_refs/mod.rs (line 125)
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 })
}

Transform this object into a tag, or return it as part of the Err if it is no commit.

Examples found in repository?
src/commit.rs (line 86)
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()
                }
            })
        }

Transform this object into a tree, or return it as part of the Err if it is no tree.

Examples found in repository?
src/object/commit.rs (line 114)
113
114
115
116
117
118
    pub fn tree(&self) -> Result<Tree<'repo>, Error> {
        match self.tree_id()?.object()?.try_into_tree() {
            Ok(tree) => Ok(tree),
            Err(crate::object::try_into::Error { actual, expected, .. }) => Err(Error::ObjectKind { actual, expected }),
        }
    }
More examples
Hide additional examples
src/object/tree/mod.rs (line 61)
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)
    }

Create an owned instance of this object, copying our data in the process.

Sever the connection to the Repository and turn this instance into a standalone object.

Conversions to detached, lower-level object types.

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?
src/revision/spec/parse/error.rs (line 92)
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(),
        }
    }

Obtain a fully parsed commit whose fields reference our data buffer.

Examples found in repository?
src/object/mod.rs (line 146)
145
146
147
    pub fn to_commit_ref(&self) -> git_object::CommitRef<'_> {
        self.try_to_commit_ref().expect("BUG: need a commit")
    }

Obtain a an iterator over commit tokens like in to_commit_iter().

Panic
  • this object is not a commit

Obtain a commit token iterator from the data in this instance, if it is a commit.

Examples found in repository?
src/object/peel.rs (line 45)
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,
                    })
                }
            }
        }
    }

Obtain a tag token iterator from the data in this instance.

Panic
  • this object is not a tag
Examples found in repository?
src/object/peel.rs (line 54)
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)?;
                }
            }
        }
    }

Obtain a tag token iterator from the data in this instance.

Panic
  • this object is not a tag

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?
src/revision/spec/parse/error.rs (line 87)
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(),
        }
    }

Obtain a fully parsed tag object whose fields reference our data buffer.

Examples found in repository?
src/object/mod.rs (line 203)
202
203
204
    pub fn to_tag_ref(&self) -> git_object::TagRef<'_> {
        self.try_to_tag_ref().expect("BUG: need tag")
    }

Return the attached id of this object.

Examples found in repository?
src/object/peel.rs (line 61)
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§

Converts this type into a shared reference of the (usually inferred) input type.
Formats the value using the given formatter. Read more
Executes the destructor for this type. Read more
Converts to this type from the input type.
Converts to this type from the input type.
This method tests for self and other values to be equal, and is used by ==.
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
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.
The type returned in the event of a conversion error.
Performs the conversion.

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
Encode the hex strict representing self into the result. Lower case letters are used (e.g. f9b4ca)
Encode the hex strict representing self into the result. Upper case letters are used (e.g. F9B4CA)
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.