Skip to content

Commit 06fe4b4

Browse files
Don't serve all refs when explicit @sha is specified
commit-id:0a298689
1 parent 7470f8d commit 06fe4b4

File tree

2 files changed

+123
-51
lines changed

2 files changed

+123
-51
lines changed

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

+93-45
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,16 @@ async fn do_filter(
338338
meta: josh_proxy::MetaConfig,
339339
temp_ns: Arc<josh_proxy::TmpGitNamespace>,
340340
filter: josh::filter::Filter,
341-
headref: String,
341+
head_ref: &HeadRef,
342342
) -> josh::JoshResult<()> {
343343
let permit = service.filter_permits.acquire().await;
344344
let heads_map = service.heads_map.clone();
345345

346-
let s = tracing::span!(tracing::Level::TRACE, "do_filter worker");
347-
let r = tokio::task::spawn_blocking(move || {
348-
let _e = s.enter();
346+
let tracing_span = tracing::span!(tracing::Level::TRACE, "do_filter worker");
347+
let head_ref = head_ref.clone();
348+
349+
tokio::task::spawn_blocking(move || {
350+
let _span_guard = tracing_span.enter();
349351
tracing::trace!("in do_filter worker");
350352
let filter_spec = josh::filter::spec(filter);
351353
josh::housekeeping::remember_filter(&meta.config.repo, &filter_spec);
@@ -357,57 +359,86 @@ async fn do_filter(
357359
&josh::to_ns(&meta.config.repo),
358360
)),
359361
)?;
360-
let mut refslist = josh::housekeeping::list_refs(transaction.repo(), &meta.config.repo)?;
361362

362-
let mut headref = headref;
363-
364-
if headref.starts_with("refs/") || headref == "HEAD" {
365-
let name = format!(
363+
let resolve_ref = |ref_value: &str| {
364+
let josh_name = format!(
366365
"refs/josh/upstream/{}/{}",
367366
&josh::to_ns(&meta.config.repo),
368-
headref
367+
ref_value
369368
);
370-
if let Ok(r) = transaction.repo().revparse_single(&name) {
371-
refslist.push((headref.clone(), r.id()));
369+
370+
transaction
371+
.repo()
372+
.revparse_single(&josh_name)
373+
.map_err(|e| josh_error(&format!("Could not find ref: {}", e)))
374+
};
375+
376+
let (refs_list, head_ref) = match head_ref {
377+
HeadRef::Explicit(ref_value)
378+
if ref_value.starts_with("refs/") || ref_value == "HEAD" =>
379+
{
380+
let object = resolve_ref(&ref_value)?;
381+
let list = vec![(ref_value.clone(), object.id())];
382+
383+
(list, ref_value.clone())
372384
}
373-
} else {
374-
// @sha case
375-
refslist.push((headref.clone(), git2::Oid::from_str(&headref)?));
376-
headref = format!("refs/heads/_{}", headref);
377-
}
385+
HeadRef::Explicit(ref_value) => {
386+
// When it's not something starting with refs/ or HEAD, it's
387+
// probably sha1
388+
let list = vec![(ref_value.clone(), git2::Oid::from_str(&ref_value)?)];
389+
let synthetic_ref = format!("refs/heads/_{}", ref_value);
378390

379-
if headref == "HEAD" {
380-
headref = heads_map
391+
(list, synthetic_ref)
392+
}
393+
HeadRef::Implicit => {
394+
// When user did not explicitly request a ref to filter,
395+
// start with a list of all existing refs
396+
let mut list =
397+
josh::housekeeping::list_refs(transaction.repo(), &meta.config.repo)?;
398+
399+
let head_ref = head_ref.get().to_string();
400+
if let Ok(object) = resolve_ref(&head_ref) {
401+
list.push((head_ref.clone(), object.id()));
402+
}
403+
404+
(list, head_ref)
405+
}
406+
};
407+
408+
let head_ref = if head_ref == "HEAD" {
409+
heads_map
381410
.read()?
382411
.get(&meta.config.repo)
383412
.unwrap_or(&"invalid".to_string())
384-
.clone();
385-
}
413+
.clone()
414+
} else {
415+
head_ref
416+
};
386417

387418
let t2 = josh::cache::Transaction::open(&repo_path.join("overlay"), None)?;
388419
t2.repo()
389420
.odb()?
390421
.add_disk_alternate(repo_path.join("mirror").join("objects").to_str().unwrap())?;
391-
let updated_refs = josh::filter_refs(&t2, filter, &refslist, josh::filter::empty())?;
422+
let updated_refs = josh::filter_refs(&t2, filter, &refs_list, josh::filter::empty())?;
392423
let mut updated_refs = josh_proxy::refs_locking(updated_refs, &meta);
393424
josh::housekeeping::namespace_refs(&mut updated_refs, temp_ns.name());
394-
josh::update_refs(&t2, &mut updated_refs, &temp_ns.reference(&headref));
425+
josh::update_refs(&t2, &mut updated_refs, &temp_ns.reference(&head_ref));
395426
t2.repo()
396427
.reference_symbolic(
397428
&temp_ns.reference("HEAD"),
398-
&temp_ns.reference(&headref),
429+
&temp_ns.reference(&head_ref),
399430
true,
400431
"",
401432
)
402433
.ok();
403434

404-
Ok(())
435+
Ok::<_, JoshError>(())
405436
})
406-
.await?;
437+
.await??;
407438

408439
std::mem::drop(permit);
409440

410-
r
441+
Ok(())
411442
}
412443

413444
fn make_response(body: hyper::Body, code: hyper::StatusCode) -> Response<hyper::Body> {
@@ -792,15 +823,31 @@ fn is_repo_blocked(meta: &MetaConfig) -> bool {
792823
false
793824
}
794825

795-
fn headref_or_default(headref: &str) -> String {
796-
let result = headref
826+
#[derive(Clone, Debug)]
827+
enum HeadRef {
828+
Explicit(String),
829+
Implicit,
830+
}
831+
832+
impl HeadRef {
833+
// Sometimes we don't care about whether it's implicit or explicit
834+
fn get(&self) -> &str {
835+
match self {
836+
HeadRef::Explicit(r) => &r,
837+
HeadRef::Implicit => "HEAD",
838+
}
839+
}
840+
}
841+
842+
fn head_ref_or_default(head_ref: &str) -> HeadRef {
843+
let result = head_ref
797844
.trim_start_matches(|char| char == '@' || char == '^')
798845
.to_owned();
799846

800847
if result.is_empty() {
801-
"HEAD".to_string()
848+
HeadRef::Implicit
802849
} else {
803-
result
850+
HeadRef::Explicit(result)
804851
}
805852
}
806853

@@ -890,9 +937,9 @@ async fn handle_serve_namespace_request(
890937
};
891938

892939
let remote_url = upstream + meta_config.config.repo.as_str();
893-
let headref = headref_or_default(&parsed_url.headref);
940+
let head_ref = head_ref_or_default(&parsed_url.headref);
894941

895-
let remote_refs = [headref.as_str()];
942+
let remote_refs = [head_ref.get()];
896943
let remote_refs = match ssh_list_refs(&remote_url, auth_socket, Some(&remote_refs)).await {
897944
Ok(remote_refs) => remote_refs,
898945
Err(e) => {
@@ -903,7 +950,7 @@ async fn handle_serve_namespace_request(
903950
}
904951
};
905952

906-
let resolved_ref = match remote_refs.get(&headref) {
953+
let resolved_ref = match remote_refs.get(head_ref.get()) {
907954
Some(resolved_ref) => resolved_ref,
908955
None => {
909956
return Ok(make_response(
@@ -918,7 +965,7 @@ async fn handle_serve_namespace_request(
918965
meta_config.config.repo.to_owned(),
919966
&remote_auth,
920967
remote_url.to_owned(),
921-
Some(&headref),
968+
Some(head_ref.get()),
922969
Some(resolved_ref),
923970
false,
924971
)
@@ -977,7 +1024,7 @@ async fn handle_serve_namespace_request(
9771024
},
9781025
);
9791026

980-
let temp_ns = match prepare_namespace(serv.clone(), &meta_config, filter, &headref).await {
1027+
let temp_ns = match prepare_namespace(serv.clone(), &meta_config, filter, &head_ref).await {
9811028
Ok(ns) => ns,
9821029
Err(e) => {
9831030
return Ok(make_response(
@@ -1151,13 +1198,13 @@ async fn call_service(
11511198
.await??);
11521199
}
11531200

1154-
let headref = headref_or_default(&parsed_url.headref);
1201+
let headref = head_ref_or_default(&parsed_url.headref);
11551202
match fetch_upstream(
11561203
serv.clone(),
11571204
meta.config.repo.to_owned(),
11581205
&remote_auth,
11591206
remote_url.to_owned(),
1160-
Some(&headref),
1207+
Some(headref.get()),
11611208
None,
11621209
false,
11631210
)
@@ -1184,7 +1231,7 @@ async fn call_service(
11841231
req.uri().query().map(|x| x.to_string()),
11851232
parsed_url.pathinfo.is_empty(),
11861233
) {
1187-
return serve_query(serv, q, meta.config.repo, filter, headref).await;
1234+
return serve_query(serv, q, meta.config.repo, filter, headref.get()).await;
11881235
}
11891236

11901237
let temp_ns = prepare_namespace(serv.clone(), &meta, filter, &headref)
@@ -1265,11 +1312,12 @@ async fn serve_query(
12651312
q: String,
12661313
upstream_repo: String,
12671314
filter: josh::filter::Filter,
1268-
headref: String,
1315+
head_ref: &str,
12691316
) -> josh::JoshResult<Response<hyper::Body>> {
1270-
let s = tracing::span!(tracing::Level::TRACE, "render worker");
1317+
let tracing_span = tracing::span!(tracing::Level::TRACE, "render worker");
1318+
let head_ref = head_ref.to_string();
12711319
let res = tokio::task::spawn_blocking(move || -> josh::JoshResult<_> {
1272-
let _e = s.enter();
1320+
let _span_guard = tracing_span.enter();
12731321

12741322
let transaction_mirror = josh::cache::Transaction::open(
12751323
&serv.repo_path.join("mirror"),
@@ -1290,7 +1338,7 @@ async fn serve_query(
12901338

12911339
let commit_id = transaction_mirror
12921340
.repo()
1293-
.refname_to_id(&transaction_mirror.refname(&headref))?;
1341+
.refname_to_id(&transaction_mirror.refname(&head_ref))?;
12941342
let commit_id =
12951343
josh::filter_commit(&transaction, filter, commit_id, josh::filter::empty())?;
12961344

@@ -1319,7 +1367,7 @@ async fn prepare_namespace(
13191367
serv: Arc<JoshProxyService>,
13201368
meta: &josh_proxy::MetaConfig,
13211369
filter: josh::filter::Filter,
1322-
headref: &str,
1370+
head_ref: &HeadRef,
13231371
) -> josh::JoshResult<std::sync::Arc<josh_proxy::TmpGitNamespace>> {
13241372
let temp_ns = Arc::new(josh_proxy::TmpGitNamespace::new(
13251373
&serv.repo_path.join("overlay"),
@@ -1334,7 +1382,7 @@ async fn prepare_namespace(
13341382
meta.clone(),
13351383
temp_ns.to_owned(),
13361384
filter,
1337-
headref.to_string(),
1385+
head_ref,
13381386
)
13391387
.await?;
13401388

tests/proxy/clone_sha.t

+30-6
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@
2828

2929
1 directory, 1 file
3030

31-
$ git push
32-
To http://localhost:8001/real_repo.git
33-
* [new branch] master -> master
31+
$ git push -q
32+
33+
$ git switch -q -c branch-1
34+
$ echo contents2 >> sub1/file1
35+
$ git add sub1/file1
36+
$ git commit -q -m "edit file1"
37+
$ git push -q origin branch-1
38+
$ git switch -q master
3439

3540
$ git show HEAD
3641
commit bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
@@ -49,15 +54,25 @@
4954

5055
$ cd ${TESTTMP}
5156

57+
Checks the following:
58+
59+
1) Two different formats for separating origin ref in the remote URL
60+
2) Ensures that using @sha works
61+
3) Ensures extra refs are not filtered, and only the requested ref is served
62+
5263
$ git ls-remote http://localhost:8002/real_repo.git@bb282e9cdc1b972fffd08fd21eead43bc0c83cb8:/.git | tr '\t' ' '
5364
bb282e9cdc1b972fffd08fd21eead43bc0c83cb8 HEAD
5465
bb282e9cdc1b972fffd08fd21eead43bc0c83cb8 refs/heads/_bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
55-
bb282e9cdc1b972fffd08fd21eead43bc0c83cb8 refs/heads/master
5666

5767
$ git ls-remote http://localhost:8002/real_repo.git^bb282e9cdc1b972fffd08fd21eead43bc0c83cb8:/.git | tr '\t' ' '
5868
bb282e9cdc1b972fffd08fd21eead43bc0c83cb8 HEAD
5969
bb282e9cdc1b972fffd08fd21eead43bc0c83cb8 refs/heads/_bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
60-
bb282e9cdc1b972fffd08fd21eead43bc0c83cb8 refs/heads/master
70+
71+
Check (2) and (3) but with a branch ref
72+
73+
$ git ls-remote http://localhost:8002/real_repo.git^refs/heads/branch-1:/.git | tr '\t' ' '
74+
36c6ab9d481503e14a88f783e87f3791aa8cef99 HEAD
75+
36c6ab9d481503e14a88f783e87f3791aa8cef99 refs/heads/branch-1
6176

6277
$ git clone -q http://localhost:8002/real_repo.git@bb282e9cdc1b972fffd08fd21eead43bc0c83cb8:/.git full_repo
6378

@@ -91,14 +106,22 @@
91106
| |-- info
92107
| | `-- exclude
93108
| |-- objects
109+
| | |-- 36
110+
| | | `-- c6ab9d481503e14a88f783e87f3791aa8cef99
94111
| | |-- 3d
95112
| | | `-- 77ff51363c9825cc2a221fc0ba5a883a1a2c72
113+
| | |-- 55
114+
| | | `-- a6786cdd5f290477fefa01bb0916555193b005
96115
| | |-- a0
97116
| | | `-- 24003ee1acc6bf70318a46e7b6df651b9dc246
98117
| | |-- bb
99118
| | | `-- 282e9cdc1b972fffd08fd21eead43bc0c83cb8
100119
| | |-- c8
101120
| | | `-- 2fc150c43f13cc56c0e9caeba01b58ec612022
121+
| | |-- d0
122+
| | | `-- 6477a050061f481e7bdbbff347d513ed321c32
123+
| | |-- df
124+
| | | `-- 698318ca7a8dbab2f16ae1d43f693fd6ff1262
102125
| | |-- info
103126
| | `-- pack
104127
| `-- refs
@@ -110,6 +133,7 @@
110133
| | |-- bb282e9cdc1b972fffd08fd21eead43bc0c83cb8
111134
| | `-- refs
112135
| | `-- heads
136+
| | |-- branch-1
113137
| | `-- master
114138
| `-- tags
115139
`-- overlay
@@ -126,5 +150,5 @@
126150
|-- namespaces
127151
`-- tags
128152

129-
30 directories, 18 files
153+
34 directories, 23 files
130154

0 commit comments

Comments
 (0)