Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Paste
P1267
Command-Line Input
Active
Public
Actions
Authored by
olasd
on Jan 26 2022, 9:46 PM.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Flag For Later
Tags
None
Subscribers
None
use
anyhow
::
Result
;
use
tempfile
::
TempDir
;
use
std
::
{
env
,
io
,
io
::
BufRead
,
path
::
PathBuf
,
sync
::
{
atomic
::
AtomicBool
,
Arc
},
};
use
git_repository
::
{
hash
::
ObjectId
,
objs
::
bstr
::
{
BString
,
ByteSlice
},
odb
::
pack
,
protocol
::
{
credentials
,
fetch
,
fetch
::
{
Action
,
Arguments
,
Delegate
,
DelegateBlocking
,
LsRefsAction
,
Ref
,
Response
},
transport
,
transport
::
client
::
Capabilities
,
FetchConnection
,
},
Progress
,
};
use
quick_error
::
quick_error
;
quick_error
!
{
#[derive(Debug)]
enum
Error
{
DecodeObject
(
err
:
git_repository
::
objs
::
decode
::
Error
)
{
display
(
"Could not decode object"
)
source
(
err
)
from
()
}
}
}
pub
struct
Context
<
W
>
{
pub
thread_limit
:
Option
<
usize
>
,
pub
should_interrupt
:
Arc
<
AtomicBool
>
,
pub
out
:
W
,
pub
object_hash
:
git_repository
::
hash
::
Kind
,
}
struct
FetchDelegate
<
W
>
{
ctx
:
Context
<
W
>
,
directory
:
Option
<
PathBuf
>
,
ref_filter
:
Option
<&
'static
[
&
'static
str
]
>
,
wanted_refs
:
Vec
<
BString
>
,
detected_version
:
Option
<
transport
::
Protocol
>
,
fetched_packfile
:
Option
<
PathBuf
>
,
}
static
FILTER
:
&
[
&
str
]
=
&
[
"HEAD"
,
"refs/tags"
,
"refs/heads"
,
"refs/pull"
,
"refs/merge-requests"
,
];
fn
remote_supports_ref_in_want
(
server
:
&
Capabilities
)
->
bool
{
server
.
capability
(
"fetch"
)
.
and_then
(
|
cap
|
cap
.
supports
(
"ref-in-want"
))
.
unwrap_or
(
false
)
}
impl
<
W
>
DelegateBlocking
for
FetchDelegate
<
W
>
{
fn
handshake_extra_parameters
(
&
self
)
->
Vec
<
(
String
,
Option
<
String
>
)
>
{
Vec
::
new
()
}
fn
prepare_ls_refs
(
&
mut
self
,
server
:
&
Capabilities
,
arguments
:
&
mut
Vec
<
BString
>
,
_features
:
&
mut
Vec
<
(
&
str
,
Option
<&
str
>
)
>
,
)
->
io
::
Result
<
LsRefsAction
>
{
if
server
.
contains
(
"ls-refs"
)
{
arguments
.
extend
(
FILTER
.
iter
().
map
(
|
r
|
format
!
(
"ref-prefix {}"
,
r
).
into
()));
}
Ok
(
if
self
.
wanted_refs
.
is_empty
()
{
LsRefsAction
::
Continue
}
else
{
LsRefsAction
::
Skip
})
}
fn
prepare_fetch
(
&
mut
self
,
version
:
transport
::
Protocol
,
server
:
&
Capabilities
,
_features
:
&
mut
Vec
<
(
&
str
,
Option
<&
str
>
)
>
,
_refs
:
&
[
Ref
],
)
->
io
::
Result
<
Action
>
{
if
!
self
.
wanted_refs
.
is_empty
()
&&
!
remote_supports_ref_in_want
(
server
)
{
return
Err
(
io
::
Error
::
new
(
io
::
ErrorKind
::
Other
,
"Want to get specific refs, but remote doesn't support this capability"
,
));
}
if
version
==
transport
::
Protocol
::
V1
{
self
.
ref_filter
=
Some
(
FILTER
);
}
self
.
detected_version
=
Some
(
version
);
Ok
(
Action
::
Continue
)
}
fn
negotiate
(
&
mut
self
,
refs
:
&
[
Ref
],
arguments
:
&
mut
Arguments
,
_previous_response
:
Option
<&
Response
>
,
)
->
io
::
Result
<
Action
>
{
if
self
.
detected_version
==
Some
(
transport
::
Protocol
::
V2
)
{
if
arguments
.
can_use_filter
()
{
arguments
.
filter
(
"tree:0"
)
}
}
if
self
.
wanted_refs
.
is_empty
()
{
for
r
in
refs
{
let
(
path
,
id
)
=
r
.
unpack
();
match
self
.
ref_filter
{
Some
(
ref_prefixes
)
=>
{
if
ref_prefixes
.
iter
()
.
any
(
|
prefix
|
path
.
starts_with_str
(
prefix
))
{
arguments
.
want
(
id
);
}
}
None
=>
arguments
.
want
(
id
),
}
}
}
else
{
for
r
in
&
self
.
wanted_refs
{
arguments
.
want_ref
(
r
.
as_ref
())
}
}
Ok
(
Action
::
Cancel
)
}
}
impl
<
W
:
std
::
io
::
Write
>
Delegate
for
FetchDelegate
<
W
>
{
fn
receive_pack
(
&
mut
self
,
input
:
impl
BufRead
,
progress
:
impl
Progress
,
refs
:
&
[
Ref
],
_previous_response
:
&
Response
,
)
->
io
::
Result
<
()
>
{
let
options
=
pack
::
bundle
::
write
::
Options
{
thread_limit
:
self
.
ctx
.
thread_limit
,
index_kind
:
pack
::
index
::
Version
::
V2
,
iteration_mode
:
pack
::
data
::
input
::
Mode
::
Verify
,
object_hash
:
self
.
ctx
.
object_hash
,
};
let
outcome
=
pack
::
Bundle
::
write_to_directory
(
input
,
self
.
directory
.
take
(),
progress
,
&
self
.
ctx
.
should_interrupt
,
None
,
options
,
)
.
map_err
(
|
err
|
io
::
Error
::
new
(
io
::
ErrorKind
::
Other
,
err
))
?
;
self
.
fetched_packfile
=
outcome
.
data_path
;
Ok
(())
}
}
fn
print_hash_and_path
(
out
:
&
mut
impl
io
::
Write
,
name
:
&
str
,
id
:
ObjectId
,
path
:
Option
<
PathBuf
>
,
)
->
io
::
Result
<
()
>
{
match
path
{
Some
(
path
)
=>
writeln
!
(
out
,
"{}: {} ({})"
,
name
,
id
,
path
.
display
()),
None
=>
writeln
!
(
out
,
"{}: {}"
,
name
,
id
),
}
}
fn
print
(
out
:
&
mut
impl
io
::
Write
,
res
:
pack
::
bundle
::
write
::
Outcome
,
refs
:
&
[
Ref
],
)
->
io
::
Result
<
()
>
{
print_hash_and_path
(
out
,
"index"
,
res
.
index
.
index_hash
,
res
.
index_path
)
?
;
print_hash_and_path
(
out
,
"pack"
,
res
.
index
.
data_hash
,
res
.
data_path
)
?
;
writeln
!
(
out
)
?
;
for
r
in
refs
{
match
r
{
Ref
::
Direct
{
path
,
object
}
=>
writeln
!
(
out
,
"{} {}"
,
object
.
to_hex
(),
path
),
Ref
::
Peeled
{
path
,
object
,
tag
}
=>
{
writeln
!
(
out
,
"{} {} tag:{}"
,
object
.
to_hex
(),
path
,
tag
)
}
Ref
::
Symbolic
{
path
,
target
,
object
,
}
=>
{
writeln
!
(
out
,
"{} {} symref-target:{}"
,
object
.
to_hex
(),
path
,
target
)
}
}
?
;
}
Ok
(())
}
fn
main
()
->
Result
<
()
>
{
for
argument
in
env
::
args
().
skip
(
1
)
{
let
url
=
argument
.
as_bytes
();
let
client
=
transport
::
connect
(
url
,
transport
::
Protocol
::
V2
)
?
;
println
!
(
"Connected with transport {:?}"
,
client
.
supported_protocol_versions
()
);
let
tmp_dir
=
TempDir
::
new
()
?
;
let
mut
delegate
=
FetchDelegate
{
ctx
:
Context
{
should_interrupt
:
Arc
::
new
(
AtomicBool
::
new
(
false
)),
out
:
std
::
io
::
stderr
(),
thread_limit
:
None
,
object_hash
:
git_repository
::
hash
::
Kind
::
Sha1
,
},
directory
:
Some
(
tmp_dir
.
path
().
into
()),
ref_filter
:
None
,
wanted_refs
:
vec
!
[],
detected_version
:
None
,
fetched_packfile
:
None
,
};
fetch
(
client
,
&
mut
delegate
,
credentials
::
helper
,
git_repository
::
progress
::
Discard
,
FetchConnection
::
AllowReuse
,
)
?
;
let
pack_path
=
delegate
.
fetched_packfile
.
expect
(
"fetched a packfile"
);
let
bundle
=
git_pack
::
Bundle
::
at
(
pack_path
,
delegate
.
ctx
.
object_hash
)
?
;
let
_result
:
Result
<
_
,
git_pack
::
index
::
traverse
::
Error
<
Error
>>
=
bundle
.
index
.
traverse
(
&
bundle
.
pack
,
git_repository
::
progress
::
Discard
,
&
delegate
.
ctx
.
should_interrupt
,
move
||
{
move
|
object_kind
,
buf
,
index_entry
,
progress
|
{
let
obj
=
git_repository
::
objs
::
Data
::
new
(
object_kind
,
buf
)
.
decode
()
.
map_err
(
|
e
|
Error
::
DecodeObject
(
e
))
?
;
println
!
(
"{:#?}"
,
obj
);
Ok
(())
}
},
git_pack
::
index
::
traverse
::
Options
{
traversal
:
git_pack
::
index
::
traverse
::
Algorithm
::
Lookup
,
thread_limit
:
delegate
.
ctx
.
thread_limit
,
check
:
pack
::
index
::
traverse
::
SafetyCheck
::
All
,
make_pack_lookup_cache
:
git_pack
::
cache
::
lru
::
StaticLinkedList
::
<
64
>
::
default
,
},
);
}
Ok
(())
}
// let capabilities = res.capabilities.clone();
// match res.actual_protocol {
// Protocol::V2 => {
// drop(res);
// let base_client_options = [
// ("agent", Some("swh.loader.git.rs/0.0.0devel")),
// ("object-format", Some("sha1")),
// ];
// let ls_refs = client.invoke(
// "ls-refs",
// base_client_options.iter().cloned(),
// Some(
// [
// "peel",
// "symrefs",
// "ref-prefix HEAD",
// "ref-prefix refs/heads",
// "ref-prefix refs/tags",
// ]
// .iter()
// .map(|s| s.as_bytes().as_bstr().to_owned()),
// ),
// )?;
// let listed_refs: HashMap<String, (String, Option<String>)> = ls_refs
// .lines()
// .flat_map(|l| {
// l.map(|s| {
// s.trim()
// .split_once(' ')
// .map(|(head, tail)| match tail.split_once(' ') {
// None => (tail.to_string(), (head.to_string(), None)),
// Some((refname, opts)) => (
// refname.to_string(),
// (head.to_string(), Some(opts.to_string())),
// ),
// })
// .unwrap()
// })
// })
// .collect();
// println!("{:#?}\n\n", listed_refs);
// let mut fetch_arguments = vec![];
// if capabilities
// .capability("fetch")
// .as_ref()
// .map(|cap| cap.supports("filter").unwrap_or(false))
// .unwrap_or(false)
// {
// fetch_arguments.push("filter tree:0".to_string());
// }
// fetch_arguments.extend(
// listed_refs
// .iter()
// .map(|(_refname, (head, opts))| match opts {
// None => Some(head.as_str()),
// Some(ref opts) => opts.strip_prefix("peeled:"),
// })
// .flatten()
// .unique()
// .map(|wanted| format!("want {}", wanted)),
// );
// fetch_arguments.push("done".to_string());
// eprintln!("{:#?}", fetch_arguments);
// let mut fetcher = client.invoke(
// "fetch",
// base_client_options.iter().cloned(),
// Some(
// fetch_arguments
// .iter()
// .map(|s| s.as_bytes().as_bstr().to_owned()),
// ),
// )?;
// let mut buf = String::new();
// fetcher.read_line(&mut buf)?;
// if buf != "packfile\n" {
// panic!("Unexpected read: {}", buf)
// }
// fetcher.set_progress_handler(Some(Box::new(|_is_err, data| {
// eprintln!("{}", std::str::from_utf8(data).expect("valid utf8"))
// })));
// let tmp_dir = TempDir::new()?;
// let bundle = git_pack::Bundle::write_to_directory(
// fetcher,
// Some(tmp_dir.path()),
// git_features::progress::Discard,
// &AtomicBool::new(false),
// None,
// git_pack::bundle::write::Options::default(),
// )
// .map(|r| r.to_bundle())?;
// }
// Protocol::V1 => {}
// };
// }
Event Timeline
olasd
created this paste.
Jan 26 2022, 9:46 PM
2022-01-26 21:46:21 (UTC+1)
Log In to Comment