Skip to content

Commit

Permalink
implement git-init
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Jun 8, 2018
1 parent 5cd746b commit 57737c2
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 18 deletions.
9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
[package]
name = "grit-cli"
version = "1.0.0"
authors = ["Sebastian Thiel <sthiel@thoughtworks.com>"]
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
publish = false

[dependencies]
failure = "0.1.1"
failure-tools = "4.0.2"
clap = "2.31.2"

[dependencies.git-core]
path = "lib/git-core"
version = "0.1.0"

[[bin]]
name="grit"
Expand All @@ -15,3 +20,5 @@ path="src/main.rs"
[profile.release]
panic = 'unwind'
incremental = false

[workspace]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The CLI uses various libraries to implement

* **git-core**
* **Repository**
* [ ] initialize
* [x] initialize
* [ ] references
* [ ] index
* [ ] odb
Expand Down
8 changes: 8 additions & 0 deletions lib/git-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "git-core"
version = "0.1.0"
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
publish = false

[dependencies]
failure = "0.1.1"
138 changes: 138 additions & 0 deletions lib/git-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#[macro_use]
extern crate failure;

use failure::{Error, ResultExt};
use std::{fs::{create_dir, OpenOptions}, io::Write, path::Path, path::PathBuf};

const GIT_DIR_NAME: &'static str = ".git";

const TPL_INFO_EXCLUDE: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/info/exclude");
const TPL_HOOKS_APPLYPATCH_MSG: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/applypatch-msg.sample");
const TPL_HOOKS_COMMIT_MSG: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/commit-msg.sample");
const TPL_HOOKS_FSMONITOR_WATCHMAN: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/fsmonitor-watchman.sample");
const TPL_HOOKS_POST_UPDATE: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/post-update.sample");
const TPL_HOOKS_PRE_APPLYPATCH: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/pre-applypatch.sample");
const TPL_HOOKS_PRE_COMMIT: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/pre-commit.sample");
const TPL_HOOKS_PRE_PUSH: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/pre-push.sample");
const TPL_HOOKS_PRE_REBASE: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/pre-rebase.sample");
const TPL_HOOKS_PRE_RECEIVE: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/pre-receive.sample");
const TPL_HOOKS_PREPARE_COMMIT_MSG: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/prepare-commit-msg.sample");
const TPL_HOOKS_UPDATE: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/hooks/update.sample");
const TPL_CONFIG: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/config");
const TPL_DESCRIPTION: &'static [u8] =
include_bytes!("../../../tests/snapshots/cli/baseline-init/description");
const TPL_HEAD: &'static [u8] = include_bytes!("../../../tests/snapshots/cli/baseline-init/HEAD");

struct PathCursor<'a>(&'a mut PathBuf);
struct NewDir<'a>(&'a mut PathBuf);

impl<'a> PathCursor<'a> {
fn at(&mut self, component: &str) -> &Path {
self.0.push(component);
self.0.as_path()
}
}

impl<'a> NewDir<'a> {
fn at(self, component: &str) -> Result<Self, Error> {
self.0.push(component);
create_dir(&self.0)?;
Ok(self)
}
fn as_mut(&mut self) -> &mut PathBuf {
self.0
}
}

impl<'a> Drop for NewDir<'a> {
fn drop(&mut self) {
self.0.pop();
}
}

impl<'a> Drop for PathCursor<'a> {
fn drop(&mut self) {
self.0.pop();
}
}

fn write_file(data: &[u8], path: &Path) -> Result<(), Error> {
let mut file = OpenOptions::new()
.write(true)
.create(true)
.append(false)
.open(path)?;
file.write_all(data)
.with_context(|_| format!("Could not initialize file at '{}'", path.display()))
.map_err(Into::into)
}

pub fn init() -> Result<(), Error> {
let mut cursor = PathBuf::from(GIT_DIR_NAME);
if cursor.is_dir() {
bail!(
"Refusing to initialize the existing '{}' directory",
cursor.display()
)
}
create_dir(&cursor)?;

{
let mut cursor = NewDir(&mut cursor).at("info")?;
write_file(TPL_INFO_EXCLUDE, PathCursor(cursor.as_mut()).at("exclude"))?;
}

{
let mut cursor = NewDir(&mut cursor).at("hooks")?;
for (tpl, filename) in &[
(TPL_HOOKS_UPDATE, "update.sample"),
(TPL_HOOKS_PREPARE_COMMIT_MSG, "prepare-commit-msg.sample"),
(TPL_HOOKS_PRE_RECEIVE, "pre-receive.sample"),
(TPL_HOOKS_PRE_REBASE, "pre-rebase.sample"),
(TPL_HOOKS_PRE_PUSH, "pre-push.sample"),
(TPL_HOOKS_PRE_COMMIT, "pre-commit.sample"),
(TPL_HOOKS_PRE_APPLYPATCH, "pre-applypatch.sample"),
(TPL_HOOKS_POST_UPDATE, "post-update.sample"),
(TPL_HOOKS_FSMONITOR_WATCHMAN, "fsmonitor-watchman.sample"),
(TPL_HOOKS_COMMIT_MSG, "commit-msg.sample"),
(TPL_HOOKS_APPLYPATCH_MSG, "applypatch-msg.sample"),
] {
write_file(tpl, PathCursor(cursor.as_mut()).at(filename))?;
}
}

{
let mut cursor = NewDir(&mut cursor).at("objects")?;
create_dir(PathCursor(cursor.as_mut()).at("info"))?;
create_dir(PathCursor(cursor.as_mut()).at("pack"))?;
}

{
let mut cursor = NewDir(&mut cursor).at("refs")?;
create_dir(PathCursor(cursor.as_mut()).at("heads"))?;
create_dir(PathCursor(cursor.as_mut()).at("tags"))?;
}

for (tpl, filename) in &[
(TPL_HEAD, "HEAD"),
(TPL_DESCRIPTION, "description"),
(TPL_CONFIG, "config"),
] {
write_file(tpl, PathCursor(&mut cursor).at(filename))?;
}

Ok(())
}
32 changes: 31 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
extern crate failure;
extern crate failure_tools;
#[macro_use]
extern crate clap;
extern crate git_core as git;

fn main() {}
use failure_tools::ok_or_exit;
use failure::{Error, ResultExt};

mod app {
use clap::{App, AppSettings, SubCommand};

pub fn new<'a, 'b>() -> App<'a, 'b> {
let app: App = app_from_crate!();
app.setting(AppSettings::SubcommandRequired).subcommand(
SubCommand::with_name("init")
.alias("initialize")
.about("Initialize the repository in the current directory."),
)
}
}

fn run() -> Result<(), Error> {
let app = app::new();
let matches = app.get_matches();
match matches.subcommand() {
("init", Some(_args)) => git::init().with_context(|_| "Repository initialization failed"),
_ => unreachable!(),
}.map_err(Into::into)
}

fn main() {
ok_or_exit(run())
}
3 changes: 3 additions & 0 deletions tests/snapshots/cli/init-fail
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
error: Repository initialization failed
Caused by:
1: Refusing to initialize the existing '.git' directory
39 changes: 24 additions & 15 deletions tests/stateless-journey.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,37 @@ exe="${root}/../$exe"
# shellcheck disable=1090
source "$root/utilities.sh"
snapshot="$root/snapshots/cli"
fixture="$root/fixtures"
# fixture="$root/fixtures"

SUCCESSFULLY=0
WITH_FAILURE=1

title "CLI"
(when "initializing a repository"
(sandbox
precondition "an initialized baseline repository" && {
git init &>/dev/null
expect_snapshot "$snapshot/baseline-init" .git
}
)
(sandbox
it "succeeds" && {
WITH_SNAPSHOT="$snapshot/init-success" \
expect_run $SUCCESSFULLY "$exe" init
}
(with "an empty directory"
(sandbox
precondition "an initialized baseline repository" && {
git init &>/dev/null
expect_snapshot "$snapshot/baseline-init" .git
}
)
(sandbox
it "succeeds" && {
WITH_SNAPSHOT="$snapshot/init-success" \
expect_run $SUCCESSFULLY "$exe" init
}

it "matches the output of baseline git init" && {
expect_snapshot "$snapshot/baseline-init" .git
}
it "matches the output of baseline git init" && {
expect_snapshot "$snapshot/baseline-init" .git
}

(when "trying to initialize the same directory again"
it "fails" && {
WITH_SNAPSHOT="$snapshot/init-fail" \
expect_run $WITH_FAILURE "$exe" init
}
)
)
)
)

0 comments on commit 57737c2

Please sign in to comment.