Skip to content

Instantly share code, notes, and snippets.

@jordan-brough
Last active September 11, 2024 21:46
Show Gist options
  • Save jordan-brough/48e2803c0ffa6dc2e0bd to your computer and use it in GitHub Desktop.
Save jordan-brough/48e2803c0ffa6dc2e0bd to your computer and use it in GitHub Desktop.
Git: Display a list of recently checked out branches/tags/commits
#!/usr/bin/env bash
# Source: https://gist.github.com/jordan-brough/48e2803c0ffa6dc2e0bd
# See also: https://stackoverflow.com/a/25095062/58876
# Download this script as "git-recent" (no extension), chmod it to be executable and put it in your
# path somewhere (e.g. /usr/bin). You can then use it via `git recent` from inside any git repo.
# Examples:
# Interactive prompt for most recent 4 branches:
# $ git recent -n 5
# 1) master 4) deleted-branch
# 2) stable 5) improve-everything
# 3) fun
# Choose a branch: 2
# List mode (non-interactive):
# $ git recent -n 5 -l
# master
# stable
# fun
# deleted-branch
# improve-everything
# Verify branch existence:
# $ git recent -n 5 -l -e
# master
# stable
# fun
# improve-everything
# something-else
# # (notice "deleted-branch" removed from results)
# Contributors:
# - jordan-brough
# - fritz-c
usage()
{
echo "usage: git recent [-n lines] [-l] [-e]"
}
while getopts "hn:le" opt; do
case $opt in
h) # help
usage
exit 1
;;
n) # number of results
NUM=$OPTARG
;;
l) # list mode (non-interactive)
INTERACTIVE=0
;;
e) # validate existence
CHECK_EXISTENCE=1
;;
\?)
usage
exit 1
;;
esac
done
NUM=${NUM-10} # default to 10 lines
INTERACTIVE=${INTERACTIVE-1} # default to interactive
CHECK_EXISTENCE=${CHECK_EXISTENCE-0} # default to *not* checking existence (faster)
BRANCHES=(
$(git reflog |
egrep -io "moving from ([^[:space:]]+)" |
awk '{ print $3 }' | # extract 3rd column
awk ' !x[$0]++' | # Removes duplicates. See http://stackoverflow.com/questions/11532157
egrep -v '^[a-f0-9]{40}$' | # remove hash results
while read line; do # verify existence
([[ $CHECK_EXISTENCE = '0' ]] || git rev-parse --verify "$line" &>/dev/null) && echo "$line"
done |
head -n "$NUM"
)
)
if [[ $INTERACTIVE = '1' ]]; then
PS3="Choose a branch: "
select d in "${BRANCHES[@]}"; do
test -n "$d" && break;
echo ">>> Invalid Selection";
done
git checkout "$d"
else
printf '%s\n' "${BRANCHES[@]}"
fi
@cool-RR
Copy link

cool-RR commented Apr 14, 2020

@jordan-brough Would you be okay with officially releasing this as open-source? All this means is commenting here "I release the code above under the MIT license".

@stephane303
Copy link

If you need this for windows, it works if you use 'Git for Windows':

  1. create a directory C:\Users<your_username>\bin
  2. in Git Bash, verfify you directory is in the path: echo $PATH
  3. copy the above file in C:\Users<your_username>\bin\git-recent

The git recent command will be available in CMD, Powershell, Git bash

@ehudhala
Copy link

ehudhala commented Feb 1, 2021

@jordan-brough Would you be okay with officially releasing this as open-source? All this means is commenting here "I release the code above under the MIT license".

@jordan-brough - This can be really useful as open source - Can you please verify the license is permissive?

@kerryj89
Copy link

kerryj89 commented Oct 28, 2021

Thanks! Would be nice to have a -f "force" option

@crazyones110
Copy link

@jordan-brough Thanks for the script! I've updated the script: adding q to quit and removing -l option, which seems more intuitive to me.

usage()
{
  echo "usage: git recent [-n lines] [-e]"
}

while getopts "hn:e" opt; do
  case $opt in
    h) # help
      usage
      exit 1
      ;;
    n) # number of results
      NUM=$OPTARG
      ;;
    e) # validate existence
      CHECK_EXISTENCE=1
      ;;
    \?)
      usage
      exit 1
      ;;
  esac
done

NUM=${NUM-10} # default to 10 lines
CHECK_EXISTENCE=${CHECK_EXISTENCE-0} # default to *not* checking existence (faster)

BRANCHES=(
  $(git reflog |
    egrep -io "moving from ([^[:space:]]+)" |
    awk '{ print $3 }' | # extract 3rd column
    awk ' !x[$0]++' | # Removes duplicates.  See http://stackoverflow.com/questions/11532157
    egrep -v '^[a-f0-9]{40}$' | # remove hash results
    while read line; do # verify existence
      ([[ $CHECK_EXISTENCE = '0' ]] || git rev-parse --verify "$line" &>/dev/null) && echo "$line"
    done |
    head -n "$NUM"
  )
)

PS3="Choose a branch (or 'q' to quit): "

# Print the options with numbers
i=1
for branch in "${BRANCHES[@]}"; do
  echo "$i) $branch"
  i=$((i+1))
done

while true; do
  # Read user input and process the choice
  echo -n "$PS3"
  read -r choice

  if [[ "$choice" == "q" ]]; then
    echo "Quitting..."
    exit 0
  elif [[ $choice -ge 1 && $choice -le ${#BRANCHES[@]} ]]; then
    d="${BRANCHES[$((choice-1))]}"
    break
  else
    echo ">>> Invalid Selection. Try again."
  fi
done

git checkout "$d"

@crazyones110
Copy link

Thanks! Would be nice to have a -f "force" option

@kerryj89 What do you expect -f option do?

@kerryj89
Copy link

kerryj89 commented Apr 26, 2023

@crazyones110 To force a checkout, but in hindsight this is quite dangerous as we wouldn't know what's stopping us from switching branch on the first run so nvm. What would be nicer is if the script warns you about uncommitted changes and then follows up with asking if you want to force checkout anyway because a lot of times what stops me are silly things like lockfiles after installing depenendencies, e.g.

Choose a branch: 1
error: Your local changes to the following files would be overwritten by checkout:
        yarn.lock
Please commit your changes or stash them before you switch branches.
Aborting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment