diff options
| author | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-13 18:00:17 +1100 |
|---|---|---|
| committer | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-13 18:00:17 +1100 |
| commit | 98cef5e9a772602d42acfcf233838c760424db9a (patch) | |
| tree | 5277fa1d7cc0a69a0f166fcbf10fd320f345f049 /comp2041 | |
initial commit
Diffstat (limited to 'comp2041')
| -rwxr-xr-x | comp2041/slippy/slippy.py | 385 | ||||
| -rwxr-xr-x | comp2041/tigger/test00.sh | 58 | ||||
| -rwxr-xr-x | comp2041/tigger/test01.sh | 53 | ||||
| -rwxr-xr-x | comp2041/tigger/test02.sh | 73 | ||||
| -rwxr-xr-x | comp2041/tigger/test03.sh | 56 | ||||
| -rwxr-xr-x | comp2041/tigger/test04.sh | 45 | ||||
| -rwxr-xr-x | comp2041/tigger/test05.sh | 46 | ||||
| -rwxr-xr-x | comp2041/tigger/test06.sh | 62 | ||||
| -rwxr-xr-x | comp2041/tigger/test07.sh | 51 | ||||
| -rwxr-xr-x | comp2041/tigger/test08.sh | 70 | ||||
| -rwxr-xr-x | comp2041/tigger/test09.sh | 61 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-add | 42 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-branch | 68 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-checkout | 84 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-commit | 88 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-init | 39 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-log | 24 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-rm | 66 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-show | 63 | ||||
| -rwxr-xr-x | comp2041/tigger/tigger-status | 100 |
20 files changed, 1534 insertions, 0 deletions
diff --git a/comp2041/slippy/slippy.py b/comp2041/slippy/slippy.py new file mode 100755 index 0000000..8cd02ba --- /dev/null +++ b/comp2041/slippy/slippy.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 + +import re, sys, os, getopt, fileinput + +def get_error_strs(): + ''' + Returns a dictionary of error strings we throw in multiple locations. + ''' + error_strs = { + "usage": "usage: slippy [-i] [-n] [-f <script-file> | <sed-command>] "\ + "[<files>...]", + "invalid_command": "slippy: command line: invalid command", + } + return error_strs + + +def find_regex(regex, str): + ''' + Returns a pair describing matched regex - None if not found. + The first element is a string describing the entire match captured. + The second element is the list of captured groups. + ''' + match = re.search(regex, str) + if match == None: + return None + + ret = [] + if match != None: + for group in match.groups(): + if group == None: + continue + ret.append(group) + return (match.group(0), ret) + + +# We use getopt instead of argparse because we need to match the spec's errors. +def get_args(): + ''' + Returns a tuple where the first element is a dictionary of parsed args and + the second argument is a dictionary of remaining args (sed regex + files). + The key in both dictionaries is the position of the argument. + This function will throw if the arguments are invalid. + Eg: ({0: ("-p", ""), 1: ("-q", "3"}, {0: ["s/asdf/asdf/g"]}) + ''' + short = "inf:" + long = [] + try: + (parsed, unparsed) = getopt.getopt(sys.argv[1:], short, long) + except: + raise RuntimeError(get_error_strs()["usage"]) + return (dict(enumerate(parsed)), dict(enumerate(unparsed))) + + +def get_command_address(command, delimit): + ''' + Returns a pair (<string>, list<string>) of addresses for a command. + The first element in the pair is the entire address. + The second element in the pair is a list of addresses. + If the address is a line number, it will NOT be contained by the delimiter. + If the address is regex, it will be surrounded by the delimiter. + If the list has more than one element, it is a range. + 3p -> ("3", ["3"]) + /.1/q -> ("/.1/", ["/.1/"]) + /4/,/6/s/[12]/9/ -> ("/4/,/6/", ["/4/", "/6/"]) NOTE the lack of "," + ''' + + d = delimit if delimit != "/" else r"\/" # regex needs \/ instead of / + + regex = r"^(\/[^\/]*\/|[0-9$]),(\/[^\/]+\/|[0-9$]+)|^(\/[^\/]+\/)|(^[0-9$]+),([0-9$]+)|(^[0-9$]+)" + find = find_regex(regex, command) + return (("", []) if find == None else find) + + +def get_command_arguments(command, delimit): + ''' + Returns the contents of a the command split by "/". + /megamaid -> ["megamaid"] + /asdf/qwer/g -> ["asdf", "qwer", "g"] + ''' + return command.split(delimit)[1:] + + +def parse_command(command): + ''' + Transforms a command represented as a string into a dictionary describing + the command. + Examples: + "1d" -> + "addresses" : ["1"], // A string list of addresses + "type" : "d", // Type as a string + "arguments" : [] // List of args + "enabled" : False // Stateful range indicator + + "s/.1/qwer/g" -> + "addresses" : [], + "type" : "s", + "arguments" : [".1", "qwer", "g"] + "enabled" : False + + "/4/,/6/s/[12]/9/" -> + "addresses" : ["/4/", "/6/"], + "type" : "s", + "arguments" : ["[12]", "9"] + "enabled" : False + ''' + + delimit = command[1] if len(command) >= 2 and command[0] == "s" else "/" + + (address_prefix, addresses) = get_command_address(command, delimit) + command = command.removeprefix(address_prefix) + + type = command[0] + command = command.removeprefix(type) + + arguments = get_command_arguments(command, delimit) + + return {"addresses": addresses, \ + "type": type, \ + "arguments": arguments, \ + "enabled": False} + + +def unroll_commands(commands): + ''' + Transforms a list of commands into a list of the results of parse_commands. + Splits commands into multiple if necessary. Removes comments. + ''' + ret = [] + for unrolled in commands: + for command in unrolled.split(";"): + + command = command.replace(" ", "") # remove whitespace + + if len(command) <= 0: + continue + + command = command.split("#", 1)[0] # remove comments + + if len(command) <= 0: + continue + + ret.append(command) + + return list(map(parse_command, ret)) + + + +def get_state(): + ''' + Returns a dictionary containing elements describing the commands passed to + the program. + The dictionary will contain the following key-values (with examples): + "commands" : [parse_command("s/qwer/asdf/g"), parse_command("etc")], + "args": ["-p", "-q"], + "files": ["readme.txt", "example.py"], + + ''' + (parsed_args, unparsed_args) = get_args() + + commands = [] + args = [] + files = [] + + for key in parsed_args: + arg = parsed_args[key][0] + if arg == "-f": + filename = parsed_args[key][1] + with open(filename) as f: + commands = f.read().splitlines() + args.append(arg) + for key in sorted(unparsed_args.keys()): + arg = unparsed_args[key] + if not "-f" in args and key == 0: + commands += [arg] + continue + files += [arg] + + # No command parsed is invalid syntax. + if len(commands) <= 0: + raise RuntimeError(get_error_strs()["usage"]) + + return { + "commands": unroll_commands(commands), + "args": args, + "files": files, + } + + +def is_regex_match(regex, count, current_line, next_line): + ''' + Returns true if the regex exists in the string, value otherwise. + ''' + return find_regex(regex, current_line) != None + + +def is_line_match(target, count, current_line, next_line, lequal=False): + ''' + Returns true if the target is equal to count, false otherwise. + ''' + if target == "$": + return len(next_line) == 0 + + conv = int(target) + if lequal: + return conv > count + return conv == count + + +def is_address_regex(address): + return len(address) >= 1 and address[0] == "/" and address[-1] == "/" + + +def is_address_match(address, count, current_line, next_line): + ''' + Deduces whether the address is a line count or regex address and returns + its deduced value. + ''' + if is_address_regex(address): + return is_regex_match(address[1:-1], count, current_line, next_line) + return is_line_match(address, count, current_line, next_line) + + +def is_address_within(address, count, current_line, next_line): + if is_address_regex(address): + return not is_regex_match(address[1:-1], count, current_line, next_line) + return is_line_match(address, count, current_line, next_line, True) + + +def is_command_within(command, count, current_line, next_line): + addresses = command["addresses"] + + start = addresses[0] + end = addresses[1] + + # If we're not in the range... + if not command["enabled"]: + if not is_address_match(start, count, current_line, next_line): + return False + + command["enabled"] = True + if not is_address_regex(end) and not is_address_within(end, count, current_line, next_line): + command["enabled"] = False + return True + + # We're in the range, check if we should be out. + if not is_address_within(end, count, current_line, next_line): + command["enabled"] = False + return True + + +def handle_q(command, count, current_line, next_line, args): + if not "-n" in args: + print(current_line, end="") + sys.exit(1) + return current_line # superfluous + + +def handle_p(command, count, current_line, next_line, args): + print(current_line, end="") + return current_line + + +def handle_d(command, count, current_line, next_line, args): + return "" + + +def handle_s(command, count, current_line, next_line, args): + command_arguments = command["arguments"] + pattern = command_arguments[0] + replace = command_arguments[1] + + sub = (re.sub(pattern, replace, current_line) \ + if len(command_arguments) >= 3 and command_arguments[2] == "g" \ + else re.sub(pattern, replace, current_line, 1)) + return sub + + +def get_command_funcs(): + ''' + Returns a dictionary of function pointers via each command type. + ''' + command_funcs = { + "q": handle_q, + "p": handle_p, + "d": handle_d, + "s": handle_s + } + return command_funcs + + +def should_run_command(command, count, current_line, next_line): + addresses = command["addresses"] + length = len(addresses) + if length == 0: + return True + if length == 1: + return is_address_match(addresses[0], count, current_line, next_line) + if length == 2: + val = is_command_within(command, count, current_line, next_line) + return val + raise RuntimeError(get_error_strs()["usage"]) + + +def run_command(command, count, current_line, next_line, args): + ''' + Runs the command, modifying current_line if required. + ''' + + # Check if the command should be run before executing. + if not should_run_command(command, count, current_line, next_line): + return current_line + + command_func = get_command_funcs()[command["type"]] + ret = command_func(command, count, current_line, next_line, args) + + return ret + + +def make_file_lines(files): + ''' + Turns a list of filenames into a list of lines, in order as they appear. + ''' + ret = [] + for filename in files: + with open(filename) as f: + ret += f.read().splitlines(True) + return ret + + +def get_new_line(file_lines, files): + ''' + Gets a new line from stdin, or from the . + Returns a zero length string if out of data. This is distinct from an empty + line, which will be "\n". + ''' + + if len(files) <= 0: + return sys.stdin.readline() + + if len(file_lines) <= 0: + return "" + + line = file_lines[0] + file_lines.pop(0) + return line + + +def loop(state): + ''' + Main loop, handles running of commands. + ''' + args = state["args"] + commands = state["commands"] + files = state["files"] + file_lines = make_file_lines(files) + + count = 1 + current_line = get_new_line(file_lines, files) + while current_line: + next_line = get_new_line(file_lines, files) + + for command in commands: + current_line = run_command(command, count, current_line, next_line,\ + args) + if len(current_line) == 0: + break + + if not "-n" in args: + print(current_line, end="") + + count += 1 + current_line = next_line + + +# We use a try block to handle error messages with the appropriate exit code. +try: + state = get_state() + #print(state) + loop(state) +except SystemExit: + pass +except BaseException as error: + print(error, file=sys.stderr) + sys.exit(1) +sys.exit(0) diff --git a/comp2041/tigger/test00.sh b/comp2041/tigger/test00.sh new file mode 100755 index 0000000..b76f4a3 --- /dev/null +++ b/comp2041/tigger/test00.sh @@ -0,0 +1,58 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that removing the expected .tigger +# folder does reset the state of tigger. In addition, there should be NO extra +# files at the end of this operation, even if it fails. We are only allowed to +# edit/create .tigger and what is inside .tigger! + +num_files_before=$(find ./ | wc -l) +assert_good_exec "./tigger-init" +num_files_after=$(find ./ | wc -l) +assert_bad_exec "./tigger-init" # should make no more files +if [ "$(find ./ | wc -l)" -ne "$num_files_after" ]; then + printf "the amount of files should be equal to before when tigger-init was called previously!\n" >&2 + exit 1 +fi +rm -rf ./.tigger +if [ "$(find ./ | wc -l)" -ne "$num_files_before" ]; then + printf "there should be the same amount as files as before any commands were run!\n" >&2 + exit 1 +fi +assert_good_exec "./tigger-init" + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test01.sh b/comp2041/tigger/test01.sh new file mode 100755 index 0000000..246ed2a --- /dev/null +++ b/comp2041/tigger/test01.sh @@ -0,0 +1,53 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that all commands fail if a valid +# repository is not present (naturally aside from tigger-init). + +#assert_bad_exec "./tigger-init" +assert_bad_exec "./tigger-add" +assert_bad_exec "./tigger-commit" +assert_bad_exec "./tigger-log" +assert_bad_exec "./tigger-show" +assert_bad_exec "./tigger-commit" +assert_bad_exec "./tigger-rm" +assert_bad_exec "./tigger-status" +assert_bad_exec "./tigger-branch" +assert_bad_exec "./tigger-checkout" +assert_bad_exec "./tigger-merge" + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test02.sh b/comp2041/tigger/test02.sh new file mode 100755 index 0000000..461da82 --- /dev/null +++ b/comp2041/tigger/test02.sh @@ -0,0 +1,73 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that there are no arbitrary limits on +# the amount of times a command may be performed, and that file +# creation/deletion and increment logic is handled correctly between commits. +# It's possible that this fails due to bad increment handling at the radix +# point (if a program is implemented incorrectly). This test might also expose +# complexity issues if they are present. + +# Specifically we might use sort as opposed to sort -n, which works until 10. +# ... i did :( + +assert_good_exec "./tigger-init" +printf "adding and committing large number of files...\n" +for _ in $(seq 25); do + echo "repeated\n" >> repeated + assert_good_exec "./tigger-add repeated" > /dev/null + assert_good_exec "./tigger-commit -m placeholder" > /dev/null +done + +# Commit count should be 50 here! +printf "testing commit count string result\n" +printf "repeated\n" >> repeated +assert_good_exec "./tigger-add repeated" > /dev/null +if [ "$(./tigger-commit -m placeholder)" != "Committed as commit 25" ]; then + printf "bad commit count string\n" >&2 + exit 0 +fi + +# New one added so 26 now. +printf "testing tigger-log line count\n" +assert_good_exec "./tigger-log" > /dev/null +if [ "$(./tigger-log | wc --line)" -ne 26 ]; then + printf "bad tigger log string length\n" >&2 + exit 0 +fi + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test03.sh b/comp2041/tigger/test03.sh new file mode 100755 index 0000000..6ce8559 --- /dev/null +++ b/comp2041/tigger/test03.sh @@ -0,0 +1,56 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to test whether tigger-add handles a large number +# of filenames. Also tests tigger-status against a new, uncommitted repo. + +assert_good_exec "./tigger-init" +filenames=$(seq 20) +printf "adding large number of files...\n" +for filename in $filenames; do + touch "$filename" > /dev/null + assert_good_exec "./tigger-add $filename" > /dev/null +done + +# Test the length of tigger-status. +printf "testing tigger-status line count\n" +if [ "$(./tigger-status | wc --line)" -ge 100 ]; then + printf "bad tigger status line count\n" >&2 + exit 1 +fi + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test04.sh b/comp2041/tigger/test04.sh new file mode 100755 index 0000000..80f7b2e --- /dev/null +++ b/comp2041/tigger/test04.sh @@ -0,0 +1,45 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that it isn't possible to commit a +# binary file. (as specified, all files should be string-representable). + +assert_good_exec "./tigger-init" +dd if=/dev/zero of=zero bs=1c count=1 status=none # stack overflow make binary file +assert_bad_exec "./tigger-add a" >&2 + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test05.sh b/comp2041/tigger/test05.sh new file mode 100755 index 0000000..cc49cd1 --- /dev/null +++ b/comp2041/tigger/test05.sh @@ -0,0 +1,46 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that adding a file with spacebars +# results in an error. This is specified to not occur in the spec, but it should +# still be handled correctly. + +assert_good_exec "./tigger-init" +printf "placeholder contents" > "file name" +assert_bad_exec "./tigger-add "file name"" + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test06.sh b/comp2041/tigger/test06.sh new file mode 100755 index 0000000..33c93f6 --- /dev/null +++ b/comp2041/tigger/test06.sh @@ -0,0 +1,62 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that deleting a .tigger folder, and then +# restoring the folder, results in the same state that occurred before deletion. +# This implies that the metadata is held only in the .tigger directory. + +assert_good_exec "./tigger-init" +touch a +touch b +touch c +assert_good_exec "./tigger-add a b c" +echo a >> a +assert_good_exec "./tigger-add a" +touch d +assert_good_exec "./tigger-commit -m commit_message" +status=$(./tigger-status) +mv ./.tigger ./.tigger.old +assert_good_exec "./tigger-init" +rm -r ./.tigger +mv ./.tigger.old ./.tigger +printf "testing previous status is the same as the current status\n" +if [ "$(./tigger-status)" != "$status" ]; then + printf "tigger-status was not the same between copies\n" >&2 + exit 1 +fi + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test07.sh b/comp2041/tigger/test07.sh new file mode 100755 index 0000000..fdffb7c --- /dev/null +++ b/comp2041/tigger/test07.sh @@ -0,0 +1,51 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that calling ./tigger-status and +# tigger-log in an empty directory is not considered an error and results in +# empty output. + +assert_good_exec "./tigger-init" +assert_good_exec "./tigger-status" +assert_good_exec "./tigger-log" +printf "testing tigger status in an empty dir\n" +if [ "$(./tigger-status | grep -E -v "^tigger-*")" != "" ]; then # remove tigger-* files from output + printf "tigger-status was not empty in an empty dir\n" >&2 + exit 1 +fi + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test08.sh b/comp2041/tigger/test08.sh new file mode 100755 index 0000000..6d9116f --- /dev/null +++ b/comp2041/tigger/test08.sh @@ -0,0 +1,70 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that creating a new branch from an empty +# branch branches with multiple files with multiple commits and then deleting +# the branch results in the same status as an empty branch. + +# TODO CURRENTLY DOESNT FUNCTION BECAUSE CHECKOUT IS WRONG IN MY IMPLEMENTATION! + +printf "WARNING: DOESN'T FUNCTION DUE TO NON FULLY IMPLEMENTED CHECKOUT\n\n" + +assert_good_exec "./tigger-init" +touch z +assert_good_exec "./tigger-add z" +assert_good_exec "./tigger-commit -m commitmessage" +before_status=$(./tigger-status) +assert_good_exec "./tigger-branch new" +assert_good_exec "./tigger-checkout new" +touch a +touch b +assert_good_exec "./tigger-add a b" +assert_good_exec "./tigger-commit -m commitmessage" +touch c +touch d +assert_good_exec "./tigger-add c d" +assert_good_exec "./tigger-commit -m commitmessage" +assert_good_exec "./tigger-checkout master" +assert_good_exec "./tigger-branch -d new" + +printf "testing tigger status is the same as before the branch was created\n" +if [ "$(./tigger-status)" != "$before_status" ]; then + printf "tigger-status was not empty in an empty dir\n" >&2 + exit 1 +fi + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/test09.sh b/comp2041/tigger/test09.sh new file mode 100755 index 0000000..6931de6 --- /dev/null +++ b/comp2041/tigger/test09.sh @@ -0,0 +1,61 @@ +#!/bin/dash + +# Test startup, create a temporary directory and cd into it with files. +tmp_dir=$(mktemp --directory --quiet --tmpdir=./) +if [ "$(find tigger-* | wc -l)" -le 0 ]; then + printf "error: no tigger commands found, cannot run tests\n" >&2 + exit 1 +fi +cp tigger-* "$tmp_dir"/ +cd "$tmp_dir" || exit + +# Functions to test if the return value was 0 or non-zero. +assert_good_exec () { + printf "%s: " "$1" + if $1; then + return + fi + printf "test failed, previous command expected zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +# Functions to test if the return value was 0 or non-zero. +assert_bad_exec () { + printf "%s: " "$1" + if ! $1; then + return + fi + printf "test failed, previous command expected non-zero output code\n" >&2 + rm -rf ../"$tmp_dir" + exit 1 +} +printf "starting tests: output and order will be printed\n\n" +# End of test startup + +# The purpose of this test is to ensure that tigger-rm'ing a file can still be +# retrieved later (provided it was in a different commit). +assert_good_exec "./tigger-init" +printf "contents" > a +assert_good_exec "./tigger-add a" +assert_good_exec "./tigger-commit -m commitmessage" +assert_good_exec "./tigger-rm a" +assert_good_exec "./tigger-commit -m commitmessage" + +# Ensure it was deleted. +if [ -e a ]; then + printf "error: file was not deleted after git rm\n" >&2 + exit 1 +fi + +# Ensure it can be later retrieved. +assert_good_exec "./tigger-show 0:a" +if [ "$(./tigger-show 0:a)" = "" ]; then + printf "error: file was not retrievable after git rm\n" >&2 + exit 1 +fi + + +# Test teardown/cleanup +printf "\ntest completed successfully\n" +rm -rf ../"$tmp_dir" +exit 0 diff --git a/comp2041/tigger/tigger-add b/comp2041/tigger/tigger-add new file mode 100755 index 0000000..4ec51bb --- /dev/null +++ b/comp2041/tigger/tigger-add @@ -0,0 +1,42 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" + exit 1 +fi + +# Get current branch, then current commit (default 0). +branch=$(cat .tigger/branch.tig) +mkdir --parents ".tigger/$branch/" + + +# Grabs the latest commit +commit=$(find .tigger/"$branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$commit" = "" ]; then + commit="0" + mkdir --parents ".tigger/$branch/$commit/" +fi + + +staged_dir=".tigger/$branch/$commit/staged/" +mkdir --parents "$staged_dir" +for filename in "$@"; do + + if ! [ -f "$filename" ]; then + + # If it doesn't exist in the dir, but does exist in staged, delete staged. + if [ -f .tigger/"$branch"/"$commit"/staged/"$filename" ]; then + rm --force .tigger/"$branch"/"$commit"/staged/"$filename" + continue + fi + + printf "tigger-add: error: can not open '%s'\n" "$filename" + exit 1 + fi + + + cp "$filename" "$staged_dir" +done + +exit 0 diff --git a/comp2041/tigger/tigger-branch b/comp2041/tigger/tigger-branch new file mode 100755 index 0000000..f4a5472 --- /dev/null +++ b/comp2041/tigger/tigger-branch @@ -0,0 +1,68 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + + +# For some reason we have to make it so that we can only run this if we're after +# the first commit, even though this isn't a part of git and our program can do +# it without issues. +if ! [ -e ".tigger/branch.unlock" ]; then + printf "tigger-branch: error: this command can not be run until after the first commit\n" >&2 + exit 1 +fi + +# Case where we give it no arguments -> list the branches. +if [ "$(echo "$@" | tr -d " \n")" = "" ]; then + printf "%s\n" "$(find .tigger/* -maxdepth 0 -type d | sort | grep -o -E "[\/][^\/]*$" | tr -d "/")" + exit 0 +fi + +target_branch=$(echo "$@" | sed "s|-d||g" | tr -d " "); +if [ "$target_branch" = "" ]; then + # no branch provided + printf "tigger-branch: error: no branch provided\n" >&2 + exit 1 +fi + +# Delete a branch, again taking care that we remove the target branch string +# before testing if it contains -d. +if [ "$(echo "$@" | sed "s|$target_branch||g" | grep -o -E "\-d" | tr -d " ")" != "" ]; then + + if [ "$target_branch" = master ]; then + printf "tigger-branch: error: can not delete branch '%s'\n" "$target_branch" >&2 + exit 1 + fi + + if ! [ -d .tigger/"$target_branch"/ ]; then + printf "tigger-branch: error: branch '%s' doesn't exist\n" "$target_branch" >&2 + exit 1 + fi + + rm -r .tigger/"$target_branch" + printf "Deleted branch '%s'\n" "$target_branch" + exit 0 +fi + +# Adding branch logic should test if a branch exists before adding it. +if [ -d .tigger/"$target_branch"/ ]; then + printf "tigger-branch: error: branch '%s' already exists\n" "$target_branch" >&2 + exit 1 +fi + +# We have to copy our current commit to the target branch. +current_branch=$(cat .tigger/branch.tig) +current_commit=$(find .tigger/"$current_branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$current_commit" = "" ]; then + current_commit="0" + mkdir --parents ".tigger/$current_branch/$current_commit/" +fi + +mkdir --parents .tigger/"$target_branch"/ +mkdir --parents .tigger/"$target_branch"/0/staged +cp -r .tigger/"$current_branch"/"$current_commit"/commit .tigger/"$target_branch"/0/ + +exit 0 diff --git a/comp2041/tigger/tigger-checkout b/comp2041/tigger/tigger-checkout new file mode 100755 index 0000000..a1a5a21 --- /dev/null +++ b/comp2041/tigger/tigger-checkout @@ -0,0 +1,84 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + +# Case where we give it no arguments -> early out (without error?) +target_branch=$(echo "$@" | tr -d " "); +if [ "$target_branch" = "" ]; then + # no branch provided, idk if we should print anything so just early out + exit 1 +fi + +if ! [ -d .tigger/"$target_branch" ]; then + printf "tigger-checkout: error: unknown branch '%s'\n" "$target_branch" >&2 + exit 1 +fi + + +current_branch=$(cat .tigger/branch.tig) +current_commit=$(find .tigger/"$current_branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$current_commit" = "" ]; then + current_commit="0" + mkdir --parents ".tigger/$current_branch/$current_commit/" +fi + +target_commit=$(find .tigger/"$target_branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$target_commit" = "" ]; then + target_commit="0" + mkdir --parents ".tigger/$target_branch/$target_commit/" +fi + +for filename in *; do + basename=$(basename "$filename") + + # nothing in dir, just exit (should exit) + if ! [ -e "$filename" ]; then + continue + fi + + # if it's in staging, do not remove + #if [ -e .tigger/"$current_branch"/"$current_commit"/staged/"$basename" ]; then + # continue + #fi + + # if it doesn't exist in the target commit, remove + if ! [ -e .tigger/"$target_branch"/"$target_commit"/commit/"$basename" ]; then + rm "$basename" + continue + fi +done + +for filename in .tigger/"$target_branch"/"$target_commit"/commit/*; do + basename=$(basename "$filename") + + # nothing in dir, just exit (should exit) + if ! [ -e "$filename" ]; then + continue + fi + + # if it's in the current dir, do nothing TODO fix if different + if [ -e "$basename" ]; then + + # If it's the same AND not different to current staging, don't copy. + if [ "$(diff .tigger/"$target_branch"/"$target_commit"/commit/"$basename" "$basename")" = "" ]; then + if [ "$(diff .tigger/"$current_branch"/"$current_commit"/staged/"$basename" "$basename")" != "" ]; then + continue + fi + fi + + fi + + cp "$filename" ./ +done + +# Non-compliant tigger vs git behaviour: copy our staged to theirs. +rm -rf .tigger/"$target_branch"/"$target_commit"/staged +cp -r .tigger/"$current_branch"/"$current_commit"/staged .tigger/"$target_branch"/"$target_commit"/staged + +echo "$target_branch" > .tigger/branch.tig +printf "Switched to branch '%s'\n" "$target_branch" +exit 0 diff --git a/comp2041/tigger/tigger-commit b/comp2041/tigger/tigger-commit new file mode 100755 index 0000000..b2c7573 --- /dev/null +++ b/comp2041/tigger/tigger-commit @@ -0,0 +1,88 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + + +# Get current branch, then current commit (default 0). +branch=$(cat .tigger/branch.tig) +mkdir --parents ".tigger/$branch/" +commit=$(find .tigger/"$branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$commit" = "" ]; then + commit="0" + mkdir --parents ".tigger/$branch/$commit/" +fi + + +commit_files=".tigger/$branch/$commit/" +file_dir="$commit_files/commit/" +mkdir --parents "$file_dir" +mkdir --parents "$commit_files/staged/" + + +# -a causes all files already in the index to have their contents from the +# current directory added to the index before the commit. We have to remove +# the message arg before testing the regex here. +message=$(echo "$@" | grep -o -E "\-m\s?.*" | cut -d' ' -f2- | tr -d \"\') +if [ "$(echo "$@" | sed "s|$message||g" | grep -o -E "\-a")" != "" ]; then + for filename in "$commit_files"/staged/*; do + basename=$(basename "$filename") + if ! [ -e "$basename" ]; then + continue + fi + cp "$basename" "$commit_files"/staged/ + done +fi + + +# If all files in staging are equal to those in commit, we have nothing to commit. +different="" +for filename in "$commit_files"/staged/*; do + basename=$(basename "$filename") + + EXISTS="$(cmp --silent .tigger/"$branch"/"$commit"/commit/"$basename" "$filename"; echo $?)" + if [ "$EXISTS" -ne 0 ]; then + different="true" + break + fi +done +for filename in "$commit_files"/commit/*; do + basename=$(basename "$filename") + + EXISTS="$(cmp --silent .tigger/"$branch"/"$commit"/staged/"$basename" "$filename"; echo $?)" + if [ "$EXISTS" -ne 0 ]; then + different="true" + break + fi +done +if [ "$different" = "" ] || [ -z "$(find .tigger/"$branch"/"$commit"/staged/ .tigger/"$branch"/"$commit"/commit/ -maxdepth 1 -type f)" ] ; then + printf "nothing to commit\n" + exit 0 +fi + + +# Copy all from staged into commit. +#for filename in "$commit_files"/staged/*; do +# cp "$filename" "$file_dir" +#done +rm -r --force "$commit_files"/commit/ +cp -r "$commit_files"/staged "$commit_files"/commit/ + + +# Copy our commit message and setup our new commit (n + 1). +#printf "%s > .tigger/%s/%s/message.tig\n" "$branch" "$commit" +printf "%s" "$message" > .tigger/"$branch"/"$commit"/message.tig +new_commit="$((($(cat .tigger/commit_count.tig) + 1)))" +mkdir --parents ".tigger/$branch/$new_commit/commit" +cp -r ".tigger/$branch/$commit/commit" ".tigger/$branch/$new_commit/" +cp -r ".tigger/$branch/$commit/commit" ".tigger/$branch/$new_commit/staged" +touch .tigger/branch.unlock + +# Increment our commit count value (which isn't used outside of printing). +echo "$new_commit" > .tigger/commit_count.tig + +printf "Committed as commit %s\n" "$((($(printf "%s" "$new_commit") - 1)))" +exit 0 diff --git a/comp2041/tigger/tigger-init b/comp2041/tigger/tigger-init new file mode 100755 index 0000000..cee1a77 --- /dev/null +++ b/comp2041/tigger/tigger-init @@ -0,0 +1,39 @@ +#!/bin/dash + +# We will create a new file in .tigger called "branch.tig" with our current +# branch. By default this should be master. All future ops should read this. + +# Commits will be contained in files starting at 0...n inside our branch dir. +# Our directory heirarchy might look like: +# .tigger/ +# branch.tig // current branch +# commit_count.tig // see the below comment @ echo "0"... +# master/ +# 0/ +# commit/ +# staged/ +# message.tig // because we have a message, it's a finished commit +# 1/ +# commit/ +# staged/ + +if [ -d ".tigger/" ]; then + printf "tigger-init: error: .tigger already exists\n" >&2 + exit 1 +fi + +branch="master" # in future should be ~ branch=$(cat ./.tigger/branch.tig) +mkdir --parents ".tigger/$branch" +echo "$branch" > ".tigger/branch.tig" + +# This is necessary because their implementation of git branch considers a +# global commit count as opposed to a (more logical) per branch commit count. +# Whenever we do any commit we have to increment this value (which is only +# used for printing so that we are compliant with their autotests). +echo "0" > .tigger/commit_count.tig + +mkdir --parents ".tigger/$branch/0/commit" +mkdir --parents ".tigger/$branch/0/staged" + +printf "Initialized empty tigger repository in .tigger\n" +exit 0 diff --git a/comp2041/tigger/tigger-log b/comp2041/tigger/tigger-log new file mode 100755 index 0000000..5e269c2 --- /dev/null +++ b/comp2041/tigger/tigger-log @@ -0,0 +1,24 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + +# Get current branch. +branch=$(cat .tigger/branch.tig) +mkdir --parents ".tigger/$branch/" + +# Iterate through commits and print their message. +for commit in $(find .tigger/"$branch"/* -maxdepth 0 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -r -n); do + + # No message = we're working on it, so it's not considered here. + if ! [ -e .tigger/"$branch"/"$commit"/message.tig ]; then + continue + fi + + printf "%s %s\n" "$commit" "$(cat .tigger/"$branch"/"$commit"/message.tig)" +done + +exit 0 diff --git a/comp2041/tigger/tigger-rm b/comp2041/tigger/tigger-rm new file mode 100755 index 0000000..e50420a --- /dev/null +++ b/comp2041/tigger/tigger-rm @@ -0,0 +1,66 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + +# Get current branch, then current commit (default 0). +branch=$(cat .tigger/branch.tig) +mkdir --parents ".tigger/$branch/" +commit=$(find .tigger/"$branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$commit" = "" ]; then + commit="0" + mkdir --parents ".tigger/$branch/$commit/" +fi + +commit_files=".tigger/$branch/$commit/" +mkdir --parents "$commit_files/commit/" +mkdir --parents "$commit_files/staged/" + +for filename in $(echo "$@" | sed "s|--force||g" | sed "s|--cached||g"); do + + basename=$(basename "$filename") + if ! [ -e .tigger/"$branch"/"$commit"/staged/"$basename" ]; then + printf "tigger-rm: error: '%s' is not in the tigger repository\n" "$basename" >&2 + exit 1 + fi + + # Accidental deletion checking, should be non-cached and non-forced. + if [ "$(echo "$@" | grep -o -E "\-\-force")" = "" ]; then + + if [ "$(cmp --silent .tigger/"$branch"/"$commit"/staged/"$basename" "$basename"; echo $?)" -ne 0 ]; then + + if [ "$(cmp --silent .tigger/"$branch"/"$commit"/staged/"$basename" .tigger/"$branch"/"$commit"/commit/"$basename"; echo $?)" -ne 0 ]; then + printf "tigger-rm: error: '%s' in index is different to both the working file and the repository\n" "$basename" >&2 + exit 1 + fi + + if ! [ "$(echo "$@" | grep -o -E "\-\-cached")" != "" ]; then + printf "tigger-rm: error: '%s' in the repository is different to the working file\n" "$basename" >&2 + exit 1 + fi + + fi + + + if ! [ "$(echo "$@" | grep -o -E "\-\-cached")" != "" ]; then + if [ "$(cmp --silent .tigger/"$branch"/"$commit"/staged/"$basename" .tigger/"$branch"/"$commit"/commit/"$basename"; echo $?)" -ne 0 ]; then + printf "tigger-rm: error: '%s' has staged changes in the index\n" "$basename" >&2 + exit 1 + fi + fi + fi + + rm --force .tigger/"$branch"/"$commit"/staged/"$basename" + # If the cached option is specified the files are removed only from the index. + if [ "$(echo "$@" | grep -o -E "\-\-cached")" != "" ]; then + continue + fi + + rm --force "$basename" + +done + +exit 0 diff --git a/comp2041/tigger/tigger-show b/comp2041/tigger/tigger-show new file mode 100755 index 0000000..05d8405 --- /dev/null +++ b/comp2041/tigger/tigger-show @@ -0,0 +1,63 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + +if [ "$(printf "%s" "$@")" = "" ]; then + printf "tigger-show: error: no arguments provided\n" >&2 + exit 1 +fi + +branch=$(cat .tigger/branch.tig) +filename=$(echo "$@" | cut -d':' -f2-2) +commit=$(echo "$@" | cut -d':' -f1-1) + +# Lots of error checking here and unnecessarily distinct printing per error. + +target="" # LAMBDA CAPTURE +if [ "$commit" != "" ]; then + target=".tigger/$branch/$commit/commit/$filename" + + if ! [ -e ".tigger/$branch/$commit/message.tig" ]; then + printf "tigger-show: error: unknown commit '%s'\n" "$commit" >&2 + exit 1 + fi + + if ! [ -e "$target" ]; then + printf "tigger-show: error: '%s' not found in commit %s\n" "$filename" "$commit" >&2 + exit 1 + fi + + printf "%s\n" "$(cat "$target")" + exit 0 + +else + commit=$(find .tigger/"$branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) + if [ "$commit" = "" ]; then + commit="0" + mkdir --parents ".tigger/$branch/$commit/" + fi + + if ! [ -e ".tigger/$branch/$commit/staged/$filename" ]; then + printf "tigger-show: error: '%s' not found in index\n" "$filename" >&2 + exit 1 + fi + + target=".tigger/$branch/$commit/staged/$filename" +fi + +if ! [ -e ".tigger/$branch/$commit/" ]; then + printf "tigger-show: error: unknown commit '%s'\n" "$commit" >&2 + exit 1 +fi + +if ! [ -e "$target" ]; then + printf "tigger-show: error: '%s' not found in commit %s\n" "$filename" "$commit" >&2 + exit 1 +fi + +printf "%s\n" "$(cat "$target")" +exit 0 diff --git a/comp2041/tigger/tigger-status b/comp2041/tigger/tigger-status new file mode 100755 index 0000000..b623432 --- /dev/null +++ b/comp2041/tigger/tigger-status @@ -0,0 +1,100 @@ +#!/bin/dash + +# Test if we have a valid repo before doing anything. +if ! [ -d ".tigger/" ]; then + printf "tigger-add: error: tigger repository directory .tigger not found\n" >&2 + exit 1 +fi + +# Get current branch + commit as usual. +branch=$(cat .tigger/branch.tig) +mkdir --parents ".tigger/$branch/" +commit=$(find .tigger/"$branch"/ -maxdepth 1 -type d | grep -o -E "[\/][^\/]*$" | tr -d "/" | sort -n -r | head -n 1) +if [ "$commit" = "" ]; then + commit="0" + mkdir --parents ".tigger/$branch/$commit/" +fi + +# Grabs regular files from our working dir, staging and commit. +files=$(find .tigger/"$branch"/"$commit"/commit/ .tigger/"$branch"/"$commit"/staged/ ./ -maxdepth 1 -type f -printf "%P\n" | sort | uniq | grep -v -E "^\." 2>/dev/null) +for filename in $files; do + + c_dir=".tigger/$branch/$commit/commit/$filename"; + s_dir=".tigger/$branch/$commit/staged/$filename"; + + is_c=0 + is_s=0 + is_w=0 + if [ -e "$c_dir" ]; then + is_c=1 + fi + if [ -e "$s_dir" ]; then + is_s=1 + fi + if [ -e "$filename" ]; then + is_w=1 + fi + + # There are a LOT of very specific cases here. + + if [ $is_w -eq 0 ] && [ $is_s -eq 0 ] && [ $is_c -eq 1 ]; then + printf "%s - deleted\n" "$filename" + continue + fi + + if [ $is_w -eq 1 ] && [ $is_s -eq 0 ] && [ $is_c -eq 0 ]; then + printf "%s - untracked\n" "$filename" + continue; + fi + + if [ $is_w -eq 1 ] && [ $is_s -eq 1 ] && [ $is_c -eq 1 ]; then + c="$(cat "$c_dir")" + s="$(cat "$s_dir")" + w="$(cat "$filename")" + + if [ "$w" != "$s" ] && [ "$s" != "$c" ] && [ "$c" != "$w" ]; then + printf "%s - file changed, different changes staged for commit\n" "$filename" + continue + fi + + if [ "$w" = "$s" ] && [ "$s" != "$c" ]; then + printf "%s - file changed, changes staged for commit\n" "$filename" + continue + fi + + if [ "$w" != "$s" ] && [ "$s" = "$c" ]; then + printf "%s - file changed, changes not staged for commit\n" "$filename" + continue + fi + + if [ "$w" = "$s" ] && [ "$s" = "$c" ]; then + printf "%s - same as repo\n" "$filename" + continue + fi + fi + + if [ $is_w -eq 0 ] && [ $is_s -eq 1 ] && [ $is_c -eq 1 ] && [ "$(diff "$s_dir" "$c_dir")" = "" ]; then + printf "%s - file deleted\n" "$filename" + continue + fi + + if [ $is_w -eq 1 ] && [ $is_s -eq 1 ] && [ $is_c -eq 0 ] && [ "$(diff "$filename" "$s_dir")" = "" ]; then + printf "%s - added to index\n" "$filename" + continue + fi + + if [ $is_w -eq 1 ] && [ $is_s -eq 1 ] && [ $is_c -eq 0 ] && [ "$(diff "$filename" "$s_dir")" != "" ]; then + printf "%s - added to index, file changed\n" "$filename" + continue; + fi + + if [ $is_w -eq 0 ] && [ $is_s -eq 1 ] && [ $is_c -eq 0 ]; then + printf "%s - added to index, file deleted\n" "$filename" + continue; + fi + + printf "%s - untracked\n" "$filename" + +done + +exit 0 |
