2019-10-11 23:51:17 +03:00
|
|
|
/*
|
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
*
|
|
|
|
* This software may be used and distributed according to the terms of the
|
2020-02-11 13:42:43 +03:00
|
|
|
* GNU General Public License version 2.
|
2019-10-11 23:51:17 +03:00
|
|
|
*/
|
2017-07-28 04:00:19 +03:00
|
|
|
|
2019-12-07 03:26:57 +03:00
|
|
|
use anyhow::{bail, Context, Error, Result};
|
2017-07-28 04:00:19 +03:00
|
|
|
use clap::App;
|
2019-08-14 13:58:11 +03:00
|
|
|
use mercurial_revlog::revlog::Revlog;
|
|
|
|
use std::{fs::File, io::Write, str::FromStr};
|
2017-07-28 04:00:19 +03:00
|
|
|
|
|
|
|
fn run() -> Result<()> {
|
|
|
|
// Define command line args and parse command line
|
|
|
|
let matches = App::new("dumprev")
|
|
|
|
.version("0.0.0")
|
|
|
|
.about("extract a revision from a revlog")
|
|
|
|
.args_from_usage(concat!(
|
|
|
|
"-d, --data=[DATAFILE] 'Data file if not inline'\n",
|
|
|
|
"-w, --write=[DUMPFILE] 'Write data to file'\n",
|
|
|
|
"<IDXFILE> 'index file'\n",
|
|
|
|
"<REV> 'revision index'"
|
|
|
|
))
|
|
|
|
.get_matches();
|
|
|
|
// Get path of index file; `unwrap()` is safe because parameter is non-optional
|
|
|
|
let idxpath = matches.value_of("IDXFILE").unwrap();
|
|
|
|
|
|
|
|
// Get optional datapath
|
|
|
|
let datapath = matches.value_of("DATAFILE");
|
|
|
|
|
|
|
|
// Also optional dumpfile
|
|
|
|
let dumpfile = matches.value_of("write");
|
|
|
|
|
|
|
|
// Get non-optional revision
|
|
|
|
let revidx = FromStr::from_str(matches.value_of("REV").unwrap())
|
2017-12-06 04:52:31 +03:00
|
|
|
.map_err(Error::from)
|
2018-03-20 21:42:43 +03:00
|
|
|
.context("idx malformed")?;
|
2017-07-28 04:00:19 +03:00
|
|
|
|
|
|
|
// Construct a `Revlog`
|
2017-12-06 04:52:31 +03:00
|
|
|
let revlog =
|
2018-03-20 21:42:43 +03:00
|
|
|
Revlog::from_idx_with_data(idxpath, datapath).context("failed to load idx and data")?;
|
2017-07-28 04:00:19 +03:00
|
|
|
println!("made revlog {:?}", revlog.get_header());
|
|
|
|
|
2018-03-20 21:42:43 +03:00
|
|
|
let entry = revlog.get_entry(revidx).context("failed to get entry")?;
|
2017-07-28 04:00:19 +03:00
|
|
|
|
|
|
|
println!("Revlog[{:?}] = {:?}", revidx, entry);
|
|
|
|
match revlog.get_rev(revidx) {
|
2018-08-20 01:42:58 +03:00
|
|
|
Ok(ref rev) => {
|
2019-02-07 01:58:25 +03:00
|
|
|
if entry.nodeid() != rev.nodeid() {
|
2017-07-28 04:00:19 +03:00
|
|
|
println!(
|
|
|
|
"NOTE: hash mismatch: expected {}, got {}",
|
|
|
|
entry.nodeid(),
|
2018-08-20 01:42:58 +03:00
|
|
|
rev.nodeid()
|
2017-07-28 04:00:19 +03:00
|
|
|
)
|
|
|
|
}
|
2018-08-20 01:42:58 +03:00
|
|
|
let revdata = rev.as_blob().as_slice();
|
|
|
|
if let Some(dumpfile) = dumpfile {
|
|
|
|
let mut file = match File::create(dumpfile) {
|
|
|
|
Ok(file) => file,
|
2019-12-05 21:10:52 +03:00
|
|
|
Err(err) => bail!("Failed to create file {}: {:?}", dumpfile, err),
|
2018-08-20 01:42:58 +03:00
|
|
|
};
|
|
|
|
println!("Writing rev {:?} to {}", rev.nodeid(), dumpfile);
|
|
|
|
if let Err(err) = file.write_all(revdata) {
|
2019-12-05 21:10:52 +03:00
|
|
|
bail!("Failed to write {}: {:?}", dumpfile, err);
|
2017-07-28 04:00:19 +03:00
|
|
|
}
|
|
|
|
} else {
|
2018-08-20 01:42:58 +03:00
|
|
|
println!(
|
|
|
|
"rev {:?}:\n{}",
|
|
|
|
rev.nodeid(),
|
|
|
|
String::from_utf8_lossy(revdata)
|
|
|
|
);
|
2017-07-28 04:00:19 +03:00
|
|
|
}
|
|
|
|
}
|
2019-12-05 21:10:52 +03:00
|
|
|
Err(err) => bail!("failed to get chunk {:?}: {}", revidx, err),
|
2017-07-28 04:00:19 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
if let Err(ref e) = run() {
|
|
|
|
println!("Failed: {}", e);
|
|
|
|
|
2019-11-22 19:55:54 +03:00
|
|
|
for e in e.chain() {
|
2017-07-28 04:00:19 +03:00
|
|
|
println!("caused by: {}", e);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|