2020-09-29 01:04:28 +02:00
|
|
|
use clap::{App, Arg, ArgMatches, SubCommand};
|
2020-11-07 23:06:42 +01:00
|
|
|
use std::collections::{HashSet, VecDeque};
|
2020-09-29 01:04:28 +02:00
|
|
|
use std::fs;
|
2020-11-07 21:26:56 +01:00
|
|
|
use std::io::Write;
|
|
|
|
use std::process::{Command, Stdio};
|
2020-10-05 14:02:20 +02:00
|
|
|
mod base;
|
2020-09-27 14:52:54 +02:00
|
|
|
mod data;
|
2020-09-27 01:25:10 +02:00
|
|
|
|
2020-09-25 18:51:39 +02:00
|
|
|
fn main() {
|
2020-09-27 01:25:10 +02:00
|
|
|
let matches = App::new("rgit vcs")
|
|
|
|
.version("0.1.0")
|
|
|
|
.author("Gonçalo Valério <gon@ovalerio.net>")
|
|
|
|
.about("A watered-down git clone")
|
|
|
|
.subcommand(SubCommand::with_name("init").about("creates new repository"))
|
2020-09-29 01:04:28 +02:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("hash-object")
|
|
|
|
.about("created an hash for an object")
|
|
|
|
.arg(Arg::with_name("file").index(1).required(true)),
|
|
|
|
)
|
2020-09-29 12:08:12 +02:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("cat-file")
|
|
|
|
.about("outputs the original object from the provided hash")
|
|
|
|
.arg(Arg::with_name("hash").index(1).required(true)),
|
|
|
|
)
|
2020-10-05 19:49:44 +02:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("write-tree")
|
|
|
|
.about("write the current working directory to the database"),
|
|
|
|
)
|
2020-10-25 19:35:47 +01:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("read-tree")
|
|
|
|
.about("writes a given tree to the working directory")
|
|
|
|
.arg(Arg::with_name("oid").index(1).required(true)),
|
|
|
|
)
|
2020-10-27 00:56:25 +01:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("commit")
|
|
|
|
.about("writes a named snapshot of the current tree")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("message")
|
|
|
|
.short("m")
|
|
|
|
.value_name("MESSAGE")
|
|
|
|
.takes_value(true),
|
|
|
|
),
|
|
|
|
)
|
2020-11-02 23:38:54 +01:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("log")
|
|
|
|
.about("List all commits")
|
2020-11-06 01:34:29 +01:00
|
|
|
.arg(Arg::with_name("oid").index(1).default_value("@")),
|
2020-11-02 23:38:54 +01:00
|
|
|
)
|
2020-11-03 00:01:31 +01:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("checkout")
|
|
|
|
.about("Move the current content and HEAD to given commit")
|
|
|
|
.arg(Arg::with_name("oid").index(1).required(true)),
|
|
|
|
)
|
2020-11-04 00:49:43 +01:00
|
|
|
.subcommand(
|
|
|
|
SubCommand::with_name("tag")
|
|
|
|
.about("Create a tag for a given commit")
|
|
|
|
.arg(Arg::with_name("name").index(1).required(true))
|
2020-11-06 01:34:29 +01:00
|
|
|
.arg(Arg::with_name("oid").index(2).default_value("@")),
|
2020-11-04 00:49:43 +01:00
|
|
|
)
|
2020-11-07 14:25:11 +01:00
|
|
|
.subcommand(SubCommand::with_name("k").about("visualize refs and commits"))
|
2020-09-27 01:25:10 +02:00
|
|
|
.get_matches();
|
|
|
|
|
2020-09-29 01:04:28 +02:00
|
|
|
match matches.subcommand_name() {
|
|
|
|
Some("init") => init(),
|
|
|
|
Some("hash-object") => hash_object(matches),
|
2020-09-29 12:08:12 +02:00
|
|
|
Some("cat-file") => cat_file(matches),
|
2020-10-05 19:49:44 +02:00
|
|
|
Some("write-tree") => write_tree(),
|
2020-10-25 19:35:47 +01:00
|
|
|
Some("read-tree") => read_tree(matches),
|
2020-10-27 00:56:25 +01:00
|
|
|
Some("commit") => commit(matches),
|
2020-11-02 23:38:54 +01:00
|
|
|
Some("log") => log_commits(matches),
|
2020-11-03 00:01:31 +01:00
|
|
|
Some("checkout") => checkout(matches),
|
2020-11-04 00:49:43 +01:00
|
|
|
Some("tag") => tag(matches),
|
2020-11-07 14:25:11 +01:00
|
|
|
Some("k") => k(),
|
2020-09-29 01:04:28 +02:00
|
|
|
_ => println!("unknown sub command"),
|
2020-09-27 01:25:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init() {
|
2020-09-27 14:52:54 +02:00
|
|
|
match data::init() {
|
|
|
|
Ok(()) => println!("Repository created"),
|
|
|
|
_ => println!("Failed. Perhaps the repository already exists."),
|
|
|
|
}
|
2020-09-25 18:51:39 +02:00
|
|
|
}
|
2020-09-29 01:04:28 +02:00
|
|
|
|
|
|
|
fn hash_object(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("hash-object") {
|
|
|
|
let content = fs::read(cmd_matches.value_of("file").unwrap())
|
|
|
|
.expect("Something went wrong reading the provided file");
|
2020-10-05 13:58:48 +02:00
|
|
|
let hash = data::hash_object(&content, "blob".to_owned());
|
2020-09-29 01:04:28 +02:00
|
|
|
println!("{}", hash);
|
|
|
|
}
|
|
|
|
}
|
2020-09-29 12:08:12 +02:00
|
|
|
|
|
|
|
fn cat_file(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("cat-file") {
|
2020-11-04 22:54:45 +01:00
|
|
|
let hash = base::get_oid(cmd_matches.value_of("hash").unwrap().to_owned());
|
|
|
|
let file_contents = data::get_object(hash, "".to_owned());
|
2020-09-29 12:08:12 +02:00
|
|
|
println!("{}", file_contents)
|
|
|
|
}
|
|
|
|
}
|
2020-10-05 19:49:44 +02:00
|
|
|
|
|
|
|
fn write_tree() {
|
2020-10-13 23:35:19 +02:00
|
|
|
println!("{}", base::write_tree(".".to_owned()));
|
2020-10-05 19:49:44 +02:00
|
|
|
}
|
2020-10-25 19:35:47 +01:00
|
|
|
|
|
|
|
fn read_tree(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("read-tree") {
|
2020-11-04 22:54:45 +01:00
|
|
|
let oid = base::get_oid(cmd_matches.value_of("oid").unwrap().to_owned());
|
|
|
|
base::read_tree(oid);
|
2020-10-25 19:35:47 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-27 00:56:25 +01:00
|
|
|
|
|
|
|
fn commit(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("commit") {
|
|
|
|
let message = cmd_matches.value_of("message").unwrap_or("");
|
|
|
|
println!("{}", base::commit(message));
|
|
|
|
}
|
|
|
|
}
|
2020-11-02 23:29:37 +01:00
|
|
|
|
2020-11-02 23:38:54 +01:00
|
|
|
fn log_commits(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("log") {
|
2020-11-06 01:34:29 +01:00
|
|
|
let provided_ref = cmd_matches.value_of("oid").unwrap().to_owned();
|
2020-11-07 23:06:42 +01:00
|
|
|
let initial_oid = base::get_oid(provided_ref.to_owned());
|
|
|
|
let mut oids = VecDeque::new();
|
|
|
|
oids.push_back(initial_oid);
|
2020-11-02 23:38:54 +01:00
|
|
|
|
2020-11-07 23:06:42 +01:00
|
|
|
for oid in base::iter_commits_and_parents(oids) {
|
2020-11-02 23:38:54 +01:00
|
|
|
let commit = base::get_commit(oid.clone());
|
2020-11-02 23:29:37 +01:00
|
|
|
|
2020-11-02 23:38:54 +01:00
|
|
|
println!("commit {}", oid);
|
|
|
|
println!("{}", commit.message);
|
|
|
|
println!("");
|
2020-11-02 23:29:37 +01:00
|
|
|
|
2020-11-02 23:38:54 +01:00
|
|
|
if commit.parent == "" {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-11-02 23:29:37 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-03 00:01:31 +01:00
|
|
|
|
|
|
|
fn checkout(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("checkout") {
|
2020-11-04 22:54:45 +01:00
|
|
|
let oid = base::get_oid(cmd_matches.value_of("oid").unwrap().to_owned());
|
2020-11-03 00:01:31 +01:00
|
|
|
base::checkout(oid);
|
|
|
|
}
|
|
|
|
}
|
2020-11-04 00:49:43 +01:00
|
|
|
|
|
|
|
fn tag(matches: ArgMatches) {
|
|
|
|
if let Some(cmd_matches) = matches.subcommand_matches("tag") {
|
|
|
|
let name = cmd_matches.value_of("name").unwrap().to_owned();
|
2020-11-06 01:34:29 +01:00
|
|
|
let provided_ref = cmd_matches.value_of("oid").unwrap().to_owned();
|
|
|
|
let oid = base::get_oid(provided_ref.clone());
|
2020-11-04 00:49:43 +01:00
|
|
|
base::create_tag(name, oid);
|
|
|
|
}
|
|
|
|
}
|
2020-11-07 14:25:11 +01:00
|
|
|
|
|
|
|
fn k() {
|
2020-11-07 21:26:56 +01:00
|
|
|
let mut dot = "digraph commits {\n".to_owned();
|
2020-11-07 23:06:42 +01:00
|
|
|
let mut oids = VecDeque::new();
|
2020-11-07 14:25:11 +01:00
|
|
|
for refinfo in data::iter_refs() {
|
2020-11-07 21:26:56 +01:00
|
|
|
dot.push_str(&format!("\"{}\" [shape=note]\n", refinfo.0));
|
|
|
|
dot.push_str(&format!("\"{}\" -> \"{}\"", refinfo.0, refinfo.1));
|
2020-11-07 23:06:42 +01:00
|
|
|
oids.push_back(refinfo.1);
|
2020-11-07 14:25:11 +01:00
|
|
|
}
|
2020-11-07 20:45:58 +01:00
|
|
|
|
|
|
|
for oid in base::iter_commits_and_parents(oids) {
|
|
|
|
let commit = base::get_commit(oid.clone());
|
2020-11-07 21:26:56 +01:00
|
|
|
dot.push_str(&format!(
|
|
|
|
"\"{}\" [shape=box style=filled label=\"{}\"]\n",
|
|
|
|
oid,
|
|
|
|
&oid[0..10]
|
|
|
|
));
|
2020-11-07 20:45:58 +01:00
|
|
|
if commit.parent != "" {
|
|
|
|
println!("Parent: {}", commit.parent);
|
2020-11-07 21:26:56 +01:00
|
|
|
dot.push_str(&format!("\"{}\" -> \"{}\"\n", oid, commit.parent));
|
2020-11-07 20:45:58 +01:00
|
|
|
}
|
|
|
|
}
|
2020-11-07 21:26:56 +01:00
|
|
|
dot.push_str("}");
|
|
|
|
println!("{}", dot);
|
|
|
|
|
|
|
|
let mut child = Command::new("dot")
|
|
|
|
.arg("-Tgtk")
|
|
|
|
.arg("/dev/stdin")
|
|
|
|
.stdin(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.expect("Failed to draw graph");
|
|
|
|
|
|
|
|
{
|
|
|
|
let stdin = child.stdin.as_mut().expect("Cannot get dot stdin");
|
|
|
|
stdin
|
|
|
|
.write_all(dot.into_bytes().as_mut_slice())
|
|
|
|
.expect("failed to write graph data");
|
|
|
|
}
|
|
|
|
let _ = child.wait();
|
2020-11-07 14:25:11 +01:00
|
|
|
}
|