set -euf -o pipefail
print_version() {
printf "\
nicotine $(httm --version | cut -f2 -d' ')
" 1>&2
exit 0
}
print_usage() {
local nicotine="\e[31mnicotine\e[0m"
local httm="\e[31mhttm\e[0m"
local git="\e[31mgit\e[0m"
local tar="\e[31mtar\e[0m"
printf "\
$nicotine is a wrapper script for $httm which converts unique snapshot file versions to a $git archive.
USAGE:
nicotine [OPTIONS]... [file1 file2...]
OPTIONS:
--output-dir:
Select the output directory. Default is the current working directory.
--no-archive
Disable archive creation. Create a new $git repository (e.g. named \"\$file1-git\")
in the output directory.
--debug:
Show $git and $tar command output. Default is to complete silence both.
--help:
Display this dialog.
--version:
Display script version.
" 1>&2
exit 1
}
print_err_exit() {
print_err "${@}"
exit 1
}
print_err() {
printf "%s\n" "Error: $*" 1>&2
}
prep_exec() {
[[ -n "$(
command -v find
exit 0
)" ]] || print_err_exit "'find' is required to execute 'nicotine'. Please check that 'find' is in your path."
[[ -n "$(
command -v readlink
exit 0
)" ]] || print_err_exit "'readlink' is required to execute 'nicotine'. Please check that 'readlink' is in your path."
[[ -n "$(
command -v git
exit 0
)" ]] || print_err_exit "'git' is required to execute 'nicotine'. Please check that 'git' is in your path."
[[ -n "$(
command -v tar
exit 0
)" ]] || print_err_exit "'tar' is required to execute 'nicotine'. Please check that 'targit' is in your path."
[[ -n "$(
command -v mktemp
exit 0
)" ]] || print_err_exit "'mktemp' is required to execute 'nicotine'. Please check that 'mktemp' is in your path."
[[ -n "$(
command -v mkdir
exit 0
)" ]] || print_err_exit "'mkdir' is required to execute 'nicotine'. Please check that 'mkdir' is in your path."
[[ -n "$(
command -v httm
exit 0
)" ]] || print_err_exit "'httm' is required to execute 'nicotine'. Please check that 'httm' is in your path."
}
function copy_add_commit {
local debug=$1
shift
local path="$1"
shift
local dest_dir="$1"
shift
if [[ -d "$path" ]]; then
cp -a "$path" "$dest_dir/"
return 0
else
cp -a "$path" "$dest_dir"
fi
if [[ $debug = true ]]; then
git add --all "$dest_dir"
git commit -m "httm commit from ZFS snapshot" --date "$(date -d "$(stat -c %y $path)")" || true
else
git add --all "$dest_dir" > /dev/null
git commit -q -m "httm commit from ZFS snapshot" --date "$(date -d "$(stat -c %y $path)")" > /dev/null || true
fi
}
function get_unique_versions {
local debug=$1
shift
local path="$1"
shift
local dest_dir="$1"
shift
local -a version_list
[[ -d "$path" ]] || while read -r line; do
version_list+=("$line")
done <<<"$(httm -n --omit-ditto "$path")"
if [[ -d "$path" ]] || [[ ${#version_list[@]} -eq 0 ]] || [[ ${#version_list[@]} -eq 1 ]]; then
copy_add_commit $debug "$path" "$dest_dir"
else
for version in "${version_list[@]}"; do
copy_add_commit $debug "$version" "$dest_dir"
done
fi
}
function traverse {
local debug=$1
shift
local path="$1"
shift
local dest_dir="$1"
shift
get_unique_versions $debug "$path" "$dest_dir"
[[ -d "$path" ]] || return 0
local -a dir_entries=()
local basename="$(basename "$path")"
while read -r line; do
[[ -z "$line" ]] || dir_entries+=("$line")
done <<<"$(find "$path" -mindepth 1 -maxdepth 1)"
[[ ${#dir_entries[@]} -ne 0 ]] || return 0
for entry in "${dir_entries[@]}"; do
if [[ -d "$entry" ]]; then
traverse $debug "$entry" "$dest_dir/$basename"
else
get_unique_versions $debug "$entry" "$dest_dir/$basename"
fi
done
}
function convert_to_git {
local debug=$1
shift
local no_archive=$1
shift
local working_dir="$1"
shift
local tmp_dir="$1"
shift
local output_dir="$1"
shift
local path="$1"
shift
local archive_dir=""
local basename=""
raw="$(basename "$path")"
basename="${raw#.}"
archive_dir="$tmp_dir/$basename"
mkdir "$archive_dir" || print_err_exit "nicotine could not create a temporary directory. Check you have permissions to create."
cd "$archive_dir" || print_err_exit "nicotine could not enter a temporary directory: $archive_dir. Check you have permissions to enter."
if [[ $debug = true ]]; then
git init || print_err_exit "git could not initialize directory"
else
git init -q >/dev/null || print_err_exit "git could not initialize directory"
fi
if [[ -d "$path" ]]; then
traverse $debug "$path" "$tmp_dir"
else
traverse $debug "$path" "$archive_dir"
fi
if [[ $no_archive = true ]]; then
cp -ra "$archive_dir" "$output_dir/$basename-git"
else
cd "$tmp_dir"
local output_file="$output_dir/$basename-git.tar.gz"
if [[ $debug = true ]]; then
tar -zcvf "$output_file" "./$basename" || print_err_exit "Archive creation failed. Quitting."
else
tar -zcvf "$output_file" "./$basename" > /dev/null || print_err_exit "Archive creation failed. Quitting."
fi
fi
[[ ! -e "$tmp_dir/$basename" ]] || rm -rf "$tmp_dir/$basename"
[[ -e "$tmp_dir/$basename" ]] || rm -rf "$tmp_dir/*"
if [[ $no_archive = true ]]; then
printf "nicotine git repository created successfully: $basename-git\n"
else
printf "nicotine git archive created successfully: $output_file\n"
fi
}
function nicotine {
prep_exec
local debug=false
local no_archive=false
local working_dir
local output_dir
working_dir="$(realpath "$( pwd )" 2>/dev/null || true)"
[[ -e "$working_dir" ]] || print_err_exit "Could not obtain current working directory. Quitting."
output_dir="$working_dir"
[[ $# -ge 1 ]] || print_usage
[[ "$1" != "-h" && "$1" != "--help" ]] || print_usage
[[ "$1" != "-V" && "$1" != "--version" ]] || print_version
while [[ $# -ge 1 ]]; do
if [[ "$1" == "--output-dir" ]]; then
shift
[[ $# -ge 1 ]] || print_err_exit "output-dir argument is empty"
output_dir="$(realpath "$1" 2>/dev/null || true)"
[[ -e "$output_dir" ]] || print_err_exit "Could not obtain output directory from path given. Quitting."
shift
elif [[ "$1" == "--debug" ]]; then
shift
debug=true
elif [[ "$1" == "--no-archive" ]]; then
shift
no_archive=true
else
break
fi
done
local tmp_dir="$( mktemp -d )"
trap "[[ ! -d "$tmp_dir" ]] || rm -rf "$tmp_dir"" EXIT
[[ -e "$tmp_dir" ]] || print_err_exit "Could not create a temporary directory for scratch work. Quitting."
[[ $# -ne 0 ]] || print_err_exit "User must specify at least one input file. Quitting."
for a; do
canonical_path="$(
realpath "$a" 2>/dev/null
[[ $? -eq 0 ]] || print_err "Could not determine canonical path for: $a"
)"
[[ -n "$canonical_path" ]] || continue
if [[ ! -e "$canonical_path" ]]; then
printf "$canonical_path does not exist. Skipping.\n"
continue
fi
if [[ -d "$canonical_path" ]] && [[ -z "$(find "$canonical_path" -mindepth 1 -maxdepth 1)" ]]; then
printf "$canonical_path is an empty directory. Skipping.\n"
continue
fi
if [[ ! -f "$canonical_path" && ! -d "$canonical_path" && ! -L "$canonical_path" ]]; then
printf "$canonical_path is not a file, directory, or symlink. Skipping.\n"
continue
fi
convert_to_git $debug $no_archive "$working_dir" "$tmp_dir" "$output_dir" "$canonical_path"
done
}
nicotine "${@}"