Skip to content

Commit 9a97ca8

Browse files
Make rev() detect all parents of the given commits (#1329)
The `:rev()` filter used to only apply filters if the specified revisions where encountered during traversal. However, this meant that if an ancestor commit is reachable by multiple paths in the history and some include the specified revision and some don't, the ancestor has multiple corresponding filtered commits. This resulted in duplicated commits in those more complicated histories and caused "non roundtrip" issues. Now the rev filter will compute if a commit is reachable from the specifed commit in any way and apply if so. Change: rev-all-parents Co-authored-by: Ralf Jung <[email protected]>
1 parent 2940c9e commit 9a97ca8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+154
-90
lines changed

josh-core/src/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::*;
22
use std::collections::HashMap;
33

4-
const CACHE_VERSION: u64 = 18;
4+
const CACHE_VERSION: u64 = 19;
55

66
lazy_static! {
77
static ref DB: std::sync::Mutex<Option<sled::Db>> = std::sync::Mutex::new(None);

josh-core/src/filter/mod.rs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ lazy_static! {
1414
std::sync::Mutex::new(std::collections::HashMap::new());
1515
static ref WORKSPACES: std::sync::Mutex<std::collections::HashMap<git2::Oid, Filter>> =
1616
std::sync::Mutex::new(std::collections::HashMap::new());
17+
static ref ANCESTORS: std::sync::Mutex<std::collections::HashMap<git2::Oid, std::collections::HashSet<git2::Oid>>> =
18+
std::sync::Mutex::new(std::collections::HashMap::new());
1719
}
1820

1921
/// Filters are represented as `git2::Oid`, however they are not ever stored
@@ -485,9 +487,21 @@ fn apply_to_commit2(
485487

486488
let id = commit.id();
487489

488-
if let Some(startfilter) = filters.get(&id) {
490+
for (&filter_tip, startfilter) in filters.iter() {
491+
if filter_tip == git2::Oid::zero() {
492+
continue;
493+
}
494+
if !ok_or!(is_ancestor_of(repo, id, filter_tip), {
495+
return Err(josh_error(&format!(
496+
"`:rev(...)` with non existing OID: {}",
497+
filter_tip
498+
)));
499+
}) {
500+
continue;
501+
}
502+
// Remove this filter but preserve the others.
489503
let mut f2 = filters.clone();
490-
f2.remove(&id);
504+
f2.remove(&filter_tip);
491505
f2.insert(git2::Oid::zero(), *startfilter);
492506
let op = if f2.len() == 1 {
493507
to_op(*startfilter)
@@ -995,6 +1009,37 @@ pub fn make_permissions_filter(filter: Filter, whitelist: Filter, blacklist: Fil
9951009
opt::optimize(filter)
9961010
}
9971011

1012+
/// Check if `commit` is an ancestor of `tip`.
1013+
///
1014+
/// Creates a cache for a given `tip` so repeated queries with the same `tip` are more efficient.
1015+
fn is_ancestor_of(
1016+
repo: &git2::Repository,
1017+
commit: git2::Oid,
1018+
tip: git2::Oid,
1019+
) -> Result<bool, git2::Error> {
1020+
let mut ancestor_cache = ANCESTORS.lock().unwrap();
1021+
let ancestors = match ancestor_cache.entry(tip) {
1022+
std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(),
1023+
std::collections::hash_map::Entry::Vacant(entry) => {
1024+
tracing::trace!("is_ancestor_of tip={tip}");
1025+
// Recursively compute all ancestors of `tip`.
1026+
// Invariant: Everything in `todo` is also in `ancestors`.
1027+
let mut todo = vec![tip];
1028+
let mut ancestors = std::collections::HashSet::from_iter(todo.iter().copied());
1029+
while let Some(commit) = todo.pop() {
1030+
for parent in repo.find_commit(commit)?.parent_ids() {
1031+
if ancestors.insert(parent) {
1032+
// Newly inserted! Also handle its parents.
1033+
todo.push(parent);
1034+
}
1035+
}
1036+
}
1037+
entry.insert(ancestors)
1038+
}
1039+
};
1040+
Ok(ancestors.contains(&commit))
1041+
}
1042+
9981043
#[cfg(test)]
9991044
mod tests {
10001045
use super::*;

josh-core/src/housekeeping.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,12 @@ pub fn refresh_known_filters(
295295
&to_filtered_ref(upstream_repo, filter_spec),
296296
upstream_repo,
297297
) {
298-
let mut u = filter_refs(
298+
let (mut u, _) = filter_refs(
299299
transaction_overlay,
300300
filter::parse(filter_spec)?,
301301
&[from],
302302
filter::empty(),
303-
)?;
303+
);
304304
u[0].0 = to_ref;
305305
updated_refs.append(&mut u);
306306
}

josh-core/src/lib.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,28 +276,33 @@ pub fn filter_refs(
276276
filterobj: filter::Filter,
277277
refs: &[(String, git2::Oid)],
278278
permissions: filter::Filter,
279-
) -> JoshResult<Vec<(String, git2::Oid)>> {
279+
) -> (Vec<(String, git2::Oid)>, Vec<(String, JoshError)>) {
280280
rs_tracing::trace_scoped!("filter_refs", "spec": filter::spec(filterobj));
281281
let s = tracing::Span::current();
282282
let _e = s.enter();
283283
let mut updated = vec![];
284+
let mut errors = vec![];
284285

285286
tracing::trace!("filter_refs");
286287

287288
for k in refs {
288-
let oid = ok_or!(filter_commit(transaction, filterobj, k.1, permissions), {
289-
tracing::event!(
290-
tracing::Level::WARN,
291-
msg = "filter_refs: Can't filter reference",
292-
warn = true,
293-
from = k.0.as_str(),
294-
);
295-
git2::Oid::zero()
296-
});
289+
let oid = match filter_commit(transaction, filterobj, k.1, permissions) {
290+
Ok(oid) => oid,
291+
Err(e) => {
292+
errors.push((k.0.to_string(), e));
293+
tracing::event!(
294+
tracing::Level::WARN,
295+
msg = "filter_refs: Can't filter reference",
296+
warn = true,
297+
from = k.0.as_str(),
298+
);
299+
git2::Oid::zero()
300+
}
301+
};
297302
updated.push((k.0.to_string(), oid));
298303
}
299304

300-
Ok(updated)
305+
(updated, errors)
301306
}
302307

303308
pub fn update_refs(

josh-filter/src/bin/josh-filter.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,12 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
236236
if i.contains(":workspace=") {
237237
continue;
238238
}
239-
let mut updated_refs = josh::filter_refs(
239+
let (mut updated_refs, _) = josh::filter_refs(
240240
&transaction,
241241
josh::filter::parse(&i)?,
242242
&[(input_ref.to_string(), r.id())],
243243
josh::filter::empty(),
244-
)?;
244+
);
245245
updated_refs[0].0 = "refs/JOSH_TMP".to_string();
246246
josh::update_refs(&transaction, &mut updated_refs, "");
247247
}
@@ -296,7 +296,12 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
296296
git2::Oid::zero()
297297
};
298298

299-
let mut updated_refs = josh::filter_refs(&transaction, filterobj, &refs, permissions_filter)?;
299+
let (mut updated_refs, errors) =
300+
josh::filter_refs(&transaction, filterobj, &refs, permissions_filter);
301+
302+
for error in errors {
303+
return Err(error.1);
304+
}
300305
for i in 0..updated_refs.len() {
301306
if updated_refs[i].0 == input_ref {
302307
if reverse {

josh-proxy/src/bin/josh-proxy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ async fn do_filter(
481481
t2.repo()
482482
.odb()?
483483
.add_disk_alternate(repo_path.join("mirror").join("objects").to_str().unwrap())?;
484-
let updated_refs = josh::filter_refs(&t2, filter, &refs_list, josh::filter::empty())?;
484+
let (updated_refs, _) = josh::filter_refs(&t2, filter, &refs_list, josh::filter::empty());
485485
let mut updated_refs = josh_proxy::refs_locking(updated_refs, &meta);
486486
josh::housekeeping::namespace_refs(&mut updated_refs, temp_ns.name());
487487
josh::update_refs(&t2, &mut updated_refs, &temp_ns.reference(&head_ref));

tests/filter/start.t

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,24 @@
4848
|/
4949
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
5050

51+
$ josh-filter -s ":rev(ffffffffffffffffffffffffffffffffffffffff:prefix=x/y)" --update refs/heads/filtered
52+
[5] :prefix=x
53+
[5] :prefix=y
54+
ERROR: `:rev(...)` with non existing OID: ffffffffffffffffffffffffffffffffffffffff
55+
[1]
56+
5157
$ josh-filter -s ":rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)" --update refs/heads/filtered
5258
[5] :prefix=x
5359
[5] :prefix=y
5460
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
5561
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
56-
* 8b4097f3318cdf47e46266fc7fef5331bf189b6c:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
62+
* 54651c29aa86e8512a7b9d39e3b8ea26da644247:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
5763
|\
5864
| * ee931ac07e4a953d1d2e0f65968946f5c09b0f4c:5d0da4f47308da86193b53b3374f5630c5a0fa3e
5965
| * cc0382917c6488d69dca4d6a147d55251b06ac08:8408d8fc882cba8e945b16bc69e3b475d65ecbeb
60-
| * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
61-
* e707f76bb6a1390f28b2162da5b5eb6933009070:5d8a699f74b48c9c595f4615dd3755244e11d176
62-
* 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72
66+
* | daf46738b8fddd211a1609bf3b9de339fe7589eb:5d8a699f74b48c9c595f4615dd3755244e11d176
67+
|/
68+
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
6369

6470

6571
$ josh-filter -s ":rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)" --update refs/heads/filtered
@@ -68,12 +74,12 @@
6874
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
6975
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
7076
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
71-
* dbc12216fd70cd41937b99940b1f74dde60b4f44:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
77+
* 5fe60a2d55b652822b3d3f25410714e9053ba72b:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
7278
|\
73-
| * 86871b8775ad3baca86484337d1072aa1d386f7e:5d0da4f47308da86193b53b3374f5630c5a0fa3e
74-
| * 975d4c4975912729482cc864d321c5196a969271:de6937d89a7433c80125962616db5dca6c206d9d
75-
| * 0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:3d77ff51363c9825cc2a221fc0ba5a883a1a2c72
76-
* 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
79+
| * 0822879dab0a93f29848500e72642d6c8c0db162:5d0da4f47308da86193b53b3374f5630c5a0fa3e
80+
| * 5c145ed574623e7687f4c7a5d1d40b48687bf17c:de6937d89a7433c80125962616db5dca6c206d9d
81+
* | 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
82+
|/
7783
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
7884
$ cat > filter.josh <<EOF
7985
> :rev(
@@ -104,16 +110,17 @@
104110
> )
105111
> EOF
106112
$ josh-filter -s --file filter.josh --update refs/heads/filtered
113+
[1] :prefix=z
107114
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
108115
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
109116
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
110117
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
111-
[5] :prefix=x
112118
[5] :prefix=y
113119
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
114120
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
115121
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
116122
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
123+
[6] :prefix=x
117124
$ cat > filter.josh <<EOF
118125
> :rev(
119126
> e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y
@@ -122,37 +129,39 @@
122129
> EOF
123130
$ josh-filter -s --file filter.josh --update refs/heads/filtered
124131
Warning: reference refs/heads/filtered wasn't updated
132+
[1] :prefix=z
125133
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
126134
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
127135
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
128136
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
129-
[5] :prefix=x
130137
[5] :prefix=y
131138
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
132139
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
133140
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
134141
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
142+
[6] :prefix=x
135143
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
136-
* e8b8c260e894186db18bffef15da3f5d292902f8:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
144+
* 1c4fe25dc386c77adaae12d6b1cd3abfa296fc3c:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
137145
|\
138-
| * d817c466a639fca29059705144ef9f63e194c3b5:5d0da4f47308da86193b53b3374f5630c5a0fa3e
139-
| * 28b0f8962384c35ff4f370c0fb8d75bc9b035248:b9d380f578c1cb2bb5039977f64ccf1a804a91de
140-
| * 26cbb56df84c5e9fdce7afc7855025862e835ee2:105b58b790c53d350e23a51ad763a88e6b977ae7
141-
* 08158c6ba260a65db99c1e9e6f519e1963dff07b:6d18321f410e431cd446258dd5e01999306d9d44
142-
* 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
146+
| * 17a13131b354b75d39aa29896f0500ac1b5e6764:5d0da4f47308da86193b53b3374f5630c5a0fa3e
147+
| * 8516b8e4396bc91c72cec0038325d82604e8d685:b9d380f578c1cb2bb5039977f64ccf1a804a91de
148+
| * 9f0db868b59a422c114df33bc6a8b2950f80490b:a087bfbdb1a5bad499b40ccd1363d30db1313f54
149+
* 74a368bd558785377d64ecdb3a47f2d1b4f25113:6d18321f410e431cd446258dd5e01999306d9d44
150+
* 26cbb56df84c5e9fdce7afc7855025862e835ee2:105b58b790c53d350e23a51ad763a88e6b977ae7
143151

144152
$ josh-filter -s :linear --update refs/heads/filtered
153+
[1] :prefix=z
145154
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
146155
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,975d4c4975912729482cc864d321c5196a969271:prefix=x/z)
147156
[2] :rev(0000000000000000000000000000000000000000:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
148157
[2] :rev(0000000000000000000000000000000000000000:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
149158
[3] :linear
150-
[5] :prefix=x
151159
[5] :prefix=y
152160
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
153161
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
154162
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
155163
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
164+
[6] :prefix=x
156165
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
157166
* f8e8bc9daf54340c9fce647be467d2577b623bbe:5f47d9fdffdc726bb8ebcfea67531d2574243c5d
158167
* e707f76bb6a1390f28b2162da5b5eb6933009070:5d8a699f74b48c9c595f4615dd3755244e11d176
@@ -188,12 +197,12 @@
188197
[2] :rev(0000000000000000000000000000000000000000:prefix=y,0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z)
189198
[3] :linear
190199
[3] :rev(0000000000000000000000000000000000000000:prefix=x,0b4cf6c9efbbda1eada39fa9c1d21d2525b027bb:prefix=z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=y)
191-
[5] :prefix=x
192200
[5] :prefix=y
193201
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y)
194202
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/y,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
195203
[5] :rev(975d4c4975912729482cc864d321c5196a969271:prefix=x/z,e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
196204
[5] :rev(e707f76bb6a1390f28b2162da5b5eb6933009070:prefix=x/y)
205+
[6] :prefix=x
197206

198207
$ git log --graph --decorate --pretty=%H:%T refs/heads/filtered
199208
* 2944f04c33ea037f7696282bf20b2e570524552e:047b1b6f39e8d95b62ef7f136189005d0e3c80b3

tests/proxy/amend_patchset.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
]
125125
.
126126
|-- josh
127-
| `-- 18
127+
| `-- 19
128128
| `-- sled
129129
| |-- blobs
130130
| |-- conf

tests/proxy/authentication.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124
"real_repo.git" = ["::sub1/"]
125125
.
126126
|-- josh
127-
| `-- 18
127+
| `-- 19
128128
| `-- sled
129129
| |-- blobs
130130
| |-- conf

tests/proxy/caching.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
]
5252
.
5353
|-- josh
54-
| `-- 18
54+
| `-- 19
5555
| `-- sled
5656
| |-- blobs
5757
| |-- conf
@@ -162,7 +162,7 @@
162162
]
163163
.
164164
|-- josh
165-
| `-- 18
165+
| `-- 19
166166
| `-- sled
167167
| |-- blobs
168168
| |-- conf

0 commit comments

Comments
 (0)