|
#!/bin/bash |
|
GIT_NAV_DIR="${HOME}/.git-nav" |
|
|
|
# Generate a file path to store commit history |
|
# It use the MD5 hash of the Git root directory as the file name |
|
generate_commit_file_path() { |
|
if git rev-parse --show-toplevel > /dev/null 2>&1; then |
|
git_root=$(git rev-parse --show-toplevel) |
|
file_name=$(echo -n "$git_root" | md5 | awk '{print $1}') |
|
mkdir -p ${GIT_NAV_DIR} |
|
echo "${GIT_NAV_DIR}/${file_name}" |
|
return 0 |
|
else |
|
echo "Not inside a Git repository" >&2 |
|
return 1 |
|
fi |
|
} |
|
|
|
COMMITS_FILE="$(generate_commit_file_path)" |
|
if [ $? -ne 0 ]; then |
|
exit 1 |
|
fi |
|
|
|
# Function to initialize commit history |
|
initialize_commits() { |
|
DEFAULT_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) |
|
BRANCH=$(git rev-parse --abbrev-ref HEAD) |
|
|
|
# check if default branch is the same as the current branch |
|
if [ "$DEFAULT_BRANCH" != "$BRANCH" ]; then |
|
MERGE_BASE=$(git merge-base "$DEFAULT_BRANCH" "$BRANCH") |
|
git log --reverse --pretty=format:"%H" "$MERGE_BASE..HEAD" > "$COMMITS_FILE" |
|
else |
|
git log --reverse --pretty=format:"%H" > "$COMMITS_FILE" |
|
fi |
|
|
|
} |
|
|
|
# Function to get the current commit index |
|
get_current_commit_index() { |
|
current_commit=$(git rev-parse HEAD) |
|
grep -n "$current_commit" "$COMMITS_FILE" | cut -d: -f1 |
|
} |
|
|
|
# Function to checkout the commit at a specific index |
|
checkout_commit() { |
|
commit=$(sed -n "${1}p" "$COMMITS_FILE") |
|
if [ -n "$commit" ]; then |
|
git checkout "$commit" |
|
else |
|
echo "No commit found at index $1" |
|
fi |
|
} |
|
|
|
# Navigate to the next commit |
|
next_commit() { |
|
current_index=$(get_current_commit_index) |
|
if [ -z "$current_index" ]; then |
|
echo "No commits found, or not on a commit." |
|
exit 1 |
|
fi |
|
next_index=$((current_index + 1)) |
|
checkout_commit $next_index |
|
} |
|
|
|
# Navigate to the previous commit |
|
prev_commit() { |
|
current_index=$(get_current_commit_index) |
|
if [ -z "$current_index" ]; then |
|
echo "No commits found, or not on a commit." |
|
exit 1 |
|
fi |
|
prev_index=$((current_index - 1)) |
|
checkout_commit $prev_index |
|
} |
|
|
|
reset_commits() { |
|
BRANCH=$(git branch --contains HEAD | grep -v HEAD | awk '{print $1}') |
|
git checkout $BRANCH |
|
} |
|
|
|
# Handle arguments to the script |
|
case "$1" in |
|
next) |
|
next_commit |
|
;; |
|
prev) |
|
prev_commit |
|
;; |
|
first) |
|
checkout_commit 1 |
|
;; |
|
reset) |
|
reset_commits |
|
;; |
|
init) |
|
initialize_commits |
|
;; |
|
*) |
|
echo "Usage: git-nav {init|next|prev|first|reset}" |
|
exit 1 |
|
;; |
|
esac |
|
|
Git Nav script
A helper script for navigating between commits in a Git branch.
While this may not be useful for everyday tasks, it can be helpful for demonstrating the progress of your feature or implementation. I use this during training sessions, where each commit contains the code implementation of the topic I want to discuss.
Usage
git-nav.sh init
from the repository to initialize the script.git-nav.sh prev
to navigate to the previous commit.git-nav.sh next
to navigate to the next commit.Alias