]> infiniteadaptability.org Git - git-extra/commitdiff
major refactor
authoralex <[email protected]>
Tue, 21 Sep 2021 06:04:17 +0000 (23:04 -0700)
committeralex <[email protected]>
Wed, 22 Sep 2021 09:43:05 +0000 (02:43 -0700)
refactored into shell script
added scripts as tests
removed old project in C
removed autotools dependency
added Makefile

81 files changed:
.gitignore
Dockerfile [deleted file]
Makefile [new file with mode: 0644]
Makefile.am [deleted file]
configure.ac [deleted file]
git-extra [new file with mode: 0644]
include/add.h [deleted file]
include/branch.h [deleted file]
include/cat.h [deleted file]
include/commit.h [deleted file]
include/default.h [deleted file]
include/edit.h [deleted file]
include/editor.h [deleted file]
include/exec.h [deleted file]
include/git.h [deleted file]
include/hash.h [deleted file]
include/log.h [deleted file]
include/ls.h [deleted file]
include/main.h [deleted file]
include/opt.h [deleted file]
include/prepend.h [deleted file]
include/templ.h [deleted file]
src/add.c [deleted file]
src/branch.c [deleted file]
src/cat.c [deleted file]
src/commit.c [deleted file]
src/default.c [deleted file]
src/edit.c [deleted file]
src/editor.c [deleted file]
src/exec.c [deleted file]
src/git.c [deleted file]
src/hash.c [deleted file]
src/log.c [deleted file]
src/ls.c [deleted file]
src/main.c [deleted file]
src/prepend.c [deleted file]
src/templ.c [deleted file]
test/add.tests.sh [new file with mode: 0755]
test/add_to_index.tests.sh [new file with mode: 0755]
test/branch_create.tests.sh [new file with mode: 0755]
test/branch_exists.tests.sh [new file with mode: 0755]
test/branch_hash.tests.sh [new file with mode: 0755]
test/branch_reset.tests.sh [new file with mode: 0755]
test/cat_file.tests.sh [new file with mode: 0755]
test/edit.tests.sh [new file with mode: 0755]
test/editor.tests.sh [new file with mode: 0755]
test/integration/Makefile.am [deleted file]
test/integration/index.js [deleted file]
test/integration/package-lock.json [deleted file]
test/integration/package.json [deleted file]
test/integration/test/add.integration.tests.js [deleted file]
test/integration/test/branch.pattern.integration.test.js [deleted file]
test/integration/test/cat.integration.tests.js [deleted file]
test/integration/test/edit.integration.tests.js [deleted file]
test/integration/test/ls.integration.tests.js [deleted file]
test/integration/test/utils.js [deleted file]
test/ls.tests.sh [new file with mode: 0755]
test/parse_arguments.tests.sh [new file with mode: 0755]
test/setup.sh [new file with mode: 0755]
test/unit/Makefile.am [deleted file]
test/unit/add.tests.c [deleted file]
test/unit/add.tests.h [deleted file]
test/unit/branch.tests.c [deleted file]
test/unit/branch.tests.h [deleted file]
test/unit/cat.tests.c [deleted file]
test/unit/cat.tests.h [deleted file]
test/unit/commit.tests.c [deleted file]
test/unit/commit.tests.h [deleted file]
test/unit/default.tests.c [deleted file]
test/unit/default.tests.h [deleted file]
test/unit/edit.tests.c [deleted file]
test/unit/edit.tests.h [deleted file]
test/unit/editor.tests.c [deleted file]
test/unit/editor.tests.h [deleted file]
test/unit/hash.tests.c [deleted file]
test/unit/hash.tests.h [deleted file]
test/unit/templ.tests.c [deleted file]
test/unit/templ.tests.h [deleted file]
test/unit/test_utils.c [deleted file]
test/unit/test_utils.h [deleted file]
test/usage.tests.sh [new file with mode: 0755]

index b1eaae02a332c598cdda8afdca1f3b45e12a3e13..07d19e434df6190315d56cc1cb905da5803c4e57 100644 (file)
@@ -1,24 +1,2 @@
-# autoconf/automake
-Makefile
-*.in
-*.in~
-aclocal.m4
-autom4te.cache
-autoscan.log
-build-aux
-config.log
-config.status
-configure
-configure.scan
-include/config.h
-include/stamp-h1
-src/.deps
-src/.dirstamp
-test/unit/.deps
-
-# build objects
-*.o
-*.log
-*.trs
-*.tests
-git-ex
\ No newline at end of file
+test/source.sh
+test/.testdir
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644 (file)
index 559c4cd..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-FROM debian:latest as base
-
-RUN apt-get update
-RUN apt-get install -y git autoconf automake gcc make vim
-
-FROM base as unit-tester
-
-RUN apt-get install -y gdb vim valgrind
-
-FROM base as core
-
-WORKDIR /build
-
-COPY Makefile.am .
-COPY configure.ac .
-
-COPY README.md .
-COPY templates templates/
-
-COPY test test/
-
-COPY include include/
-RUN autoreconf -vif
-
-COPY src src/
-RUN ./configure
-
-RUN make
-RUN make distcheck
-RUN make install
-
-FROM unit-tester
-
-WORKDIR /build
-COPY --from=core /build /build
-
-COPY --from=core /usr/local/share/git-extra/templates/ /usr/local/share/git-extra/templates/
-
-RUN ./configure
-RUN make check || cat test/unit/test-suite.log
-
-FROM node:latest as integration-tester
-
-COPY --from=core /usr/local/bin/git-ex /usr/local/bin/git-ex
-COPY --from=core /usr/local/share/git-extra/templates/ /usr/local/share/git-extra/templates/
-
-USER node
-WORKDIR /home/node
-
-COPY --from=core --chown=node:node /build/test/integration/index.js .
-COPY --from=core --chown=node:node /build/test/integration/package.json .
-COPY --from=core --chown=node:node /build/test/integration/package-lock.json .
-
-RUN npm install
-
-COPY --from=core --chown=node:node /build/test/integration/test/ test/
-
-RUN npm test
-
-FROM core as final
-
-WORKDIR /root
-RUN git init working
-
-# setup test repository; Docker image meant to be a sandbox
-WORKDIR /root/working
-
-RUN echo "test" > test.txt
-RUN git config --global core.editor vim
-
-RUN git config --local user.name "test"
-RUN git config --local user.email "test@email"
-
-RUN git add -A
-RUN git commit -a -m "..."
-
-ENTRYPOINT bash
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c36ab6c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+PREFIX ?= /usr/local
+BINDIR ?= $(PREFIX)/bin
+
+all:
+       @echo "git-extra is a shell script; nothing to make. Try 'make install' instead."
+
+check:
+       @head -n `grep -n "main()" git-extra | cut -f1 -d:` git-extra | head -n -1 > test/source.sh
+       @cd test && run-parts --exit-on-error --regex '.*\.tests\.sh' .
+
+clean:
+       @rm -vrf test/.testdir
+
+install:
+       @install -v -d "$(BINDIR)/" && install -m 0755 -v git-extra "$(BINDIR)/git-ex"
+
+uninstall:
+       @rm -vrf \
+               $(BINDIR)/git-ex
+
+.PHONY: install uninstall check clean
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644 (file)
index 6334348..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-templatesdir=$(datadir)/git-extra/templates
-
-AM_CPPFLAGS = \
-       -I$(top_builddir)/include/ \
-       -I$(top_srcdir)/include/ \
-       -DTEMPLATE_DIR='"$(templatesdir)"'
-
-bin_PROGRAMS = git-ex
-git_ex_SOURCES = \
-       src/add.c \
-       src/branch.c \
-       src/cat.c \
-       src/commit.c \
-       src/default.c \
-       src/edit.c \
-       src/editor.c \
-       src/exec.c \
-       src/git.c \
-       src/hash.c \
-       src/log.c \
-       src/ls.c \
-       src/main.c \
-       src/prepend.c \
-       src/templ.c
-git_ex_SOURCES += \
-       include/add.h \
-       include/branch.h \
-       include/cat.h \
-       include/commit.h \
-       include/default.h \
-       include/edit.h \
-       include/editor.h \
-       include/exec.h \
-       include/git.h \
-       include/hash.h \
-       include/log.h \
-       include/ls.h \
-       include/main.h \
-       include/opt.h \
-       include/prepend.h \
-       include/templ.h
-
-dist_doc_DATA = $(top_srcdir)/README.md
-dist_templates_DATA = \
-       $(top_srcdir)/templates/bug.template
-
-SUBDIRS = test/integration test/unit
-
-if HAVE_DOCKER
-IMAGE_NAME=git-extra
-CONTAINER_NAME=git-extra-test
-build:
-       docker build -t $(IMAGE_NAME) .
-
-run: build
-       docker run -d \
-               --name $(CONTAINER_NAME) \
-               --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
-               $(IMAGE_NAME)
-
-start: build
-       -docker run -it --rm \
-               --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
-               $(IMAGE_NAME)
-
-stop:
-       docker stop $(CONTAINER_NAME)
-       docker rm $(CONTAINER_NAME);
-else
-build: missing-local
-run: missing-local
-start: missing-local
-stop: missing-local
-missing-local:
-       @echo "docker not found"
-endif
\ No newline at end of file
diff --git a/configure.ac b/configure.ac
deleted file mode 100644 (file)
index bc6f3f6..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-AC_PREREQ([2.69])
-AC_INIT([git-extra], [0.0.0])
-
-# Store build files not in main directory
-AC_CONFIG_AUX_DIR([build-aux])
-
-AM_INIT_AUTOMAKE([foreign subdir-objects nostdinc -Wall -Werror])
-
-AC_CONFIG_SRCDIR([src/main.c])
-AC_CONFIG_HEADERS([include/config.h])
-
-AC_ARG_ENABLE([memcheck],
-  [AS_HELP_STRING([--disable-memcheck],
-  [disable valgrind (enabled by default)])],
-  [enable_memcheck=$enableval],
-  [enable_memcheck=yes])
-
-AC_PATH_PROG([GIT], [git])
-AM_CONDITIONAL([HAVE_GIT], [test -n "$GIT"])
-AM_COND_IF([HAVE_GIT],,[AC_MSG_ERROR([Please install git before trying to build git-extra])])
-
-AC_PATH_PROG([DOCKER], [docker])
-AM_CONDITIONAL([HAVE_DOCKER], [test -n "$DOCKER"])
-
-AC_PATH_PROG([NODE], [node])
-AM_CONDITIONAL([HAVE_NODE], [test -n "$NODE"])
-
-AC_PATH_PROG([VALGRIND], [valgrind])
-AM_CONDITIONAL([HAVE_VALGRIND], [test -n "$VALGRIND"])
-
-dnl disable memcheck if valgrind not found
-if test "x$enable_memcheck" = "xyes"; then
-  if test -z "$VALGRIND"; then
-    enable_memcheck=no
-  fi
-fi
-
-AC_MSG_CHECKING([if memcheck should be enabled])
-if test x$enable_memcheck != xno; then
-  AC_MSG_RESULT(yes)
-else
-  AC_MSG_RESULT(no)
-fi
-
-AM_CONDITIONAL([ENABLE_MEMCHECK],[test x$enable_memcheck = xyes])
-
-# Checks for programs.
-AC_PROG_CC
-AC_PROG_INSTALL
-
-# Checks for libraries.
-
-# Checks for header files.
-AC_CHECK_HEADERS([stddef.h stdlib.h string.h unistd.h])
-
-# Checks for typedefs, structures, and compiler characteristics.
-AC_TYPE_OFF_T
-AC_TYPE_PID_T
-AC_TYPE_SIZE_T
-AC_TYPE_SSIZE_T
-
-# Checks for library functions.
-AC_FUNC_FORK
-AC_FUNC_MALLOC
-AC_CHECK_FUNCS([dup2 getcwd memset setenv])
-
-AC_CONFIG_FILES([
-       Makefile
-  test/integration/Makefile
-       test/unit/Makefile
-])
-AC_OUTPUT
diff --git a/git-extra b/git-extra
new file mode 100644 (file)
index 0000000..f566e17
--- /dev/null
+++ b/git-extra
@@ -0,0 +1,310 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# Option variables
+LOG_LEVEL=default
+BRANCH=extra
+EDITOR=
+MESSAGE=
+TEMPLATE=
+
+# helper variables
+LONG_OUTPUT=false
+POSITIONAL_ARGUMENTS=
+PREFIX=
+PREV_COMMIT=
+UTILITY=ls
+WORKING_BRANCH=
+
+detect_git() {
+       if [[ -z "`git --version`" ]]; then
+               log_err "git not found in path"
+               exit 1
+       fi
+}
+
+detect_commits() {
+       set +e
+       git rev-list --count HEAD > /dev/null 2>&1
+       if [[ $? -ne 0 ]]; then
+               log_err "no commits found, create initial commit first"
+               exit 1
+       fi
+       set -e
+}
+
+log_info() {
+       if [[ "$LOG_LEVEL" == "info" ]]; then
+               log "$@"
+       fi
+}
+
+log_err() {
+       echo -e "$@"
+}
+
+log() {
+       if [[ "$LOG_LEVEL" != "error" ]]; then
+               echo -e "$@"
+       fi
+}
+
+usage() {
+       log_err "Usage:"
+       log_err ""
+       log_err "\tgit extra [options] [add|cat|edit|ls] [path]"
+       log_err ""
+       log_err "Options:"
+       log_err "\t--branch, -b <branch>"
+       log_err "\t--editor, -e <editor>"
+       log_err "\t--long, -l"
+       log_err "\t--message, -m <message>"
+       log_err "\t--quiet, -q"
+       log_err "\t--template, -t <template>"
+       log_err "\t--verbose, -v"
+       exit 1
+}
+
+set_branch() {
+       log_info "setting branch to $1"
+       BRANCH="$1"
+}
+
+set_editor() {
+       log_info "setting editor to $1"
+       EDITOR="$1"
+}
+
+set_loglevel() {
+       log_info "setting loglevel to '$1'"
+       case "$1" in
+               "default") LOG_LEVEL="default" ;;
+               "info") LOG_LEVEL="info" ;;
+               "error") LOG_LEVEL="error" ;;
+               *) log_err "unknown log level '$1'" ;;
+       esac
+}
+
+set_long() {
+       log_info "setting long output option to true"
+       LONG_OUTPUT=true
+}
+
+set_message() {
+       log_info "setting message to '$1'"
+       MESSAGE="$1"
+}
+
+set_template() {
+       log_info "setting template to '$1'"
+       TEMPLATE="$1"
+}
+
+parse_arguments() {
+       # transform long options into short opts
+       for arg in "$@"; do
+               shift
+               case "$arg" in
+                       "--branch") set -- "$@" "-b" ;;
+                       "--editor") set -- "$@" "-e" ;;
+                       "--long") set -- "$@" "-l" ;;
+                       "--message") set -- "$@" "-m" ;;
+                       "--quiet") set -- "$@" "-q" ;;
+                       "--template") set -- "$@" "-t" ;;
+                       "--verbose") set -- "$@" "-v" ;;
+                       *) set -- "$@" "$arg" ;;
+               esac
+       done
+
+       # parse short options
+       OPTIND=1
+       while getopts "b:e:lm:qt:v" opt
+       do
+               case "$opt" in
+                       "b") set_branch "$OPTARG" ;;
+                       "e") set_editor "$OPTARG" ;;
+                       "l") set_long ;;
+                       "m") set_message "$OPTARG" ;;
+                       "q") set_loglevel "error" ;;
+                       "t") set_template "$OPTARG" ;;
+                       "v") set_loglevel "info" ;;
+                       "?") usage ;;
+               esac
+       done
+
+       shift $(expr $OPTIND - 1) # remove options
+       if [[ "$#" -gt 0 ]]; then
+               UTILITY="$1"
+               shift
+               POSITIONAL_ARGUMENTS="$@"
+       fi
+}
+
+branch_create() {
+       log_info "creating branch $BRANCH"
+       git check-ref-format --branch "$BRANCH" > /dev/null
+       HASH=`echo "" | git mktree --batch`
+       TREE_HASH=`git commit-tree -m "init" "$HASH"`
+       git update-ref "refs/heads/$BRANCH" "$TREE_HASH"
+}
+
+branch_exists() {
+       if test $(git branch --list "$BRANCH" | wc -c) == 0; then
+               branch_create
+       fi
+}
+
+branch_hash() {
+       # changes to extra branch and stores prevous commit hash in PREV_COMMIT
+       branch_exists
+
+       WORKING_BRANCH=`git rev-parse --verify HEAD --abbrev-ref`
+
+       git symbolic-ref HEAD "refs/heads/$BRANCH"
+       git reset > /dev/null
+
+       PREV_COMMIT=`git rev-parse --verify HEAD`
+}
+
+branch_reset() {
+       log_info "reseting branch back to $WORKING_BRANCH"
+
+       git symbolic-ref HEAD "refs/heads/$WORKING_BRANCH"
+       git reset
+}
+
+add_to_index() {
+       log_info "adding $2 to index"
+
+       # fills PREV_COMMIT with $BRANCH's previous commit hash
+       branch_hash
+
+       hash=`git hash-object "$1" -w`
+       rm "$1"
+
+       git update-index --add --cacheinfo 100644 "$hash" "$2"
+       tree_hash=`git write-tree`
+       commit_hash=`git commit-tree -m "${MESSAGE:-updated using git-extra}" -p "$PREV_COMMIT" "$tree_hash"`
+
+       git update-ref "refs/heads/$BRANCH" "$commit_hash"
+       
+       branch_reset
+}
+
+add() {
+       branch_exists
+
+       if [[ "$#" -ne 1 ]]; then
+               log_err "no path given to add"
+               usage
+       fi
+       
+       if test $(git ls-tree "$BRANCH" "$1" | wc -c) != 0; then
+               log_err "file '$1' already exists; did you mean to edit '$1' instead?"
+               usage
+       fi
+
+       tmpfile=`mktemp`
+
+       if [[ -n "$TEMPLATE" ]]; then
+               if [[ -r "$TEMPLATE" ]]; then
+                       log_info "using template '$TEMPLATE'"
+                       cat "$TEMPLATE" > "$tmpfile"
+               else
+                       log_err "template '$TEMPLATE' doesn't exist"
+                       exit 1
+               fi
+       fi
+
+       editor "$tmpfile"
+
+       add_to_index "$tmpfile" "$1"
+
+       log "added $1"
+}
+
+cat_file() {
+       branch_exists
+       HASH=`git ls-tree "$BRANCH" "$1" | awk -F ' ' '{print $3}'`
+       if [[ -z "$HASH" ]]; then
+               log_err "$1 doesn't exist"
+               exit 1
+       fi
+
+       if [[ $# -eq 2 ]]; then
+               git cat-file -p "$HASH" > "$2"
+       else
+               git cat-file -p "$HASH"
+       fi
+}
+
+edit() {
+       tmpfile=`mktemp`
+
+       log_info "editing $1 using $tmpfile"
+
+       cat_file "$1" "$tmpfile"
+
+       editor $tmpfile
+
+       add_to_index "$tmpfile" "$1"
+
+       log "saved $1"
+}
+
+editor() {
+       log_info "opening temporary file '$1'"
+
+       pre_edit_hash=`b2sum $1`
+
+       if [[ -z "$EDITOR" ]]; then
+               log_info "no editor found, checking git config"
+               set +e
+               EDITOR="`git config core.editor`"
+               set -e
+               if [[ -z "$EDITOR" ]]; then
+                       log_err "no editor defined; use --editor option, set EDITOR environmental variable, or set git config core.editor."
+                       exit 1
+               fi
+       fi
+
+       CMD=`printf "$EDITOR %s\n" "$1"`
+       eval "$CMD"
+
+       post_edit_hash=`b2sum $1`
+
+       if [[ "$pre_edit_hash" == "$post_edit_hash" ]]; then
+               log_err "no changes detected"
+               exit 1
+       fi
+}
+
+ls() {
+       branch_exists
+
+       if [[ "$LONG_OUTPUT" == "true" ]]; then
+               git ls-tree "$BRANCH" -r $@
+       else
+               git ls-tree "$BRANCH" -r --name-only $@
+       fi
+}
+
+main() {
+       detect_git
+       detect_commits
+
+       parse_arguments "$@"
+
+       case "$UTILITY" in
+               "add") add "$POSITIONAL_ARGUMENTS" ;;
+               "cat") cat_file "$POSITIONAL_ARGUMENTS" ;;
+               "edit") edit "$POSITIONAL_ARGUMENTS" ;;
+               "ls") ls "$POSITIONAL_ARGUMENTS" ;;
+               *) log_err "unknown utility: $UTILITY"; usage ;;
+       esac
+
+       exit
+}
+
+main "$@"
diff --git a/include/add.h b/include/add.h
deleted file mode 100644 (file)
index b90df92..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef __ADD_H_
-#define __ADD_H_
-
-#include<stdio.h>
-#include<stdlib.h>
-
-#include<commit.h>
-#include<editor.h>
-#include<git.h>
-#include<log.h>
-#include<opt.h>
-#include<templ.h>
-
-#define ADD_MESSAGE_ADDED_FILE_INDEX_HASH "added %s to index with hash: %s\n"
-#define ADD_MESSAGE_ALREADY_ADDED "%s already exists\n"
-#define ADD_MESSAGE_ADDED_FILE "added %s\n"
-#define ADD_MESSAGE_TEMP_FILENAME "temporary file %s created\n"
-
-int add(char*);
-int add_to_index(char*,char*);
-
-#endif
\ No newline at end of file
diff --git a/include/branch.h b/include/branch.h
deleted file mode 100644 (file)
index a860dbf..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef __BRANCH_H_
-#define __BRANCH_H_
-
-#include<assert.h>
-#include<stddef.h>
-
-#include<exec.h>
-#include<git.h>
-#include<log.h>
-#include<opt.h>
-#include<prepend.h>
-
-#define BRANCH_MISSING_MESSAGE "branch `%s` missing\n"
-#define BRANCH_CREATED_MESSAGE "branch `%s` created\n"
-#define BRANCH_RESET_MESSAGE "switch back to branch `%s` \n"
-#define BRANCH_WORKING_BRANCH_ERROR_MESSAGE "failed to get current working branch; additional output might be printed above\n"
-
-int branch(char*,size_t);
-int branch_create();
-int branch_exists();
-int branch_reset();
-
-#endif
\ No newline at end of file
diff --git a/include/cat.h b/include/cat.h
deleted file mode 100644 (file)
index ef6e682..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __CAT_H_
-#define __CAT_H_
-
-#include<stdlib.h>
-
-#include<exec.h>
-#include<git.h>
-#include<log.h>
-
-int cat(char*);
-
-#endif
\ No newline at end of file
diff --git a/include/commit.h b/include/commit.h
deleted file mode 100644 (file)
index f78b643..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __COMMIT_H_
-#define __COMMIT_H_
-
-#include<assert.h>
-
-#include<branch.h>
-#include<git.h>
-#include<prepend.h>
-
-int commit(char*,size_t,char*);
-
-#endif
\ No newline at end of file
diff --git a/include/default.h b/include/default.h
deleted file mode 100644 (file)
index 485853f..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef __DEFAULT_H_
-#define __DEFAULT_H_
-
-#include<stdlib.h>
-#include<unistd.h>
-
-#include<exec.h>
-#include<git.h>
-#include<opt.h>
-
-int defaults();
-void default_branch();
-int default_editor();
-int default_path_prefix();
-
-#endif
\ No newline at end of file
diff --git a/include/edit.h b/include/edit.h
deleted file mode 100644 (file)
index ee1dff2..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef __EDIT_H_
-#define __EDIT_H_
-
-#include<stdio.h>
-#include<stdlib.h>
-
-#include<add.h>
-#include<cat.h>
-#include<commit.h>
-#include<log.h>
-
-#define EDIT_MESSAGE_EDITTED_FILE "%s saved\n"
-#define EDIT_MESSAGE_TEMP_FILENAME "temporary file %s created\n"
-
-int edit(char*);
-
-#endif
\ No newline at end of file
diff --git a/include/editor.h b/include/editor.h
deleted file mode 100644 (file)
index 5c3fa83..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef __EDITOR_H_
-#define __EDITOR_H_
-
-#include<errno.h>
-#include<stdlib.h>
-#include<string.h>
-#include<sys/wait.h>
-#include<unistd.h>
-#include<wordexp.h>
-
-#include<exec.h>
-#include<hash.h>
-#include<log.h>
-#include<opt.h>
-
-#define EDITOR_MESSAGE_MISSING_EDITOR "no editor or core.editor config option set; set EDITOR env variable or pass --editor option\n"
-#define EDITOR_MESSAGE_NO_CHANGE "no change detected; aborting...\n"
-
-int editor(char*);
-
-#endif
\ No newline at end of file
diff --git a/include/exec.h b/include/exec.h
deleted file mode 100644 (file)
index 6cc4eeb..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef __EXEC_H_
-#define __EXEC_H_
-
-#include<errno.h>
-#include<stdlib.h>
-#include<string.h>
-#include<sys/wait.h>
-#include<unistd.h>
-
-#include<log.h>
-
-#define EXEC_INVALID_RETURN_STATUS "%s invalid return status [%d]\n"
-
-int exec_cmd(char**,char*,char*,size_t);
-int exec_cmd_with_stds(char**);
-
-#endif
\ No newline at end of file
diff --git a/include/git.h b/include/git.h
deleted file mode 100644 (file)
index 7b14ff3..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef __GIT_H_
-#define __GIT_H_
-
-#include<stdlib.h>
-#include<string.h>
-
-#include<exec.h>
-#include<log.h>
-#include<opt.h>
-
-#define HASH_BUF_LENGTH 41
-
-#define LS_TREE_MODE_LENGTH 7
-#define LS_TREE_TYPE_LENGTH 5
-#define LS_TREE_PREFIX_LENGTH LS_TREE_MODE_LENGTH+LS_TREE_TYPE_LENGTH
-#define LS_TREE_ENTRY_LENGTH HASH_BUF_LENGTH+LS_TREE_PREFIX_LENGTH
-
-#define GIT_COMMIT_DEFAULT_MESSAGE "updated using git-ex"
-#define GIT_COMMIT_DEFAULT_MESSAGE_LENGTH 21
-
-#define GIT_SYMBOLIC_REF_BRANCH_NAME_TOO_LARGE "consider making branch name shorter...\n"
-#define GIT_UPDATE_REF_BRANCH_NAME_TOO_LARGE "consider making branch name shorter...\n"
-
-enum git_exec_flags {
-       GIT_EXEC_FLAG_DEFAULT = 0,
-       GIT_EXEC_FLAG_WITH_STDS = 1
-};
-
-enum rev_parse_type {
-       REV_PARSE_BRANCH_NAME,
-       REV_PARSE_HASH,
-       REV_PARSE_TOP_LEVEL
-};
-
-enum hash_object_type {
-       HASH_OBJECT_DEFAULT,
-       HASH_OBJECT_ADD_TO_INDEX
-};
-
-#define git_branch(output,len) git(GIT_EXEC_FLAG_DEFAULT,NULL,output,len,"git","branch","--list",global_opts.branch,NULL)
-#define git_cat_file(hash) git(GIT_EXEC_FLAG_WITH_STDS,NULL,NULL,0,"git","cat-file","-p",hash,NULL)
-#define git_commit_tree(tree_hash,parent_hash,output,len) git(GIT_EXEC_FLAG_DEFAULT,NULL,output,len,"git","commit-tree","-m",(NULL!=global_opts.message)?global_opts.message:GIT_COMMIT_DEFAULT_MESSAGE,tree_hash,(NULL!=parent_hash)?"-p":NULL,parent_hash,NULL)
-#define git_config(option,output,len) git(GIT_EXEC_FLAG_DEFAULT,NULL,output,len,"git","config","--get",option,NULL)
-#define git_hash_object(filename,type,output,len) git(GIT_EXEC_FLAG_DEFAULT,NULL,output,len,"git","hash-object",filename,(HASH_OBJECT_ADD_TO_INDEX==type)?"-w":NULL,NULL)
-#define git_ls_tree(path,output,len) \
-       git( \
-               (NULL==path)?GIT_EXEC_FLAG_WITH_STDS:GIT_EXEC_FLAG_DEFAULT, \
-               NULL, \
-               output, \
-               len, \
-               "git", \
-               "ls-tree", \
-               global_opts.branch, \
-               (NULL==path)?"-r":path, \
-               (NULL!=path)?NULL:(GIT_LS_TREE_OPTION_NAME_ONLY==global_opts.ls_options)?"--name-only":NULL, \
-               NULL \
-       )
-#define git_mktree(output,len) git(GIT_EXEC_FLAG_DEFAULT,"\n",output,len,"git","mktree","--batch",NULL)
-#define git_reset() git(GIT_EXEC_FLAG_DEFAULT,NULL,NULL,0,"git","reset",NULL)
-#define git_rev_parse(type,output,len) git(GIT_EXEC_FLAG_DEFAULT,NULL,output,len,"git","rev-parse",(REV_PARSE_TOP_LEVEL==type)?"--show-toplevel":"--verify",(REV_PARSE_TOP_LEVEL==type)?NULL:"HEAD",(REV_PARSE_BRANCH_NAME==type)?"--abbrev-ref":NULL,NULL)
-#define git_symbolic_ref(branch) git(GIT_EXEC_FLAG_DEFAULT,NULL,NULL,0,"git","symbolic-ref","HEAD",branch,NULL)
-#define git_update_index(file_hash,path) git(GIT_EXEC_FLAG_DEFAULT,NULL,NULL,0,"git","update-index","--add","--cacheinfo","100644",file_hash,path,NULL)
-#define git_update_ref(commit_hash,branch) git(GIT_EXEC_FLAG_DEFAULT,NULL,NULL,0,"git","update-ref",branch,commit_hash,NULL)
-#define git_write_tree(output,len) git(GIT_EXEC_FLAG_DEFAULT,NULL,output,len,"git","write-tree",NULL)
-
-int git(enum git_exec_flags,char*,char*,size_t,...);
-
-#endif
\ No newline at end of file
diff --git a/include/hash.h b/include/hash.h
deleted file mode 100644 (file)
index af13a15..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __HASH_H_
-#define __HASH_H_
-
-#include<assert.h>
-
-#include<exec.h>
-#include<git.h>
-
-int hash(char*,char*,int);
-
-#endif
\ No newline at end of file
diff --git a/include/log.h b/include/log.h
deleted file mode 100644 (file)
index 544ab75..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef __LOG_H_
-#define __LOG_H_
-
-#include<stdarg.h>
-#include<stdio.h>
-
-extern int verbose_flag;
-
-enum log_level {
-       LOG_LEVEL_SILENT = -1, /* suppresses all output */
-       LOG_LEVEL_ERRORS = 0,  /* only prints errors */
-       LOG_LEVEL_DEFAULT = 1, /* normal output */
-       LOG_LEVEL_VERBOSE = 2  /* logging and debugging info */
-};
-
-void log_message(enum log_level,const char*,...);
-
-#endif
\ No newline at end of file
diff --git a/include/ls.h b/include/ls.h
deleted file mode 100644 (file)
index 9b3fac1..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef __LS_H_
-#define __LS_H_
-
-#include<stdlib.h>
-
-#include<exec.h>
-#include<git.h>
-
-#define LS_MESSAGE_TREE_LEVEL "using ls_tree option: %d\n"
-
-int ls(int);
-
-#endif
\ No newline at end of file
diff --git a/include/main.h b/include/main.h
deleted file mode 100644 (file)
index 163cf88..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef __MAIN_H_
-
-#include<getopt.h>
-#include<stdlib.h>
-#include<string.h>
-
-#include<add.h>
-#include<cat.h>
-#include<default.h>
-#include<edit.h>
-#include<log.h>
-#include<ls.h>
-#include<opt.h>
-
-#define MAIN_BRANCH_SET_MESSAGE "using branch `%s`\n"
-#define MAIN_EDITOR_SET_MESSAGE "using %s as editor\n"
-#define MAIN_UNKNOWN_SUBCOMMAND_MESSAGE "unknown command `git ex %s`\n"
-#define MAIN_WRONG_NUMBER_ARGS "wrong number of arguments\n"
-
-#define MAIN_SUBCOMMAND_ADD "add"
-#define MAIN_SUBCOMMAND_CAT "cat"
-#define MAIN_SUBCOMMAND_EDIT "edit"
-#define MAIN_SUBCOMMAND_LS "ls"
-
-int main(int,char**);
-
-#endif
\ No newline at end of file
diff --git a/include/opt.h b/include/opt.h
deleted file mode 100644 (file)
index 8df5028..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __OPT_H_
-#define __OPT_H_
-
-// global options
-#define WORKING_BRANCH_BUF_SIZE 30
-
-enum ls_tree_options {
-       GIT_LS_TREE_OPTION_DEFAULT = 0,
-       GIT_LS_TREE_OPTION_FULL = 1,
-       GIT_LS_TREE_OPTION_NAME_ONLY = 2
-};
-
-struct options {
-       char *branch;
-       char *editor;
-       char *message;
-       char *path_prefix;
-       char *template;
-       enum ls_tree_options ls_options;
-       char working_branch[WORKING_BRANCH_BUF_SIZE];
-};
-
-extern struct options global_opts;
-
-#endif
\ No newline at end of file
diff --git a/include/prepend.h b/include/prepend.h
deleted file mode 100644 (file)
index 65f4a27..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef __PREPEND_H_
-#define __PREPEND_H_
-
-#include<assert.h>
-#include<stdlib.h>
-#include<string.h>
-
-char *prepend(char*,char*);
-
-#endif
\ No newline at end of file
diff --git a/include/templ.h b/include/templ.h
deleted file mode 100644 (file)
index 75fd2c9..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef __TEMPL_H_
-#define __TEMPL_H_
-
-#include<stdio.h>
-#include<stdlib.h>
-#include<string.h>
-#include<sys/sendfile.h>
-
-#include<opt.h>
-
-#define TEMPLATE_PREFIX "/"
-#define TEMPLATE_PREFIX_LENGTH 1
-#define TEMPLATE_SUFFIX ".template"
-#define TEMPLATE_SUFFIX_LENGTH 9
-#define TEMPLATE_EXTRA_LENGTH TEMPLATE_PREFIX_LENGTH+TEMPLATE_SUFFIX_LENGTH
-
-int template(int);
-
-#endif
\ No newline at end of file
diff --git a/src/add.c b/src/add.c
deleted file mode 100644 (file)
index 199177e..0000000
--- a/src/add.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#include<add.h>
-
-int add(char *path) {
-       char ls_tree_result[LS_TREE_ENTRY_LENGTH];
-
-       if(NULL==path) { return EXIT_FAILURE; }
-
-       // verify not attempting to add same file again
-       if(branch_exists()>0) {
-               ls_tree_result[0] = 0;
-               if(git_ls_tree(path,ls_tree_result,LS_TREE_ENTRY_LENGTH)<0) { return EXIT_FAILURE; }
-               if(ls_tree_result[0]!=0) {
-                       log_message(LOG_LEVEL_ERRORS,ADD_MESSAGE_ALREADY_ADDED,path);
-                       return EXIT_FAILURE;
-               }
-       }
-
-       char filename[] = "/tmp/XXXXXX";
-       int fd = mkstemp(filename);
-       if(fd<0) { return EXIT_FAILURE; }
-
-       log_message(LOG_LEVEL_VERBOSE,ADD_MESSAGE_TEMP_FILENAME,filename);
-       
-       if(NULL!=global_opts.template) {
-               if(template(fd)<0) {
-                       close(fd);
-                       return EXIT_FAILURE;
-               }
-       }
-
-       close(fd);
-
-       if(add_to_index(filename,path)<0) { return EXIT_FAILURE; }
-
-       log_message(LOG_LEVEL_DEFAULT,ADD_MESSAGE_ADDED_FILE,path);
-
-       return EXIT_SUCCESS;
-}
-
-int add_to_index(char *tmp_filename, char *path) {
-       char file_hash[HASH_BUF_LENGTH];
-       int i;
-
-       if(editor(tmp_filename)<0) { goto cleanup; }
-
-       // add tmp file to index
-       if(git_hash_object(tmp_filename,HASH_OBJECT_ADD_TO_INDEX,file_hash,HASH_BUF_LENGTH)<0) { goto cleanup; }
-
-       log_message(LOG_LEVEL_VERBOSE,ADD_MESSAGE_ADDED_FILE_INDEX_HASH,path,file_hash);
-
-       // delete file
-       if(remove(tmp_filename)!=0) { return -1; }
-       
-       if(NULL!=global_opts.path_prefix) {
-               char *tmp = malloc(sizeof(char)*(strlen(path)+strlen(global_opts.path_prefix)+1));
-               if(NULL==tmp) { return -1; }
-
-               strcpy(tmp,global_opts.path_prefix);
-               strcat(tmp,path);
-
-               path = tmp;
-       }
-
-       if(commit(file_hash,HASH_BUF_LENGTH,path)<0) { return -1; }
-
-       return 1;
-       cleanup:
-               remove(tmp_filename);
-               return -1;
-}
\ No newline at end of file
diff --git a/src/branch.c b/src/branch.c
deleted file mode 100644 (file)
index 72ccf74..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-#include<branch.h>
-
-int branch(char *commit_hash, size_t commit_hash_len) {
-       char *p;
-       int i;
-
-       assert(commit_hash_len==HASH_BUF_LENGTH);
-
-       if((i = branch_exists())<=0) {
-               log_message(LOG_LEVEL_VERBOSE,BRANCH_MISSING_MESSAGE,global_opts.branch);
-               if(i<0) { return -1; }
-               if(branch_create()<0) { return -1; }
-       }
-
-       // store current branch into global_opts.working_branch
-       if(git_rev_parse(REV_PARSE_BRANCH_NAME,global_opts.working_branch,WORKING_BRANCH_BUF_SIZE)<0) {
-               log_message(LOG_LEVEL_ERRORS,BRANCH_WORKING_BRANCH_ERROR_MESSAGE);
-               return -1;
-       }
-
-       // git rev-parse returns branch with \n at the end
-       while(i++<WORKING_BRANCH_BUF_SIZE) {
-               if(global_opts.working_branch[i]=='\0') { break; }
-               if(global_opts.working_branch[i]=='\n') {
-                       global_opts.working_branch[i] = '\0';
-                       break;
-               }
-       }
-
-       /* switch to extra branch and reset index
-        * 1. git symbolic-ref HEAD refs/heads/[BRANCH_NAME]
-        * 2. git reset
-        */
-
-       p = prepend("refs/heads/",global_opts.branch); // see git.c:365
-       if(NULL==p) { return -1; }
-       if(git_symbolic_ref(p)<0) { return -1; }
-       free(p);
-
-       if(git_reset()<0) { return -1; }
-
-       // store current commit's hash in buf
-       if(git_rev_parse(REV_PARSE_HASH,commit_hash,HASH_BUF_LENGTH)<0) {
-               log_message(LOG_LEVEL_ERRORS,BRANCH_WORKING_BRANCH_ERROR_MESSAGE);
-               return -1;
-       }
-
-       return 1;
-}
-
-int branch_create() {
-       char tree_hash[HASH_BUF_LENGTH];
-       char commit_hash[HASH_BUF_LENGTH];
-       char *p;
-       
-       /* 
-        * create blank branch if global_opts.branch doesn't exist:
-        * 1. echo "" | git mktree --batch
-        * 2. git commit-tree -m "init" [HASH from (1)]
-        * 3. git update-ref refs/heads/[global_opts.branch] [HASH from (2)]
-        */
-
-       // 1.)
-       if(git_mktree(tree_hash,HASH_BUF_LENGTH)<0) { return -1; }
-
-       // 2.)
-       char init_message[] = "init";
-       p = global_opts.message;
-       global_opts.message = init_message;
-       if(git_commit_tree(tree_hash,NULL,commit_hash,HASH_BUF_LENGTH)<0) { return -1; }
-       global_opts.message = p;
-               
-       // 3.)
-       p = prepend("refs/heads/",global_opts.branch);
-       if(NULL==p) { return -1; }
-       if(git_update_ref(commit_hash,p)<0) { return -1; }
-       free(p);
-
-       log_message(LOG_LEVEL_VERBOSE,BRANCH_CREATED_MESSAGE,global_opts.branch);
-
-       return 1;
-}
-
-int branch_exists() {
-       char output[HASH_BUF_LENGTH];
-       int i;
-       
-       output[0] = 0;
-       if(git_branch(output,HASH_BUF_LENGTH)<0) { return -1; }
-       
-       if(output[0]!=0) {
-               return 1;
-       }
-       return 0;
-}
-
-int branch_reset() {
-       char *p;
-
-       log_message(LOG_LEVEL_VERBOSE,BRANCH_RESET_MESSAGE,global_opts.working_branch);
-
-       /* switch back to working branch and reset index
-        * 1. git symbolic-ref HEAD refs/heads/[WORKING_BRANCH_NAME]
-        * 2. git reset
-        */
-
-       p = prepend("refs/heads/",global_opts.working_branch);
-       if(NULL==p) { return -1; }
-       if(git_symbolic_ref(p)<0) { return -1; }
-       free(p);
-
-       if(git_reset()<0) { return -1; }
-
-       return 1;
-}
\ No newline at end of file
diff --git a/src/cat.c b/src/cat.c
deleted file mode 100644 (file)
index 1900b92..0000000
--- a/src/cat.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include<cat.h>
-
-int cat(char *path) {
-       char ls_tree_result[LS_TREE_ENTRY_LENGTH];
-       
-       if(NULL==path) { return EXIT_FAILURE; }
-
-       // get reference to path in branch
-       if(git_ls_tree(path,ls_tree_result,LS_TREE_ENTRY_LENGTH)<0) { return EXIT_FAILURE; }
-       
-       if(git_cat_file(&(ls_tree_result[LS_TREE_PREFIX_LENGTH]))<0) { return EXIT_FAILURE; }
-       
-       return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/src/commit.c b/src/commit.c
deleted file mode 100644 (file)
index 3588858..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#include<commit.h>
-
-int commit(char *file_hash, size_t file_hash_len, char *path) {
-       char prev_commit_hash[HASH_BUF_LENGTH];
-       char tree_hash[HASH_BUF_LENGTH];
-       char commit_hash[HASH_BUF_LENGTH];
-       char *p;
-       int i;
-
-       assert(file_hash_len==HASH_BUF_LENGTH);
-
-       if(branch(prev_commit_hash,HASH_BUF_LENGTH)<0) { return -1; }
-
-       /*
-        * add object to tree from cache using git update-index
-        * and update extra branch's ref to point at new commit:
-        * 1. git update-index --add --cacheinfo 100644 [FILE_HASH] [PATH]
-        * 2. git write-tree
-        * 3. git commit-tree -m "[COMMIT_MESSAGE]" [HASH from (2)] -p [PARENT_COMMIT_HASH]
-        * 4. git update-ref refs/heads/[EXTRA_BRANCH_NAME] [HASH from (3)]
-        */
-
-       // 1.)
-       if(git_update_index(file_hash,path)<0) { goto reset; }
-
-       // 2.)
-       if(git_write_tree(tree_hash,HASH_BUF_LENGTH)<0) { goto reset; }
-
-       // 3.)
-       if(git_commit_tree(tree_hash,prev_commit_hash,commit_hash,HASH_BUF_LENGTH)<0) { goto reset; }
-       
-       // 4.)
-       p = prepend("refs/heads/",global_opts.branch);
-       if(NULL==p) { goto reset; }
-       if(git_update_ref(commit_hash,p)<0) { goto reset; }
-       free(p);
-       
-       if(branch_reset()<0) { return -1; }
-       
-       return 1;
-       reset:
-               if(branch_reset()<0) { return -1; }
-               return -1;
-}
\ No newline at end of file
diff --git a/src/default.c b/src/default.c
deleted file mode 100644 (file)
index ae78ef6..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include<default.h>
-
-struct options global_opts = {NULL,NULL,NULL,NULL,NULL,GIT_LS_TREE_OPTION_FULL};
-
-int defaults() {
-       default_branch();
-       if(default_editor()<0) { return -1; }
-       if(default_path_prefix()<0) { return -1; }
-
-       return 1;
-}
-
-void default_branch() {
-       global_opts.branch = getenv("GIT_EX_BRANCH");
-       if(NULL==global_opts.branch) {
-               global_opts.branch = "extra";
-       }
-}
-
-int default_editor() {
-       /*
-        * editor can be found in multiple places: (ordered by priority)
-        * -passed in via option (handled in main.c (called after this)
-        * -EDITOR env variable
-        * -git config
-       */
-       global_opts.editor = getenv("EDITOR");
-       if(NULL==global_opts.editor) {
-               char tmp[100]; // should be no longer than this; copying to global_opts below
-
-               if(git_config("core.editor",tmp,100)<0) {
-                       /*
-                        * will error out if core.editor
-                        * not set, proceed
-                        */
-                       return 1;
-               }
-
-               int i = strlen(tmp);
-               if(i>0) {
-                       global_opts.editor = malloc(sizeof(char)*(i+1));
-                       strcpy(global_opts.editor,tmp);
-               }
-       }
-
-       return 1;
-}
-
-int default_path_prefix() {
-       /*
-        * generate path prefix for when not working in git root directory
-        */
-       char *cwd,*root;
-       int i,cwd_len,root_len;
-
-       cwd = getcwd(NULL,0);
-       if(NULL==cwd) { return -1; }
-
-       // root will be at least as long as cwd
-       cwd_len = strlen(cwd)+1;
-       root = malloc(sizeof(char)*cwd_len);
-       if(NULL==root) { return -1; }
-
-       if(git_rev_parse(REV_PARSE_TOP_LEVEL,root,cwd_len)<0) { return -1; }
-       
-       cwd_len--;
-       root_len = strlen(root);
-
-       if(root_len<cwd_len) {
-               global_opts.path_prefix = malloc(sizeof(char)*(cwd_len-root_len+2));
-               strcpy(global_opts.path_prefix,&(cwd[root_len]));
-               strcat(global_opts.path_prefix,"/");
-       }
-       
-       free(cwd);
-
-       return 1;
-}
\ No newline at end of file
diff --git a/src/edit.c b/src/edit.c
deleted file mode 100644 (file)
index 73c5f57..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#include<edit.h>
-
-int edit(char *path) {
-       char file_hash[HASH_BUF_LENGTH];
-       fpos_t pos;
-       int fd;
-
-       if(NULL==path) { return EXIT_FAILURE; }
-
-       char filename[] = "/tmp/XXXXXX";
-       fd = mkstemp(filename);
-       close(fd);
-
-       log_message(LOG_LEVEL_VERBOSE,EDIT_MESSAGE_TEMP_FILENAME,filename);
-
-       // redirect stdout to tmp file
-       fd = dup(fileno(stdout));
-       if(NULL==freopen(filename,"w",stdout)) {
-               perror("freopen() failed");
-               return EXIT_FAILURE;
-       }
-
-       fflush(stdout);
-       
-       if(cat(path)!=EXIT_SUCCESS) { return EXIT_FAILURE; }
-
-       // reset stdout
-       while((dup2(fd,fileno(stdout))==-1)&&(errno==EINTR)) {}
-       close(fd);
-       clearerr(stdout);
-       fsetpos(stdout,&pos); /* for C9X */
-
-       if(add_to_index(filename,path)<0) { return EXIT_FAILURE; }
-
-       log_message(LOG_LEVEL_DEFAULT,EDIT_MESSAGE_EDITTED_FILE,path);
-
-       return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/src/editor.c b/src/editor.c
deleted file mode 100644 (file)
index 54bcee3..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include<editor.h>
-
-int editor(char *filename) {
-       char pre_edit_hash[HASH_BUF_LENGTH];
-       char post_edit_hash[HASH_BUF_LENGTH];
-
-       if(NULL==filename) { return -1; }
-
-       if(NULL==global_opts.editor) {
-               log_message(LOG_LEVEL_ERRORS,EDITOR_MESSAGE_MISSING_EDITOR);
-               return -1;
-       }
-
-       if(hash(filename,pre_edit_hash,HASH_BUF_LENGTH)<0) { return -1; }
-
-       wordexp_t result;
-
-       if(wordexp(global_opts.editor,&result,WRDE_NOCMD)!=0) { goto clean; }
-       if(wordexp(filename,&result,WRDE_APPEND)!=0) { goto clean; }
-       if(exec_cmd_with_stds(result.we_wordv)<0) { goto clean; }
-
-       wordfree(&result);
-
-       if(hash(filename,post_edit_hash,HASH_BUF_LENGTH)<0) { return -1; }
-
-       if(memcmp(pre_edit_hash,post_edit_hash,HASH_BUF_LENGTH)==0) {
-               log_message(LOG_LEVEL_DEFAULT,EDITOR_MESSAGE_NO_CHANGE);
-               return -1;
-       }
-
-       return 1;
-       clean:
-               wordfree(&result);
-               return -1;
-}
\ No newline at end of file
diff --git a/src/exec.c b/src/exec.c
deleted file mode 100644 (file)
index b4922bf..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#include<exec.h>
-
-int exec_cmd(char **argv, char *input, char *output, size_t output_len) {
-       int parent_to_child_fds[2];
-       int child_to_parent_fds[2];
-
-       if(pipe(parent_to_child_fds)==-1) {
-               perror("pipe() failed");
-               return -1;
-       }
-
-       if(pipe(child_to_parent_fds)==-1) {
-               perror("pipe() failed");
-               return -1;
-       }
-
-       pid_t pid = fork();
-       if(pid==-1) {
-               perror("fork() failed");
-               return -1;
-       } else if(pid==0) { // child
-               int i = 0;
-               while(argv[i]!=NULL) {
-                       log_message(LOG_LEVEL_VERBOSE,"%s ",argv[i]);
-                       i++;
-               }
-
-               while((dup2(parent_to_child_fds[0],STDIN_FILENO)==-1)&&(errno==EINTR)) {}
-               while((dup2(child_to_parent_fds[1],STDOUT_FILENO)==-1)&&(errno==EINTR)) {}
-               close(parent_to_child_fds[0]);
-               close(parent_to_child_fds[1]);
-               close(child_to_parent_fds[0]);
-               close(child_to_parent_fds[1]);
-
-               execvp(argv[0],argv);
-               perror("execvp() failed");
-               exit(1);
-       }
-       
-       close(child_to_parent_fds[1]);
-
-       if(NULL!=input) {
-               while(1) {
-                       ssize_t count = write(parent_to_child_fds[1],input,strlen(input)+1);
-                       if(-1==count) {
-                               if(EINTR==errno) {
-                                       // allow for retry if interrupted
-                                       continue;
-                               } else {
-                                       perror("read() failed");
-                                       return -1;
-                               }
-                       }
-                       break;
-               }
-       }
-
-       close(parent_to_child_fds[0]);
-       close(parent_to_child_fds[1]);
-
-       int status = 0;
-       if(wait(&status)==-1) {
-               perror("wait() failed");
-               return -1;
-       }
-
-       if(status!=0) {
-               log_message(LOG_LEVEL_VERBOSE,EXEC_INVALID_RETURN_STATUS,argv[0],status);
-               return -1;
-       }
-
-       if(NULL!=output) {
-               while(1) {
-                       ssize_t count = read(child_to_parent_fds[0],output,output_len);
-                       if(-1==count) {
-                               if(EINTR==errno) {
-                                       // allow for retry if interrupted
-                                       continue;
-                               } else {
-                                       perror("read() failed");
-                                       return -1;
-                               }
-                       }
-                       break;
-               }
-
-               // remove final newline from output (if exists)
-               strtok(output,"\n");
-               // make sure that output ends w/ '\0'
-               output[output_len-1] = '\0';
-       }
-
-       close(child_to_parent_fds[0]);
-       
-       return 1;
-}
-
-int exec_cmd_with_stds(char **argv) {
-       pid_t pid = fork();
-       if(pid==-1) {
-               perror("fork() failed");
-               return -1;
-       } else if(pid==0) { // child
-               execvp(argv[0],argv);
-               perror("execvp() failed");
-               exit(1);
-       }
-
-       int status = 0;
-       if(wait(&status)==-1) {
-               perror("wait() failed");
-               return -1;
-       }
-
-       if(status!=0) {
-               log_message(LOG_LEVEL_VERBOSE,EXEC_INVALID_RETURN_STATUS,argv[0],status);
-               return -1;
-       }
-
-       return 1;
-}
\ No newline at end of file
diff --git a/src/git.c b/src/git.c
deleted file mode 100644 (file)
index 57c402b..0000000
--- a/src/git.c
+++ /dev/null
@@ -1,24 +0,0 @@
-#include<git.h>
-
-char *argv[10];
-
-int git(enum git_exec_flags flags, char *input, char *output, size_t output_len, ...) {
-       va_list args;
-       va_start(args,output_len);
-       int i;
-
-       memset(argv,0,sizeof argv);
-
-       i = 0;
-       while(1) {
-               argv[i] = va_arg(args,char*);
-               if(NULL==argv[i]) { break; }
-               i++;
-       }
-       va_end(args);
-       
-       if(flags&GIT_EXEC_FLAG_WITH_STDS) {
-               return exec_cmd_with_stds(argv);
-       }
-       return exec_cmd(argv,input,output,output_len);
-}
\ No newline at end of file
diff --git a/src/hash.c b/src/hash.c
deleted file mode 100644 (file)
index 663844f..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#include<hash.h>
-
-int hash(char *filename, char *buf, int buf_len) {
-       assert(buf_len==HASH_BUF_LENGTH);
-
-       if(git_hash_object(filename,HASH_OBJECT_DEFAULT,buf,HASH_BUF_LENGTH)<0) { return -1; }
-
-       return 1;
-}
\ No newline at end of file
diff --git a/src/log.c b/src/log.c
deleted file mode 100644 (file)
index 69303e3..0000000
--- a/src/log.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include<log.h>
-
-int verbose_flag = LOG_LEVEL_DEFAULT;
-
-void log_message(enum log_level level, const char *format,...) {
-       if(verbose_flag>=0) {
-               if(level<=verbose_flag) {
-                       va_list args;
-                       va_start(args,format);
-                       vprintf(format,args);
-                       va_end(args);
-               }
-       }
-}
\ No newline at end of file
diff --git a/src/ls.c b/src/ls.c
deleted file mode 100644 (file)
index bf676c8..0000000
--- a/src/ls.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include<ls.h>
-
-int ls(int ls_flag) {
-       log_message(LOG_LEVEL_VERBOSE,"flag: %d\n",ls_flag);
-       global_opts.ls_options = (GIT_LS_TREE_OPTION_FULL==ls_flag)?GIT_LS_TREE_OPTION_FULL:GIT_LS_TREE_OPTION_NAME_ONLY;
-       
-       log_message(LOG_LEVEL_VERBOSE,LS_MESSAGE_TREE_LEVEL,ls_flag);
-
-       // get list of files based on directory relative to git root
-       if(git_ls_tree(NULL,NULL,0)<0) { return EXIT_FAILURE; }
-       
-       return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/src/main.c b/src/main.c
deleted file mode 100644 (file)
index 2e57811..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#include<main.h>
-
-/*
- * main sets the following flags
- * int verbose_flag (defined in log.c)
- */
-
-int ls_flag = GIT_LS_TREE_OPTION_DEFAULT;
-
-static struct option long_options[] = {
-       {"long", no_argument, &ls_flag, GIT_LS_TREE_OPTION_FULL},
-       {"verbose", no_argument, &verbose_flag, LOG_LEVEL_VERBOSE},
-       {"quiet", no_argument, &verbose_flag, LOG_LEVEL_SILENT},
-       {"branch", required_argument, 0, 'b'},
-       {"editor", required_argument, 0, 'e'},
-       {"message", required_argument, 0, 'm'},
-       {"template", required_argument, 0, 't'},
-       {0,0,0,0}
-};
-
-int main(int argc, char **argv) {
-       if(defaults()<0) { return EXIT_FAILURE; }
-
-       int c;
-
-       while(1) {
-               int option_index = 0;
-
-               if((c = getopt_long(argc,argv,"vqm:b:e:lt:",long_options,&option_index))==-1) { break; }
-
-               switch(c) {
-                       case 0:
-                               if(long_options[option_index].flag!=0) { break; }
-
-                               log_message(LOG_LEVEL_VERBOSE,"option %s",long_options[option_index].name);
-                               if(optarg) {
-                                       log_message(LOG_LEVEL_VERBOSE," with arg %s",optarg);
-                               }
-                               log_message(LOG_LEVEL_VERBOSE,"\n");
-                               return EXIT_FAILURE;
-                               
-                               break;
-                       case 'b':
-                               global_opts.branch = optarg;
-                               break;
-                       case 'e':
-                               if(NULL!=global_opts.editor) {
-                                       free(global_opts.editor);
-                               }
-                               global_opts.editor = optarg;
-                               break;
-                       case 'l':
-                               ls_flag = GIT_LS_TREE_OPTION_FULL;
-                               break;
-                       case 'm':
-                               global_opts.message = optarg;
-                               break;
-                       case 'q':
-                               verbose_flag = LOG_LEVEL_ERRORS;
-                               break;
-                       case 't':
-                               global_opts.template = optarg;
-                               break;
-                       case 'v':
-                               verbose_flag = LOG_LEVEL_VERBOSE;
-                               break;
-                       case '?':
-                       default:
-                               return EXIT_FAILURE;
-               }
-       }
-
-       log_message(LOG_LEVEL_VERBOSE,MAIN_BRANCH_SET_MESSAGE,global_opts.branch);
-       log_message(LOG_LEVEL_VERBOSE,MAIN_EDITOR_SET_MESSAGE,global_opts.editor);
-
-       if(optind<argc) {
-               char *p = argv[optind];
-               if(strcmp(MAIN_SUBCOMMAND_ADD,p)==0) {
-                       if(optind+2==argc) { // 1 extra arg: path
-                               return add(argv[optind+1]);
-                       }
-               } else if(strcmp(MAIN_SUBCOMMAND_CAT,p)==0) {
-                       if(optind+2==argc) { // 1 extra arg: path
-                               return cat(argv[optind+1]);
-                       }
-               } else if(strcmp(MAIN_SUBCOMMAND_EDIT,p)==0) {
-                       if(optind+2==argc) { // 1 extra arg: path
-                               return edit(argv[optind+1]);
-                       }
-               } else if(strcmp(MAIN_SUBCOMMAND_LS,p)==0) {
-                       if(optind+1==argc) {
-                               return ls(ls_flag);
-                       }
-               } else {
-                       log_message(LOG_LEVEL_ERRORS,MAIN_UNKNOWN_SUBCOMMAND_MESSAGE,p);
-                       return EXIT_FAILURE;
-               }
-       } else {
-               return ls(ls_flag);
-       }
-
-       log_message(LOG_LEVEL_ERRORS,MAIN_WRONG_NUMBER_ARGS);
-       return EXIT_FAILURE;
-}
\ No newline at end of file
diff --git a/src/prepend.c b/src/prepend.c
deleted file mode 100644 (file)
index f6c02fe..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#include<prepend.h>
-
-char *prepend(char *prefix, char *str) {
-       char *p;
-       int len;
-
-       assert(prefix!=NULL);
-
-       if(NULL==str) { return NULL; }
-       
-       len = strlen(str);
-       len += strlen(prefix);
-       
-       //sanity check len - no way branch name should be this large
-       if(len>100) { return NULL; }
-
-       p = malloc(sizeof(char)*(len));
-       
-       strcpy(p,prefix);
-       strcat(p,str);
-
-       return p;
-}
\ No newline at end of file
diff --git a/src/templ.c b/src/templ.c
deleted file mode 100644 (file)
index e2e62e9..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#include<templ.h>
-
-int template(int tmp_fd) {
-       /*
-        * copy template to temp file (if exists)
-        * will treat global_opts.template as a full path first,
-        * then search [ADD_TEMPLATE_PREFIX].template (see add.h)
-        */
-
-       if(NULL==global_opts.template) { return 0; }
-
-       FILE *fp = fopen(global_opts.template,"r");
-       if(NULL==fp) {
-               int len = strlen(global_opts.template);
-               
-               /*
-                * this is defined using autotools and
-                * can be modified with ./configure. See 
-                * templatesdir varible in top-level
-                * Makefile.am.
-                */
-               char templatedir[] = TEMPLATE_DIR;
-               len += strlen(templatedir);
-               
-               char *fullname = malloc(sizeof(char)*(len+TEMPLATE_EXTRA_LENGTH+1));
-               if(NULL==fullname) { return -1; }
-
-               strcpy(fullname,TEMPLATE_DIR);
-               strcat(fullname,TEMPLATE_PREFIX);
-               strcat(fullname,global_opts.template);
-               strcat(fullname,TEMPLATE_SUFFIX);
-
-               fp = fopen(fullname,"r");
-
-               free(fullname);
-       }
-
-       if(fp==NULL) { return 0; }
-       
-       if(0!=fseek(fp,0,SEEK_END)) {
-               perror("fseek() failed");
-               return -1;
-       }
-
-       long size = ftell(fp);
-       if(size<0) {
-               perror("ftell() failed");
-               return -1;
-       }
-
-       rewind(fp);
-
-       size_t count = size;
-
-       int template_fd = fileno(fp);
-       off_t off = 0;
-       while(count>0) {
-               ssize_t sent = sendfile(tmp_fd,template_fd,&off,count);
-               if(sent<0) {
-                       perror("sendfile() failed");
-                       fclose(fp);
-                       return -1;
-               }
-               count -= sent;
-       }
-
-       if(0!=fclose(fp)) {
-               perror("fclose() failed");
-               return -1;
-       }
-
-       return 1;
-}
\ No newline at end of file
diff --git a/test/add.tests.sh b/test/add.tests.sh
new file mode 100755 (executable)
index 0000000..ac7283a
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+EDITOR="sh -c 'echo \"alskdjfalskdfjsadf\" >> \"%s\"'"
+
+set +e
+(add > result 2>&1)
+EXPECTED=$'creating branch '
+EXPECTED+="$BRANCH"
+EXPECTED+=$'\nno path given to add\n'
+EXPECTED+="$USAGE"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+(add test test2 > result 2>&1)
+EXPECTED=$'no path given to add\n'
+EXPECTED+="$USAGE"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+TEMPLATE="notanexistingtemplate"
+(add test > result 2>&1)
+EXPECTED="template 'notanexistingtemplate' doesn't exist"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+TEMPLATE=
+
+set -e
+
+add "todo" > result 2>&1
+TEMP=`head -n 1 result | sed 's/opening temporary file //' | tr -d "'"`
+EXPECTED="opening temporary file '$TEMP'"
+EXPECTED+=$'\nadding todo to index\nreseting branch back to '
+EXPECTED+="$WORKING_BRANCH"
+EXPECTED+=$'\nadded todo'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+EXPECTED=$'100644 blob b2fd069fbb2dfb5f9e7fa3245e763a3cae08ef43\ttodo'
+assert "$EXPECTED" "`git ls-tree $BRANCH`"
+
+set +e
+
+(add "todo" > result 2>&1)
+EXPECTED=$'file \'todo\' already exists; did you mean to edit \'todo\' instead?\n'
+EXPECTED+="$USAGE"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+set -e
+
+TEMPLATE="test.txt"
+add "test1copy" > result 2>&1
+TEMP=`head -n 2 result | tail -n 1 | sed 's/opening temporary file//' | tr -d "'" | tr -d " "`
+EXPECTED="using template '$TEMPLATE'"
+EXPECTED+=$'\nopening temporary file '
+EXPECTED+="'$TEMP'"
+EXPECTED+=$'\nadding test1copy to index'
+EXPECTED+=$'\nreseting branch back to master\nadded test1copy'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+EXPECTED=$'100644 blob ce643f67f68e645a58da5b64d77f18dbc5e8c1f9\ttest1copy\n100644 blob b2fd069fbb2dfb5f9e7fa3245e763a3cae08ef43\ttodo'
+assert "$EXPECTED" "`git ls-tree $BRANCH`"
+TEMPLATE=
+
+test_succeeded
diff --git a/test/add_to_index.tests.sh b/test/add_to_index.tests.sh
new file mode 100755 (executable)
index 0000000..f55d297
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+
+TEMP=`mktemp`
+echo "alksdjflaksdjfklajsdkflj" > "$TEMP"
+
+add_to_index "$TEMP" "todo" > result 2>&1
+EXPECTED=$'adding todo to index\ncreating branch '
+EXPECTED+="$BRANCH"
+EXPECTED+=$'\nreseting branch back to '
+EXPECTED+="$WORKING_BRANCH"
+RESULT=$(<result)
+
+assert "$EXPECTED" "$RESULT"
+
+EXPECTED=$'100644 blob 363c8e40561f98736e889697d86ca66178997cff\ttodo'
+assert "$EXPECTED" "`git ls-tree $BRANCH`"
+EXPECTED=$'100644 blob c8ced6bb17c2965c058f5cdb5677cb8cd9091f9c\thi/test/nope/what/test3.txt\n100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest.txt\n100644 blob a8453014c7d0581a0d904792f009f1ad7ff477a1\ttest2.txt'
+assert "$EXPECTED" "`git ls-tree -r $WORKING_BRANCH`"
+assert "$EXPECTED" "`git ls-tree -r HEAD`"
+
+test_succeeded
diff --git a/test/branch_create.tests.sh b/test/branch_create.tests.sh
new file mode 100755 (executable)
index 0000000..8408fa9
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+assert "0" "`git branch --list $BRANCH | wc -c`"
+
+LOG_LEVEL=info
+branch_create > result 2>&1
+EXPECTED="creating branch $BRANCH"
+RESULT=$(<result)
+
+assert "$EXPECTED" "$RESULT"
+
+assert "13" "`git branch --list $BRANCH | wc -c`"
+assert "" "`git ls-tree $BRANCH`"
+
+test_succeeded
diff --git a/test/branch_exists.tests.sh b/test/branch_exists.tests.sh
new file mode 100755 (executable)
index 0000000..0d360e6
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+LOG_LEVEL=info
+
+set +e
+BRANCH="mas.."
+(branch_exists > result 2>&1)
+EXPECTED="creating branch $BRANCH"
+# NOTE: this normally would stop after first fatal, but since set +e above, has second fatal error
+EXPECTED+=$'\nfatal: \'mas..\' is not a valid branch name\nfatal: update_ref failed for ref \'refs/heads/mas..\': refusing to update ref with bad name \'refs/heads/mas..\''
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+set -e
+
+assert "0" "`git branch --list $BRANCH | wc -c`"
+
+BRANCH="testbranch"
+assert "0" "`git branch --list $BRANCH | wc -c`"
+
+branch_exists > result 2>&1
+EXPECTED="creating branch $BRANCH"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+assert "13" "`git branch --list $BRANCH | wc -c`"
+assert "" "`git ls-tree $BRANCH`"
+
+test_succeeded
diff --git a/test/branch_hash.tests.sh b/test/branch_hash.tests.sh
new file mode 100755 (executable)
index 0000000..70d637f
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+
+EXPECTED=$'100644 blob c8ced6bb17c2965c058f5cdb5677cb8cd9091f9c\thi/test/nope/what/test3.txt\n100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest.txt\n100644 blob a8453014c7d0581a0d904792f009f1ad7ff477a1\ttest2.txt'
+assert "$EXPECTED" "`git ls-tree -r HEAD`"
+
+branch_hash > result 2>&1
+EXPECTED="creating branch $BRANCH"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+assert "master" "$WORKING_BRANCH"
+assert "41" "`echo $PREV_COMMIT | wc -c`"
+assert "" "`git ls-tree $PREV_COMMIT`"
+
+assert "" "`git ls-tree HEAD`"
+
+test_succeeded
diff --git a/test/branch_reset.tests.sh b/test/branch_reset.tests.sh
new file mode 100755 (executable)
index 0000000..06048e1
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+
+EXPECTED_TREE=$'100644 blob c8ced6bb17c2965c058f5cdb5677cb8cd9091f9c\thi/test/nope/what/test3.txt\n100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest.txt\n100644 blob a8453014c7d0581a0d904792f009f1ad7ff477a1\ttest2.txt'
+assert "$EXPECTED_TREE" "`git ls-tree -r HEAD`"
+
+branch_hash > result 2>&1
+EXPECTED="creating branch $BRANCH"
+RESULT=$(<result)
+
+assert "$EXPECTED" "$RESULT"
+
+assert "master" "$WORKING_BRANCH"
+assert "" "`git ls-tree $PREV_COMMIT`"
+
+assert "$PREV_COMMIT" "`git rev-parse HEAD`"
+
+branch_reset > result 2>&1
+EXPECTED="reseting branch back to $WORKING_BRANCH"
+RESULT=$(<result)
+
+assert "$EXPECTED" "$RESULT"
+assert "$EXPECTED_TREE" "`git ls-tree -r HEAD`"
+
+test_succeeded
diff --git a/test/cat_file.tests.sh b/test/cat_file.tests.sh
new file mode 100755 (executable)
index 0000000..743fe6b
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+EDITOR="sh -c 'echo \"alskdjfalskdfjsadf\" >> \"%s\"'"
+
+set +e
+(cat_file "notanexistingfile" > result 2>&1)
+EXPECTED=$'creating branch testbranch\nnotanexistingfile doesn\'t exist'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+set -e
+
+add "todo" > result 2>&1
+
+cat_file "todo" > result 2>&1
+EXPECTED="alskdjfalskdfjsadf"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+test_succeeded
diff --git a/test/edit.tests.sh b/test/edit.tests.sh
new file mode 100755 (executable)
index 0000000..4c0b494
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+EDITOR="sh -c 'echo \"alskdjfalskdfjsadf\" >> \"%s\"'"
+
+set +e
+(edit "todo" > result 2>&1)
+TEMP=`head -n 1 result | sed 's/editing todo using //' | tr -d "'"`
+EXPECTED="editing todo using $TEMP"
+EXPECTED+=$'\ncreating branch testbranch\ntodo doesn\'t exist'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+set -e
+
+add "todo" > result 2>&1
+
+edit "todo" > result 2>&1
+TEMP=`head -n 1 result | sed 's/editing todo using //' | tr -d "'"`
+EXPECTED="editing todo using $TEMP"
+EXPECTED+=$'\nopening temporary file \''
+EXPECTED+="$TEMP"
+EXPECTED+=$'\'\nadding todo to index\nreseting branch back to master\nsaved todo'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+cat_file "todo" > result 2>&1
+EXPECTED="alskdjfalskdfjsadf"
+EXPECTED+=$'\nalskdjfalskdfjsadf'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+test_succeeded
diff --git a/test/editor.tests.sh b/test/editor.tests.sh
new file mode 100755 (executable)
index 0000000..31a1e63
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="testbranch"
+LOG_LEVEL=info
+
+TEMP=`mktemp`
+
+set +e
+
+EDITOR="sh -c 'echo \"alskdjfalskdfjsadf\"'"
+(editor "$TEMP" > result 2>&1)
+EXPECTED="opening temporary file '$TEMP'"
+EXPECTED+=$'\nalskdjfalskdfjsadf\nno changes detected'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+EDITOR=
+git config --local core.editor "sh -c 'echo \"hello\"'"
+(editor "$TEMP" > result 2>&1)
+EXPECTED="opening temporary file '$TEMP'"
+EXPECTED+=$'\nno editor found, checking git config\nhello\nno changes detected'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+git config --local core.editor ""
+(editor "$TEMP" > result 2>&1)
+EXPECTED="opening temporary file '$TEMP'"
+EXPECTED+=$'\nno editor found, checking git config\nno editor defined; use --editor option, set EDITOR environmental variable, or set git config core.editor.'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+set -e
+
+EDITOR="sh -c 'echo \"alskdjfalskdfjsadf\" >> \"%s\"'"
+editor "$TEMP" > result 2>&1
+EXPECTED="opening temporary file '$TEMP'"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+test_succeeded
diff --git a/test/integration/Makefile.am b/test/integration/Makefile.am
deleted file mode 100644 (file)
index d48a721..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-EXTRA_DIST = \
-       index.js \
-       package.json \
-       package-lock.json \
-       test/utils.js \
-       test/ls.integration.tests.js \
-       test/cat.integration.tests.js \
-       test/edit.integration.tests.js \
-       test/add.integration.tests.js \
-       test/branch.pattern.integration.test.js
-
-if HAVE_DOCKER
-PACKAGE_CONTAINER_NAME=git_extra_package_init
-
-package_init:
-       docker run -d --name $(PACKAGE_CONTAINER_NAME) node:latest tail -f /dev/null
-       docker cp package.json $(PACKAGE_CONTAINER_NAME):/home/node/
-       docker cp package-lock.json $(PACKAGE_CONTAINER_NAME):/home/node/
-
-package_extract:
-       docker cp $(PACKAGE_CONTAINER_NAME):/home/node/package.json ./package.json
-       docker cp $(PACKAGE_CONTAINER_NAME):/home/node/package-lock.json ./package-lock.json
-       $(MAKE) -s package_stop
-
-package_stop:
-       docker stop $(PACKAGE_CONTAINER_NAME)
-       docker rm $(PACKAGE_CONTAINER_NAME)
-
-outdated: package_init
-       -docker exec -it -w /home/node $$(docker container ls -q -f name=$(PACKAGE_CONTAINER_NAME)) npm outdated
-       $(MAKE) -s package_stop
-
-update: package_init
-       -docker exec -it -w /home/node $$(docker container ls -q -f name=$(PACKAGE_CONTAINER_NAME)) /bin/bash
-       $(MAKE) -s package_extract
-
-inspect: package_init
-       docker exec -it $$(docker container ls -q -f name=$(PACKAGE_CONTAINER_NAME)) /bin/bash
-
-else
-build: missing-local
-run: missing-local
-extract: missing-local
-start: missing-local
-stop: missing-local
-package_init: missing-local
-package_extract: missing-local
-package_stop: missing-local
-outdated: missing-local
-update: missing-local
-inspect: missing-local
-missing-local:
-       @echo "docker not found"
-endif
-
-if HAVE_NODE
-all-local:
-       npm install
-
-check-local:
-       npm run test
-else
-all-local: missing-local
-check-local: missing-local
-missing-local:
-       @echo "node not found"
-endif
\ No newline at end of file
diff --git a/test/integration/index.js b/test/integration/index.js
deleted file mode 100644 (file)
index a14ab47..0000000
+++ /dev/null
@@ -1 +0,0 @@
-console.log("Run `npm run test` to run integration tests");
\ No newline at end of file
diff --git a/test/integration/package-lock.json b/test/integration/package-lock.json
deleted file mode 100644 (file)
index bd1556a..0000000
+++ /dev/null
@@ -1,1166 +0,0 @@
-{
-  "name": "git-extra-integration-tests",
-  "version": "0.0.0",
-  "lockfileVersion": 1,
-  "requires": true,
-  "dependencies": {
-    "ansi-colors": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
-      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
-      "dev": true
-    },
-    "ansi-regex": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-      "dev": true,
-      "requires": {
-        "color-convert": "^2.0.1"
-      }
-    },
-    "anymatch": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
-      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
-      "dev": true,
-      "requires": {
-        "normalize-path": "^3.0.0",
-        "picomatch": "^2.0.4"
-      }
-    },
-    "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dev": true,
-      "requires": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "array.prototype.map": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz",
-      "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.0-next.1",
-        "es-array-method-boxes-properly": "^1.0.0",
-        "is-string": "^1.0.4"
-      }
-    },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
-    },
-    "binary-extensions": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
-      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
-      "dev": true
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "braces": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-      "dev": true,
-      "requires": {
-        "fill-range": "^7.0.1"
-      }
-    },
-    "browser-stdout": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
-      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
-      "dev": true
-    },
-    "camelcase": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
-      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
-      "dev": true
-    },
-    "chalk": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
-      "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^4.1.0",
-        "supports-color": "^7.1.0"
-      }
-    },
-    "chokidar": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
-      "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
-      "dev": true,
-      "requires": {
-        "anymatch": "~3.1.1",
-        "braces": "~3.0.2",
-        "fsevents": "~2.1.2",
-        "glob-parent": "~5.1.0",
-        "is-binary-path": "~2.1.0",
-        "is-glob": "~4.0.1",
-        "normalize-path": "~3.0.0",
-        "readdirp": "~3.4.0"
-      }
-    },
-    "cliui": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
-      "dev": true,
-      "requires": {
-        "string-width": "^3.1.0",
-        "strip-ansi": "^5.2.0",
-        "wrap-ansi": "^5.1.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        }
-      }
-    },
-    "color-convert": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-      "dev": true,
-      "requires": {
-        "color-name": "~1.1.4"
-      }
-    },
-    "color-name": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "debug": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-      "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
-      "dev": true,
-      "requires": {
-        "ms": "^2.1.1"
-      }
-    },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
-      "dev": true
-    },
-    "define-properties": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
-      "requires": {
-        "object-keys": "^1.0.12"
-      }
-    },
-    "diff": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
-      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
-      "dev": true
-    },
-    "emoji-regex": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
-      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
-      "dev": true
-    },
-    "es-abstract": {
-      "version": "1.17.7",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
-      "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
-      "requires": {
-        "es-to-primitive": "^1.2.1",
-        "function-bind": "^1.1.1",
-        "has": "^1.0.3",
-        "has-symbols": "^1.0.1",
-        "is-callable": "^1.2.2",
-        "is-regex": "^1.1.1",
-        "object-inspect": "^1.8.0",
-        "object-keys": "^1.1.1",
-        "object.assign": "^4.1.1",
-        "string.prototype.trimend": "^1.0.1",
-        "string.prototype.trimstart": "^1.0.1"
-      },
-      "dependencies": {
-        "es-abstract": {
-          "version": "1.18.0-next.1",
-          "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
-          "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
-          "requires": {
-            "es-to-primitive": "^1.2.1",
-            "function-bind": "^1.1.1",
-            "has": "^1.0.3",
-            "has-symbols": "^1.0.1",
-            "is-callable": "^1.2.2",
-            "is-regex": "^1.1.1",
-            "object-inspect": "^1.8.0",
-            "object-keys": "^1.1.1",
-            "object.assign": "^4.1.1",
-            "string.prototype.trimend": "^1.0.1",
-            "string.prototype.trimstart": "^1.0.1"
-          }
-        },
-        "object.assign": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
-          "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
-          "requires": {
-            "define-properties": "^1.1.3",
-            "has-symbols": "^1.0.1",
-            "object-keys": "^1.1.1"
-          }
-        }
-      }
-    },
-    "es-array-method-boxes-properly": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
-      "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
-      "dev": true
-    },
-    "es-get-iterator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz",
-      "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==",
-      "dev": true,
-      "requires": {
-        "es-abstract": "^1.17.4",
-        "has-symbols": "^1.0.1",
-        "is-arguments": "^1.0.4",
-        "is-map": "^2.0.1",
-        "is-set": "^2.0.1",
-        "is-string": "^1.0.5",
-        "isarray": "^2.0.5"
-      }
-    },
-    "es-to-primitive": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
-      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
-      "requires": {
-        "is-callable": "^1.1.4",
-        "is-date-object": "^1.0.1",
-        "is-symbol": "^1.0.2"
-      }
-    },
-    "escape-string-regexp": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
-      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
-      "dev": true
-    },
-    "esprima": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-      "dev": true
-    },
-    "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "requires": {
-        "to-regex-range": "^5.0.1"
-      }
-    },
-    "find-up": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
-      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
-      "dev": true,
-      "requires": {
-        "locate-path": "^6.0.0",
-        "path-exists": "^4.0.0"
-      }
-    },
-    "flat": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
-      "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
-      "dev": true,
-      "requires": {
-        "is-buffer": "~2.0.3"
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "fsevents": {
-      "version": "2.1.3",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
-      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
-      "dev": true,
-      "optional": true
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
-    },
-    "get-caller-file": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true
-    },
-    "glob": {
-      "version": "7.1.6",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
-      "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "glob-parent": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
-      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
-      "dev": true,
-      "requires": {
-        "is-glob": "^4.0.1"
-      }
-    },
-    "growl": {
-      "version": "1.10.5",
-      "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
-      "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
-      "dev": true
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "has-flag": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
-      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
-    },
-    "he": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
-      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "is-arguments": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
-      "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
-      "dev": true
-    },
-    "is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "requires": {
-        "binary-extensions": "^2.0.0"
-      }
-    },
-    "is-buffer": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
-      "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
-      "dev": true
-    },
-    "is-callable": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
-      "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA=="
-    },
-    "is-date-object": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
-      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-fullwidth-code-point": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-      "dev": true
-    },
-    "is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-      "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-map": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz",
-      "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==",
-      "dev": true
-    },
-    "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
-    },
-    "is-plain-obj": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
-      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
-      "dev": true
-    },
-    "is-regex": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
-      "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
-      "requires": {
-        "has-symbols": "^1.0.1"
-      }
-    },
-    "is-set": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz",
-      "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==",
-      "dev": true
-    },
-    "is-string": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
-      "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
-      "dev": true
-    },
-    "is-symbol": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
-      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
-      "requires": {
-        "has-symbols": "^1.0.1"
-      }
-    },
-    "isarray": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
-      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
-      "dev": true
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
-    },
-    "iterate-iterator": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.1.tgz",
-      "integrity": "sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==",
-      "dev": true
-    },
-    "iterate-value": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz",
-      "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==",
-      "dev": true,
-      "requires": {
-        "es-get-iterator": "^1.0.2",
-        "iterate-iterator": "^1.0.1"
-      }
-    },
-    "js-yaml": {
-      "version": "3.14.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
-      "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
-      "dev": true,
-      "requires": {
-        "argparse": "^1.0.7",
-        "esprima": "^4.0.0"
-      }
-    },
-    "locate-path": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
-      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
-      "dev": true,
-      "requires": {
-        "p-locate": "^5.0.0"
-      }
-    },
-    "log-symbols": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
-      "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
-      "dev": true,
-      "requires": {
-        "chalk": "^4.0.0"
-      }
-    },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "mocha": {
-      "version": "8.1.3",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.3.tgz",
-      "integrity": "sha512-ZbaYib4hT4PpF4bdSO2DohooKXIn4lDeiYqB+vTmCdr6l2woW0b6H3pf5x4sM5nwQMru9RvjjHYWVGltR50ZBw==",
-      "dev": true,
-      "requires": {
-        "ansi-colors": "4.1.1",
-        "browser-stdout": "1.3.1",
-        "chokidar": "3.4.2",
-        "debug": "4.1.1",
-        "diff": "4.0.2",
-        "escape-string-regexp": "4.0.0",
-        "find-up": "5.0.0",
-        "glob": "7.1.6",
-        "growl": "1.10.5",
-        "he": "1.2.0",
-        "js-yaml": "3.14.0",
-        "log-symbols": "4.0.0",
-        "minimatch": "3.0.4",
-        "ms": "2.1.2",
-        "object.assign": "4.1.0",
-        "promise.allsettled": "1.0.2",
-        "serialize-javascript": "4.0.0",
-        "strip-json-comments": "3.0.1",
-        "supports-color": "7.1.0",
-        "which": "2.0.2",
-        "wide-align": "1.1.3",
-        "workerpool": "6.0.0",
-        "yargs": "13.3.2",
-        "yargs-parser": "13.1.2",
-        "yargs-unparser": "1.6.1"
-      }
-    },
-    "ms": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
-      "dev": true
-    },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "dev": true
-    },
-    "object-inspect": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
-      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
-    },
-    "object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
-    },
-    "object.assign": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
-      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "function-bind": "^1.1.1",
-        "has-symbols": "^1.0.0",
-        "object-keys": "^1.0.11"
-      }
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "p-limit": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
-      "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
-      "dev": true,
-      "requires": {
-        "p-try": "^2.0.0"
-      }
-    },
-    "p-locate": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
-      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
-      "dev": true,
-      "requires": {
-        "p-limit": "^3.0.2"
-      }
-    },
-    "p-try": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
-    },
-    "path-exists": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
-      "dev": true
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "picomatch": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
-      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
-      "dev": true
-    },
-    "promise.allsettled": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.2.tgz",
-      "integrity": "sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==",
-      "dev": true,
-      "requires": {
-        "array.prototype.map": "^1.0.1",
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.0-next.1",
-        "function-bind": "^1.1.1",
-        "iterate-value": "^1.0.0"
-      }
-    },
-    "randombytes": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
-      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "readdirp": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
-      "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
-      "dev": true,
-      "requires": {
-        "picomatch": "^2.2.1"
-      }
-    },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
-      "dev": true
-    },
-    "require-main-filename": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-      "dev": true
-    },
-    "safe-buffer": {
-      "version": "5.2.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-      "dev": true
-    },
-    "serialize-javascript": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
-      "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
-      "dev": true,
-      "requires": {
-        "randombytes": "^2.1.0"
-      }
-    },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
-      "dev": true
-    },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
-      "dev": true
-    },
-    "string-width": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
-      "dev": true,
-      "requires": {
-        "is-fullwidth-code-point": "^2.0.0",
-        "strip-ansi": "^4.0.0"
-      }
-    },
-    "string.prototype.trimend": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
-      "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
-      "requires": {
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.5"
-      }
-    },
-    "string.prototype.trimstart": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
-      "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
-      "requires": {
-        "define-properties": "^1.1.3",
-        "es-abstract": "^1.17.5"
-      }
-    },
-    "strip-ansi": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^3.0.0"
-      }
-    },
-    "strip-json-comments": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
-      "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
-      "dev": true
-    },
-    "supports-color": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
-      "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
-      "dev": true,
-      "requires": {
-        "has-flag": "^4.0.0"
-      }
-    },
-    "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-      "dev": true,
-      "requires": {
-        "is-number": "^7.0.0"
-      }
-    },
-    "which": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
-      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
-      "requires": {
-        "isexe": "^2.0.0"
-      }
-    },
-    "which-module": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
-      "dev": true
-    },
-    "wide-align": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
-      "dev": true,
-      "requires": {
-        "string-width": "^1.0.2 || 2"
-      }
-    },
-    "workerpool": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.0.tgz",
-      "integrity": "sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA==",
-      "dev": true
-    },
-    "wrap-ansi": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.0",
-        "string-width": "^3.0.0",
-        "strip-ansi": "^5.0.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "color-convert": {
-          "version": "1.9.3",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-          "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-          "dev": true,
-          "requires": {
-            "color-name": "1.1.3"
-          }
-        },
-        "color-name": {
-          "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-          "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        }
-      }
-    },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    },
-    "y18n": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
-      "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
-      "dev": true
-    },
-    "yargs": {
-      "version": "13.3.2",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
-      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
-      "dev": true,
-      "requires": {
-        "cliui": "^5.0.0",
-        "find-up": "^3.0.0",
-        "get-caller-file": "^2.0.1",
-        "require-directory": "^2.1.1",
-        "require-main-filename": "^2.0.0",
-        "set-blocking": "^2.0.0",
-        "string-width": "^3.0.0",
-        "which-module": "^2.0.0",
-        "y18n": "^4.0.0",
-        "yargs-parser": "^13.1.2"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "find-up": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
-          "dev": true,
-          "requires": {
-            "locate-path": "^3.0.0"
-          }
-        },
-        "locate-path": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
-          "dev": true,
-          "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
-          }
-        },
-        "p-limit": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-          "dev": true,
-          "requires": {
-            "p-try": "^2.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-          "dev": true,
-          "requires": {
-            "p-limit": "^2.0.0"
-          }
-        },
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        }
-      }
-    },
-    "yargs-parser": {
-      "version": "13.1.2",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^5.0.0",
-        "decamelize": "^1.2.0"
-      }
-    },
-    "yargs-unparser": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz",
-      "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^5.3.1",
-        "decamelize": "^1.2.0",
-        "flat": "^4.1.0",
-        "is-plain-obj": "^1.1.0",
-        "yargs": "^14.2.3"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "find-up": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
-          "dev": true,
-          "requires": {
-            "locate-path": "^3.0.0"
-          }
-        },
-        "locate-path": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
-          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
-          "dev": true,
-          "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
-          }
-        },
-        "p-limit": {
-          "version": "2.3.0",
-          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
-          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
-          "dev": true,
-          "requires": {
-            "p-try": "^2.0.0"
-          }
-        },
-        "p-locate": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
-          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
-          "dev": true,
-          "requires": {
-            "p-limit": "^2.0.0"
-          }
-        },
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        },
-        "yargs": {
-          "version": "14.2.3",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
-          "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==",
-          "dev": true,
-          "requires": {
-            "cliui": "^5.0.0",
-            "decamelize": "^1.2.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^2.0.1",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^3.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^15.0.1"
-          }
-        },
-        "yargs-parser": {
-          "version": "15.0.1",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz",
-          "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
-        }
-      }
-    }
-  }
-}
diff --git a/test/integration/package.json b/test/integration/package.json
deleted file mode 100644 (file)
index 268a7a1..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "name": "git-extra-integration-tests",
-  "version": "0.0.0",
-  "description": "",
-  "main": "index.js",
-  "scripts": {
-    "test": "mocha"
-  },
-  "author": "",
-  "license": "UNLICENSED",
-  "devDependencies": {
-    "mocha": "^8.1.3"
-  }
-}
diff --git a/test/integration/test/add.integration.tests.js b/test/integration/test/add.integration.tests.js
deleted file mode 100644 (file)
index 13ed888..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-const assert = require('assert');
-const util = require('util');
-
-const exec = util.promisify(require('child_process').exec);
-const fs_stat = util.promisify(require('fs').stat);
-const fs_readFile = util.promisify(require('fs').readFile);
-
-const {reset_env,setup_env} = require('./utils.js');
-
-describe('add integration tests', () => {
-
-       /*
-        * This file should contain tests to test the full add functionality,
-        * from parsing command line options and their interactions with adding files.
-        * Should also test behavior of add which depends on state.
-        */
-
-       beforeEach(async() => {
-               await reset_env();
-               await setup_env();      
-       });
-
-       it('should succeed when adding a new file', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex add hello.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'added hello.txt\n',stderr:''});
-               });
-       });
-
-       it('should fail when not given a file', async() => {
-               await assert.rejects(async() => await exec('git ex add',{cwd:"/home/node/working"}));
-       });
-
-       it('should succeed when given a valid template and verify template was applied', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex add -t bug hello.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'added hello.txt\n',stderr:''});
-
-                       const expected = (await fs_readFile("/usr/local/share/git-extra/templates/bug.template","utf8"))+"hello\n";
-                       const written = (await exec('git ex cat hello.txt',{cwd:"/home/node/working"})).stdout;
-
-                       assert.deepStrictEqual(expected,written);
-               });
-       });
-
-       it(`should succeed when given a template which doesn't exist`, async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex add -t templatewhichdoesnotexist hello.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'added hello.txt\n',stderr:''});
-
-                       const written = (await exec('git ex cat hello.txt',{cwd:"/home/node/working"})).stdout;
-                       assert.deepStrictEqual("hello\n",written);
-               });
-       });
-
-       it('should fail when attempting to add a file that has already been added', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex add hello.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'added hello.txt\n',stderr:''});
-                       
-                       await assert.rejects(async() => await exec('git ex add hello.txt',{cwd:"/home/node/working"}));
-               });
-       });
-
-       it('should delete temporary file after adding', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex add -v hello.txt',{cwd:"/home/node/working"});
-                       const index = res.stdout.indexOf("temporary file ");
-                       assert(index>-1);
-                       const tmp_filename = res.stdout.substr(index+15,11);
-                       await assert.rejects(async() => fs_stat(tmp_filename));
-               });
-       });
-
-       after(async() => {
-               await reset_env();
-       });
-
-});
\ No newline at end of file
diff --git a/test/integration/test/branch.pattern.integration.test.js b/test/integration/test/branch.pattern.integration.test.js
deleted file mode 100644 (file)
index d0f6e8c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-const assert = require('assert');
-const util = require('util');
-
-const exec = util.promisify(require('child_process').exec);
-
-const {reset_env,setup_env} = require('./utils.js');
-
-describe('branch pattern integration test', () => {
-
-       before(async() => {
-               await reset_env();
-               await setup_env();
-       });
-
-       it('should fail when given a branch pattern instead of the full branch', async() => {
-               await assert.rejects(async() => await exec('git ex add -b mas* hello.txt',{cwd:"/home/node/working"}));
-       });
-
-});
\ No newline at end of file
diff --git a/test/integration/test/cat.integration.tests.js b/test/integration/test/cat.integration.tests.js
deleted file mode 100644 (file)
index bee78ab..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-const assert = require('assert');
-const util = require('util');
-
-const exec = util.promisify(require('child_process').exec);
-
-const {reset_env,setup_env} = require('./utils.js');
-
-describe('cat integration tests', () => {
-
-       /*
-        * This file should contain tests to test cat functionality
-        */
-
-       beforeEach(async() => {
-               await reset_env();
-               await setup_env();
-               await exec('git ex add hello.txt',{cwd:"/home/node/working"});
-       });
-
-       it('should cat successfully', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex cat hello.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'hello\n',stderr:''});
-               });
-       });
-
-       it(`should fail when given a file which doesn't exist`, async() => {
-               await assert.rejects(async() => await exec('git ex cat -b extra hello1.txt',{cwd:"/home/node/working"}));
-       });
-
-       it(`should fail when given a file which doesn't exist on the given branch`, async() => {
-               await assert.rejects(async() => await exec('git ex cat -b notarealbranch hello1.txt',{cwd:"/home/node/working"}));
-       });
-
-       it('should cat successfully on the current branch', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex cat -b master test.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'test\n',stderr:''});
-               });
-       });
-
-       after(async() => {
-               await reset_env();
-       });
-
-});
\ No newline at end of file
diff --git a/test/integration/test/edit.integration.tests.js b/test/integration/test/edit.integration.tests.js
deleted file mode 100644 (file)
index d27b9d4..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-const assert = require('assert');
-const util = require('util');
-
-const exec = util.promisify(require('child_process').exec);
-
-const {reset_env,setup_env} = require('./utils.js');
-
-describe('edit integration tests', () => {
-
-       /*
-        * This file should contain tests to test the full edit functionality,
-        * from parsing command line options and their interactions with editting files.
-        * Should also test behavior of edit which depends on state.
-        */
-
-       beforeEach(async() => {
-               await reset_env();
-               await setup_env();
-               await exec('git ex add hello.txt',{cwd:"/home/node/working"});
-       });
-
-       it('should succeed when editting a new file', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex edit hello.txt',{cwd:"/home/node/working"});
-                       assert.deepStrictEqual(res,{stdout:'hello.txt saved\n',stderr:''});
-               });
-       });
-
-       it('should delete temporary file after editting', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex edit -v hello.txt',{cwd:"/home/node/working"});
-                       const index = res.stdout.indexOf("temporary file ");
-                       assert(index>-1);
-                       const tmp_filename = res.stdout.substr(index+15,11);
-                       await assert.rejects(async() => fs_stat(tmp_filename));
-               });
-       });
-
-       after(async() => {
-               await reset_env();
-       });
-
-});
\ No newline at end of file
diff --git a/test/integration/test/ls.integration.tests.js b/test/integration/test/ls.integration.tests.js
deleted file mode 100644 (file)
index 7e5e2c0..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-const assert = require('assert');
-const util = require('util');
-
-const exec = util.promisify(require('child_process').exec);
-
-const {reset_env,setup_env} = require('./utils.js');
-
-describe('ls integration tests', () => {
-
-       /*
-        * This file should contain tests to test ls functionality.
-        */
-
-       beforeEach(async() => {
-               await reset_env();
-               await setup_env();
-               await exec('git ex add hello.txt',{cwd:"/home/node/working"});
-               await exec('git ex add hello1.txt',{cwd:"/home/node/working"});
-               await exec('mkdir test',{cwd:"/home/node/working"});
-               await exec('git ex add test/hello.txt',{cwd:"/home/node/working"});
-               await exec('mkdir -p test2/test',{cwd:"/home/node/working"});
-               await exec('git ex add test2/test/hello.txt',{cwd:"/home/node/working"});
-       });
-
-       it('should ls successfully', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex ls --long',{cwd:"/home/node/working"});
-
-                       let expected = '100644 blob ce013625030ba8dba906f756967f9e9ca394464a\thello.txt\n';
-                       expected += '100644 blob ce013625030ba8dba906f756967f9e9ca394464a\thello1.txt\n';
-                       expected += '100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest/hello.txt\n';
-                       expected += '100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest2/test/hello.txt\n';
-
-                       assert.deepStrictEqual(res,{stdout:expected,stderr:''});
-               });
-       });
-
-       it('should ls (only names) successfully', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex ls',{cwd:"/home/node/working"});
-
-                       let expected = 'hello.txt\n';
-                       expected += 'hello1.txt\n';
-                       expected += 'test/hello.txt\n';
-                       expected += 'test2/test/hello.txt\n';
-                       assert.deepStrictEqual(res,{stdout:expected,stderr:''});
-               });
-       });
-
-       it('should ls inside a sub-directory successfully', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex ls --long',{cwd:"/home/node/working/test"});
-
-                       const expected = '100644 blob ce013625030ba8dba906f756967f9e9ca394464a\thello.txt\n';
-                       assert.deepStrictEqual(res,{stdout:expected,stderr:''});
-               });
-       });
-
-       it('should ls properly when not given any arguments', async() => {
-               await assert.doesNotReject(async() => {
-                       const res = await exec('git ex',{cwd:"/home/node/working"});
-
-                       let expected = 'hello.txt\n';
-                       expected += 'hello1.txt\n';
-                       expected += 'test/hello.txt\n';
-                       expected += 'test2/test/hello.txt\n';
-                       assert.deepStrictEqual(res,{stdout:expected,stderr:''});
-               });
-       });
-
-       after(async() => {
-               await reset_env();
-       });
-
-});
\ No newline at end of file
diff --git a/test/integration/test/utils.js b/test/integration/test/utils.js
deleted file mode 100644 (file)
index b4ae39c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-const assert = require('assert');
-const util = require('util');
-
-const exec = util.promisify(require('child_process').exec);
-
-const workdir = "working";
-
-const reset_env = async() => {
-       await assert.doesNotReject(async() => await exec(`rm -rf ${workdir}`,{cwd:"/home/node/"}));
-};
-
-const setup_env = async() => {
-       await assert.doesNotReject(async() => await exec(`git init ${workdir}`,{cwd:"/home/node/"}));
-       await assert.doesNotReject(async() => await exec('git config --local user.email "[email protected]"',{cwd:"/home/node/working"}));
-       await assert.doesNotReject(async() => await exec('git config --local user.name "test"',{cwd:"/home/node/working"}));
-       await assert.doesNotReject(async() => await exec(`git config --local core.editor "sh -c 'echo \\"hello\\" >> \\"\\$0\\"'"`,{cwd:"/home/node/working"}));
-       await assert.doesNotReject(async() => await exec(`echo "test" > test.txt`,{cwd:"/home/node/working"}));
-       await assert.doesNotReject(async() => await exec(`git add -A`,{cwd:"/home/node/working"}));
-       await assert.doesNotReject(async() => await exec(`git commit -a -m "..."`,{cwd:"/home/node/working"}));
-};
-
-module.exports = {
-       reset_env,
-       setup_env
-};
\ No newline at end of file
diff --git a/test/ls.tests.sh b/test/ls.tests.sh
new file mode 100755 (executable)
index 0000000..7c58027
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+BRANCH="master"
+LOG_LEVEL=info
+
+ls > result 2>&1
+EXPECTED=$'hi/test/nope/what/test3.txt\ntest.txt\ntest2.txt'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+LONG_OUTPUT=true
+ls > result 2>&1
+EXPECTED=$'100644 blob c8ced6bb17c2965c058f5cdb5677cb8cd9091f9c\thi/test/nope/what/test3.txt\n100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest.txt\n100644 blob a8453014c7d0581a0d904792f009f1ad7ff477a1\ttest2.txt'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+ls test.txt > result 2>&1
+EXPECTED=$'100644 blob ce013625030ba8dba906f756967f9e9ca394464a\ttest.txt'
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+test_succeeded
diff --git a/test/parse_arguments.tests.sh b/test/parse_arguments.tests.sh
new file mode 100755 (executable)
index 0000000..275ca1b
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+set +e
+(parse_arguments "--notarealoption" > result 2>&1)
+if [[ $? -ne 1 ]]; then
+       test_failed
+fi
+
+EXPECTED=$'./parse_arguments.tests.sh: illegal option -- -\n'
+EXPECTED+="$USAGE"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+(parse_arguments --branch > result 2>&1)
+if [[ $? -ne 1 ]]; then
+       test_failed
+fi
+
+EXPECTED=$'./parse_arguments.tests.sh: option requires an argument -- b\n'
+EXPECTED+="$USAGE"
+RESULT=$(<result)
+assert "$EXPECTED" "$RESULT"
+
+set -e
+
+parse_arguments --editor test > result 2>&1
+assert "test" "$EDITOR"
+
+parse_arguments --long > result 2>&1
+assert "true" "$LONG_OUTPUT"
+
+parse_arguments --branch test --editor NOPE --template what huh? arg1 arg2 > result 2>&1
+assert "test" "$BRANCH"
+assert "NOPE" "$EDITOR"
+assert "what" "$TEMPLATE"
+assert "huh?" "$UTILITY"
+assert "arg1 arg2" "$POSITIONAL_ARGUMENTS"
+
+test_succeeded
diff --git a/test/setup.sh b/test/setup.sh
new file mode 100755 (executable)
index 0000000..1b41925
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+TEST_DIR=".testdir"
+
+source source.sh
+
+set +e
+(usage > temp)
+USAGE=$(<temp)
+rm -f temp
+set -e
+
+clean_env() {
+       cd ..
+       rm -rf "$TEST_DIR"
+}
+
+setup_env() {
+       mkdir "$TEST_DIR"
+       cd "$TEST_DIR"
+       git init
+       echo "hello" > test.txt
+       echo "laskdjfalskdfj" > test2.txt
+       mkdir -p "hi/test/nope/what"
+       echo "alskdjflaksdjflkasjdflkasjdklasjdlkfjd" > "hi/test/nope/what/test3.txt"
+       git config user.email "[email protected]"
+       git config user.name "git-extra-test"
+       git add test.txt
+       git add test2.txt
+       git add hi/test/nope/what/test3.txt
+       git commit -m "initial commit"
+} > /dev/null 2>&1
+
+reset_env() {
+       clean_env
+       setup_env
+}
+
+assert() {
+       if [[ "$1" != "$2" ]]; then
+               echo -e "Expected:\n$1\nResult:\n$2"
+               echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]} failed"
+               exit 1
+       fi
+}
+
+test_failed() {
+       echo "${BASH_SOURCE[1]}:${BASH_LINENO[0]} failed"
+       exit 1
+}
+
+test_succeeded() {
+       echo "${BASH_SOURCE[1]} succeeded"
+       clean_env
+}
diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am
deleted file mode 100644 (file)
index 6fdee03..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-TEMPLATE_DIR=$(builddir)/
-
-AM_CPPFLAGS = \
-       -I$(top_builddir)/include/ \
-       -I$(top_builddir)/test/unit/ \
-       -I$(top_srcdir)/include/ \
-       -I$(top_srcdir)/test/unit/ \
-       -DTEMPLATE_DIR='"$(TEMPLATE_DIR)"'
-
-EXTRA_DIST = \
-       add.tests.h \
-       branch.tests.h \
-       cat.tests.h \
-       commit.tests.h \
-       default.tests.h \
-       editor.tests.h \
-       edit.tests.h \
-       hash.tests.h \
-       templ.tests.h \
-       test_utils.h
-
-check_PROGRAMS = add.tests branch.tests cat.tests commit.tests default.tests edit.tests editor.tests hash.tests templ.tests
-TESTS = $(check_PROGRAMS)
-
-if ENABLE_MEMCHECK
-LOG_COMPILER = $(VALGRIND)
-AM_LOG_FLAGS = --leak-check=full -v
-endif
-
-common_SOURCES = test_utils.c
-
-TEST_SRC_DIR = $(top_srcdir)/src
-
-common_SOURCES += \
-       $(TEST_SRC_DIR)/default.c \
-       $(TEST_SRC_DIR)/exec.c \
-       $(TEST_SRC_DIR)/git.c \
-       $(TEST_SRC_DIR)/log.c
-
-add_tests_SOURCES = \
-       $(common_SOURCES) \
-       add.tests.c \
-       $(TEST_SRC_DIR)/add.c \
-       $(TEST_SRC_DIR)/branch.c \
-       $(TEST_SRC_DIR)/commit.c \
-       $(TEST_SRC_DIR)/editor.c \
-       $(TEST_SRC_DIR)/hash.c \
-       $(TEST_SRC_DIR)/prepend.c \
-       $(TEST_SRC_DIR)/templ.c
-
-branch_tests_SOURCES = \
-       $(common_SOURCES) \
-       branch.tests.c \
-       $(TEST_SRC_DIR)/branch.c \
-       $(TEST_SRC_DIR)/prepend.c
-
-cat_tests_SOURCES = \
-       $(common_SOURCES) \
-       cat.tests.c \
-       $(TEST_SRC_DIR)/cat.c
-
-commit_tests_SOURCES = \
-       $(common_SOURCES) \
-       commit.tests.c \
-       $(TEST_SRC_DIR)/branch.c \
-       $(TEST_SRC_DIR)/commit.c \
-       $(TEST_SRC_DIR)/prepend.c
-
-default_tests_SOURCES = \
-       $(common_SOURCES) \
-       default.tests.c
-
-edit_tests_SOURCES = \
-       $(common_SOURCES) \
-       edit.tests.c \
-       $(TEST_SRC_DIR)/add.c \
-       $(TEST_SRC_DIR)/branch.c \
-       $(TEST_SRC_DIR)/cat.c \
-       $(TEST_SRC_DIR)/commit.c \
-       $(TEST_SRC_DIR)/edit.c \
-       $(TEST_SRC_DIR)/editor.c \
-       $(TEST_SRC_DIR)/hash.c \
-       $(TEST_SRC_DIR)/prepend.c \
-       $(TEST_SRC_DIR)/templ.c
-
-editor_tests_SOURCES = \
-       $(common_SOURCES) \
-       editor.tests.c \
-       $(TEST_SRC_DIR)/editor.c \
-       $(TEST_SRC_DIR)/hash.c
-
-hash_tests_SOURCES = \
-       $(common_SOURCES) \
-       hash.tests.c \
-       $(TEST_SRC_DIR)/hash.c
-
-templ_tests_SOURCES = \
-       $(common_SOURCES) \
-       templ.tests.c \
-       $(TEST_SRC_DIR)/templ.c
\ No newline at end of file
diff --git a/test/unit/add.tests.c b/test/unit/add.tests.c
deleted file mode 100644 (file)
index 09d0402..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#include<add.tests.h>
-
-int main() {
-       setup_env();
-
-       add_basic_test();
-
-       clean_env();
-}
-
-void add_basic_test() {
-       defaults();
-       
-       assert(EXIT_FAILURE==add(NULL));
-       assert(EXIT_SUCCESS==add("test.txt"));
-       assert(EXIT_FAILURE==add("test.txt"));
-       assert(EXIT_SUCCESS==add("test2.txt"));
-}
\ No newline at end of file
diff --git a/test/unit/add.tests.h b/test/unit/add.tests.h
deleted file mode 100644 (file)
index 5aa9ba0..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __ADD_TESTS_H_
-#define __ADD_TESTS_H_
-
-#include<test_utils.h>
-
-#include<add.h>
-#include<default.h>
-
-int main();
-void add_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/branch.tests.c b/test/unit/branch.tests.c
deleted file mode 100644 (file)
index 8f5d695..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#include<branch.tests.h>
-
-int main() {
-       setup_env();
-
-       branch_create_basic_test();
-       reset_env();
-
-       branch_exists_basic_test();
-       reset_env();
-
-       branch_basic_test();
-       branch_reset_basic_test();
-
-       clean_env();
-}
-
-void branch_basic_test() {
-       global_opts.branch = "extra";
-
-       char got[HASH_BUF_LENGTH];
-
-       assert(1==branch(got,HASH_BUF_LENGTH));
-}
-
-void branch_create_basic_test() {
-       global_opts.branch = NULL;
-       assert(branch_create()<0);
-
-       global_opts.branch = "test";
-       assert(branch_create()==1);
-}
-
-void branch_exists_basic_test() {
-       global_opts.branch = "master";
-       assert(branch_exists()==1);
-
-       global_opts.branch = "notarealbranch";
-       assert(branch_exists()==0);
-}
-
-void branch_reset_basic_test() {
-       assert(branch_reset()==1);
-}
\ No newline at end of file
diff --git a/test/unit/branch.tests.h b/test/unit/branch.tests.h
deleted file mode 100644 (file)
index 04d3c14..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef __BRANCH_TESTS_H_
-#define __BRANCH_TESTS_H_
-
-#include<test_utils.h>
-
-#include<branch.h>
-
-int main();
-void branch_basic_test();
-void branch_create_basic_test();
-void branch_exists_basic_test();
-void branch_reset_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/cat.tests.c b/test/unit/cat.tests.c
deleted file mode 100644 (file)
index 9bd542c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#include<cat.tests.h>
-
-int main() {
-       setup_env();
-
-       cat_basic_test();
-
-       clean_env();
-}
-
-void cat_basic_test() {
-       assert(EXIT_FAILURE==cat(NULL));
-
-       global_opts.branch = "extra";
-       assert(EXIT_FAILURE==cat("test.txt"));
-
-       global_opts.branch = "master";
-       assert(EXIT_SUCCESS==cat("test.txt"));
-}
\ No newline at end of file
diff --git a/test/unit/cat.tests.h b/test/unit/cat.tests.h
deleted file mode 100644 (file)
index d1dbb20..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __CAT_TESTS_H_
-#define __CAT_TESTS_H_
-
-#include<test_utils.h>
-
-#include<cat.h>
-
-int main();
-void cat_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/commit.tests.c b/test/unit/commit.tests.c
deleted file mode 100644 (file)
index a6e15c1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#include<commit.tests.h>
-
-int main() {
-       setup_env();
-
-       commit_basic_test();
-
-       clean_env();
-}
-
-void commit_basic_test() {
-       assert(0==system("echo 'test2' > test2.txt"));
-
-       global_opts.branch = "extra";
-
-       char file_hash[HASH_BUF_LENGTH];
-       memset(file_hash,0,HASH_BUF_LENGTH);
-
-       char **argv;
-       int i;
-       
-       assert(1==git_hash_object("test2.txt",HASH_OBJECT_ADD_TO_INDEX,file_hash,HASH_BUF_LENGTH));
-       
-       char c = file_hash[0];
-       file_hash[0]++;
-       assert(commit(file_hash,HASH_BUF_LENGTH,"hahahah")<0);
-
-       file_hash[0] = c;
-       assert(1==commit(file_hash,HASH_BUF_LENGTH,"hahahah"));
-}
\ No newline at end of file
diff --git a/test/unit/commit.tests.h b/test/unit/commit.tests.h
deleted file mode 100644 (file)
index 2f683cf..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __COMMIT_TESTS_H_
-#define __COMMIT_TESTS_H_
-
-#include<test_utils.h>
-
-#include<commit.h>
-
-int main();
-void commit_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/default.tests.c b/test/unit/default.tests.c
deleted file mode 100644 (file)
index 93e326c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#include<default.tests.h>
-
-int main() {
-       setup_env();
-
-       default_basic_test();
-       default_editor_basic_test();
-
-       clean_env();
-}
-
-void default_basic_test() {
-       defaults();
-
-       assert(0==strcmp(global_opts.branch,"extra"));
-       assert(0==strcmp(global_opts.editor,"sh -c 'echo \"hello\" >> \"$0\"'"));
-       assert(NULL==global_opts.path_prefix);
-}
-
-void default_editor_basic_test() {
-       assert(0==setenv("EDITOR","hahahahah",1));
-
-       assert(default_editor()==1);
-
-       assert(0==strcmp(global_opts.editor,"hahahahah"));
-}
\ No newline at end of file
diff --git a/test/unit/default.tests.h b/test/unit/default.tests.h
deleted file mode 100644 (file)
index ea22576..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef __DEFAULT_TESTS_H_
-#define __DEFAULT_TESTS_H_
-
-#include<stdlib.h>
-
-#include<test_utils.h>
-
-#include<default.h>
-
-int main();
-void default_basic_test();
-void default_editor_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/edit.tests.c b/test/unit/edit.tests.c
deleted file mode 100644 (file)
index 186266c..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include<edit.tests.h>
-
-int main() {
-       setup_env();
-
-       edit_basic_test();
-
-       clean_env();
-}
-
-void edit_basic_test() {
-       defaults();
-
-       assert(EXIT_FAILURE==edit(NULL));
-       assert(EXIT_FAILURE==edit("test.txt"));
-
-       assert(EXIT_SUCCESS==add("test.txt"));
-       assert(EXIT_SUCCESS==edit("test.txt"));
-
-       assert(EXIT_FAILURE==edit("test2.txt"));
-}
\ No newline at end of file
diff --git a/test/unit/edit.tests.h b/test/unit/edit.tests.h
deleted file mode 100644 (file)
index 2328a80..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __EDIT_TESTS_H_
-#define __EDIT_TESTS_H_
-
-#include<test_utils.h>
-
-#include<default.h>
-#include<edit.h>
-
-int main();
-void edit_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/editor.tests.c b/test/unit/editor.tests.c
deleted file mode 100644 (file)
index 936a125..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#include<editor.tests.h>
-
-int main() {
-       setup_env();
-
-       editor_basic_test();
-
-       clean_env();
-}
-
-void editor_basic_test() {
-       defaults();
-
-       assert(editor(NULL)<0);
-       assert(editor("filewhichdoesntexist.txt")<0);
-       
-       assert(editor("test.txt")>0);
-
-       global_opts.editor = "sh -c 'echo \"$0\"'";
-       assert(editor("test.txt")<0);
-}
\ No newline at end of file
diff --git a/test/unit/editor.tests.h b/test/unit/editor.tests.h
deleted file mode 100644 (file)
index c979df9..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __EDITOR_TESTS_H_
-#define __EDITOR_TESTS_H_
-
-#include<test_utils.h>
-
-#include<default.h>
-#include<editor.h>
-
-int main();
-void editor_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/hash.tests.c b/test/unit/hash.tests.c
deleted file mode 100644 (file)
index 788e45a..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#include<hash.tests.h>
-
-int main() {
-       setup_env();
-
-       hash_basic_test();
-
-       clean_env();
-}
-
-void hash_basic_test() {
-       char expected[HASH_BUF_LENGTH] = "9daeafb9864cf43055ae93beb0afd6c7d144bfa4";
-       char got[HASH_BUF_LENGTH];
-
-       memset(got,0,HASH_BUF_LENGTH);
-
-       assert(hash("test.txt",got,HASH_BUF_LENGTH)==1);
-
-       assert(memcmp(expected,got,HASH_BUF_LENGTH)==0);
-}
\ No newline at end of file
diff --git a/test/unit/hash.tests.h b/test/unit/hash.tests.h
deleted file mode 100644 (file)
index 710d281..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __HASH_TESTS_H_
-#define __HASH_TESTS_H_
-
-#include<test_utils.h>
-
-#include<hash.h>
-
-int main();
-void hash_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/templ.tests.c b/test/unit/templ.tests.c
deleted file mode 100644 (file)
index cc69a74..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#include<templ.tests.h>
-
-int main() {
-       setup_env();
-
-       template_basic_test();
-
-       clean_env();
-}
-
-void template_basic_test() {
-       char filename[] = "/tmp/XXXXXX";
-       int fd = mkstemp(filename);
-
-       assert(fd>=0);
-
-       global_opts.template = NULL;
-       assert(template(fd)==0);
-
-       global_opts.template = "notavalidtemplatename";
-       assert(template(fd)==0);
-
-       global_opts.template = "test.template";
-       assert(template(fd)==1);
-}
\ No newline at end of file
diff --git a/test/unit/templ.tests.h b/test/unit/templ.tests.h
deleted file mode 100644 (file)
index c502065..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef __TEMPL_TESTS_H_
-#define __TEMPL_TESTS_H_
-
-#include<test_utils.h>
-
-#include<templ.h>
-
-int main();
-void template_basic_test();
-
-#endif
\ No newline at end of file
diff --git a/test/unit/test_utils.c b/test/unit/test_utils.c
deleted file mode 100644 (file)
index 1cf07a8..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#include<test_utils.h>
-
-void clean_env() {
-       assert(chdir("..")==0);
-       assert(0==system("rm -rf working"));
-}
-
-void reset_env() {
-       clean_env();
-       setup_env();
-}
-
-void setup_env() {
-       // remove old test directory (if previous test failed)
-       assert(0==system("rm -rf working"));
-       
-       assert(0==system("git init working"));
-       
-       assert(chdir("working")==0);
-       
-       assert(0==system("git config --local user.email \"[email protected]\""));
-       assert(0==system("git config --local user.name \"[email protected]\""));
-       assert(0==system("git config --local core.editor \"sh -c 'echo \\\"hello\\\" >> \\\"\\$0\\\"'\""));
-       
-       assert(0==system("echo \"template\n\n\nhahaha\" > test.template"));
-       assert(0==system("echo \"test\" > test.txt"));
-       assert(0==system("git add -A"));
-       assert(0==system("git commit -a -m \"...\""));
-}
\ No newline at end of file
diff --git a/test/unit/test_utils.h b/test/unit/test_utils.h
deleted file mode 100644 (file)
index 5011cc8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __TEST_UTILS_H_
-#define __TEST_UTILS_H_
-
-#include<assert.h>
-#include<stdlib.h>
-#include<unistd.h>
-
-void clean_env();
-void setup_env();
-void reset_env();
-
-#endif
\ No newline at end of file
diff --git a/test/usage.tests.sh b/test/usage.tests.sh
new file mode 100755 (executable)
index 0000000..6834ec1
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# this command will fail if not executed in test/ directory
+source setup.sh
+
+setup_env
+
+set +e
+(usage > result)
+if [[ $? -ne 1 ]]; then
+       test_failed
+fi
+set -e
+
+EXPECTED=$'Usage:\n\n\tgit extra [options] [add|cat|edit|ls] [path]\n\nOptions:\n\t--branch, -b <branch>\n\t--editor, -e <editor>\n\t--long, -l\n\t--message, -m <message>\n\t--quiet, -q\n\t--template, -t <template>\n\t--verbose, -v'
+RESULT=$(<result)
+
+if [[ "$EXPECTED" != "$RESULT" ]]; then
+       test_failed
+fi
+
+test_succeeded