Skip to content

Instantly share code, notes, and snippets.

@SomajitDey
Last active September 26, 2021 15:51
Show Gist options
  • Save SomajitDey/e39bdade20c46c7741b683432a45a3bf to your computer and use it in GitHub Desktop.
Save SomajitDey/e39bdade20c46c7741b683432a45a3bf to your computer and use it in GitHub Desktop.
TODO list for any git branch. Read file header for installation and usage.
#!/usr/bin/env bash
# Brief: TODO list specific to git branches. No effect on commits, staged or worktree
# Installation:
# chmod +x ./git-todo
# sudo mv ./git-todo /usr/local/bin
# git config --global alias.todo '!git-todo'
# Help: git todo -h
# Author: Somajit Dey <dey.somajit@gmail.com> 2021 [https://github.com/SomajitDey]
# Source: https://gist.github.com/SomajitDey
# GitHubGist Search Terms: username:SomajitDey filename:git-todo.bash
usage(){
echo " Brief: TODO list specific to given git branch. If branch is not given, current branch is used.
Usage: git todo [-e] [-d] [-u] [-r] [-f] [-a \"new task to be appended\"] [branch]
Options:
-e Edit TODO list (through editor)
-a Quickly append given task to TODO list (without opening editor)
-u Undo last edit
-r Undo last undo
-d Delete TODO list
-f Delete current TODO list and create a fresh one"
}
while getopts heurdfa: opt; do
case "${opt}" in
h) usage; exit;;
e) mode="edit" ;;
d) mode="delete" ;;
u) mode="undo" ;;
r) mode="redo" ;;
f) mode="freshen" ;;
a) mode="${mode:-edit}"; task="${OPTARG}";;
*) exit 1;;
esac
done
mode="${mode:-show}"
git_dir=$(git rev-parse --absolute-git-dir) || exit 1
todo_dir="${git_dir}/todo" ; mkdir -p "${todo_dir}"
branch="${!OPTIND:-"$(git branch --show-current)"}"
(git branch -a | grep -q "${branch}") || { echo "Given branch doesn't exist" >&2; exit 1;}
todo_file="${todo_dir}/${branch}"
[[ -e "${todo_file}" ]] || mode="edit"
header="### This is the TODO list for branch \"${branch}\". Write your tasks below. Separate tasks with blank line(s). ###"
show(){
case "$(head -n1 "${todo_file}")" in
"${header}") local read_from=2;;
*) local read_from=1;;
esac
tail -q -n "+${read_from}" "${todo_file}" | cat -sb | fold -sw "$((COLUMNS-2))"
echo
}
edit(){
local editor=$(git config core.editor)
[[ -e "${todo_file}" ]] || echo "${header}" > "${todo_file}"
cp -n "${todo_file}" "${todo_file}.undo.candidate"
if [[ -v task ]]; then
echo -e "\n${task}" >> "${todo_file}"
else
"${editor:=vi}" "${todo_file}"
fi
if diff -q "${todo_file}" "${todo_file}.undo.candidate" &>/dev/null; then
rm "${todo_file}.undo.candidate"
else
mv "${todo_file}.undo.candidate" "${todo_file}.undo"
# Remove trailing blank lines. We use awk which can't do in place edit. So,
# using the redo file which would have to be deleted anyway.
awk '/^$/ {nlstack=nlstack "\n";next;} {printf "%s",nlstack; nlstack=""; print;}' "${todo_file}" > "${todo_file}.redo"
# Ref: https://unix.stackexchange.com/a/81688
# Credit: @HaukeLaging https://unix.stackexchange.com/users/32191/hauke-laging
mv "${todo_file}.redo" "${todo_file}"
fi
}
undo(){
if [[ -e "${todo_file}.undo" ]]; then
mv "${todo_file}" "${todo_file}.redo"
mv "${todo_file}.undo" "${todo_file}"
else
echo "Nothing to undo" >&2
return 1
fi
}
redo(){
if [[ -e "${todo_file}.redo" ]]; then
mv "${todo_file}" "${todo_file}.undo"
mv "${todo_file}.redo" "${todo_file}"
else
echo "Nothing to redo" >&2
return 1
fi
}
delete(){
mv "${todo_file}" "${todo_file}.undo"
cp "${todo_file}.undo" "${todo_file}.undo.candidate"
}
freshen(){
delete
edit
}
"${mode}"
exit
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment