You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
4.0 KiB

#!/usr/bin/bash
function help() {
cat <<HELP
Git Jump (Forward & Back)
http://benalman.com/
Modified slightly by Jonathan Hodgson
https://jonathanh.co.uk/
Copyright (c) 2017 Jonathan Hodgson
Licensed under the MIT license.
https://en.wikipedia.org/wiki/MIT_License
Usage: $(basename "$0") [command]
Commands:
next Jump forward to the next commit in this branch
prev Jump backward to the next commit in this branch
Git config:
git-jump.branch Branch to jump through. If not set, defaults to master
Description:
"Replay" Git commits by moving forward / backward through a branch's
history. Before jumping, any current unstaged changes and untracked
files are saved in a tag for later retrieval, which is restored when
jumped back to.
Original Licence:
Copyright (c) 2014 "Cowboy" Ben Alman
Licensed under the MIT license.
http://benalman.com/about/license/
HELP
}
function usage() {
echo "Usage: $(basename "$0") [next | prev]"
}
# Get branch stored in Git config or default to master
git_branch="$(git config git-jump.branch || echo "master")"
# Get some (short) SHAs
function git_branch_sha() {
git rev-parse --short "$git_branch"
}
function git_head_sha() {
git rev-parse --short HEAD
}
function git_prev_sha() {
git log --format='%h' "$git_branch" "$@" | awk "/^$(git_head_sha)/{getline; print}"
}
function git_next_sha() {
git_prev_sha --reverse
}
# Get absolute path to root of Git repo
function git_repo_toplevel() {
git rev-parse --show-toplevel
}
# Get subject of specified commit
function git_commit_subject() {
git log --format='%s' -n 1 $1
}
# Save changes for later retrieval
function save() {
local status=""
local head_sha=$(git_head_sha)
# Checkout current HEAD by SHA to force detached state
git checkout -q $head_sha
# Add all files in repo
git add "$(git_repo_toplevel)"
# Commit changes (if there were any)
git commit --no-verify -m "Git Jump: saved changes for $head_sha" >/dev/null
# If the commit was successful, tag it (overwriting any previous tag)
if [[ $? == 0 ]]; then
status="*"
git tag -f "git-jump-$head_sha" >/dev/null
fi
echo "Previous HEAD was $head_sha$status, $(git_commit_subject $head_sha)"
}
# Restore previously-saved changes
function restore() {
local status=""
# Save current changes before restoring
save
# Attempt to restore saved changes for specified commit
git checkout "git-jump-$1" 2>/dev/null
if [[ $? == 0 ]]; then
# If the restore was successful, figure out exactly what was saved, check
# out the original commit, then restore the saved changes on top of it
status="*"
local patch="$(git format-patch HEAD^ --stdout)"
git checkout HEAD^ 2>/dev/null
echo "$patch" | git apply -
else
# Otherwise, just restore the original commit
git checkout "$1" 2>/dev/null
fi
echo "HEAD is now $1$status, $(git_commit_subject $1)"
}
# Jump to next commit
function next() {
local next_sha=$(git_next_sha)
if [[ "$next_sha" == "$(git_head_sha)" ]]; then
# Abort if no more commits
echo "Already at last commit in $git_branch. Congratulations!"
else
# Checkout branch by name if at its HEAD
if [[ "$next_sha" == "$(git_branch_sha)" ]]; then
next_sha="$git_branch"
fi
echo "Jumping ahead to next commit."
restore $next_sha
fi
}
# Jump to previous commit
function prev() {
local prev_sha=$(git_prev_sha)
if [[ "$prev_sha" == "$(git_head_sha)" ]]; then
# Abort if no more commits
echo "Already at first commit in $git_branch."
else
echo "Jumping back to previous commit."
restore $prev_sha
fi
}
# Show help if requested
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
help
exit
fi
# Check if branch is valid
git rev-parse "$git_branch" &>/dev/null
if [[ $? != 0 ]]; then
echo "Error: Branch \"$git_branch\" does not appear to be valid."
echo "Try $(basename "$0") --help for more information."
exit 1
fi
# Handle CLI arguments
if [[ "$1" == "next" ]]; then
next
elif [[ "$1" == "prev" ]]; then
prev
else
usage
exit 1
fi