158 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable file
		
	
	
	
	
| #!/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
 |