1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
use std::collections::HashSet;
use git_hash::ObjectId;
use super::Error;
use crate::{bstr, bstr::BString, ext::ObjectIdExt, Repository};
#[derive(Debug)]
pub enum CandidateInfo {
FindError {
source: crate::object::find::existing::Error,
},
Object {
kind: git_object::Kind,
},
Tag {
name: BString,
},
Commit {
date: git_date::Time,
title: BString,
},
}
impl std::fmt::Display for CandidateInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CandidateInfo::FindError { source } => write!(f, "lookup error: {source}"),
CandidateInfo::Tag { name } => write!(f, "tag {name:?}"),
CandidateInfo::Object { kind } => std::fmt::Display::fmt(kind, f),
CandidateInfo::Commit { date, title } => {
write!(f, "commit {} {title:?}", date.format(git_date::time::format::SHORT))
}
}
}
}
impl Error {
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(),
}
}
pub(crate) fn from_errors(errors: Vec<Self>) -> Self {
assert!(!errors.is_empty());
match errors.len() {
0 => unreachable!(
"BUG: cannot create something from nothing, must have recorded some errors to call from_errors()"
),
1 => errors.into_iter().next().expect("one"),
_ => {
let mut it = errors.into_iter().rev();
let mut recent = Error::Multi {
current: Box::new(it.next().expect("at least one error")),
next: None,
};
for err in it {
recent = Error::Multi {
current: Box::new(err),
next: Some(Box::new(recent)),
}
}
recent
}
}
}
}