mirror of
https://github.com/paulirish/git-recent.git
synced 2026-09-10 15:36:18 -04:00
Compare commits
43 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f6b498667 | ||
|
|
aa19c34401 | ||
|
|
3b429ea804 | ||
|
|
d23d325693 | ||
|
|
724f48ca79 | ||
|
|
cdffe4c40a | ||
|
|
bd0ad4207d | ||
|
|
2e5e69fe30 | ||
|
|
6b98834931 | ||
|
|
6322cde317 | ||
|
|
479bc17a4a | ||
|
|
d892c4bbf2 | ||
|
|
73e40691c6 | ||
|
|
d5f63cf328 | ||
|
|
34af71aef0 | ||
|
|
5af5ff70c8 | ||
|
|
959e8343da | ||
|
|
6b628b190c | ||
|
|
fbb93ae381 | ||
|
|
aa46ef3070 | ||
|
|
1bf0581139 | ||
|
|
6bd7ff3fdf | ||
|
|
bfda73056b | ||
|
|
1c233e995c | ||
|
|
b324fc2e4a | ||
|
|
8237f19b10 | ||
|
|
e8a0560e05 | ||
|
|
de576427fa | ||
|
|
b55a2e4217 | ||
|
|
8d19748426 | ||
|
|
fd0275f148 | ||
|
|
938db7514f | ||
|
|
d2e08436da | ||
|
|
9d13948f50 | ||
|
|
03cf5a320e | ||
|
|
cfd00de858 | ||
|
|
8d67039988 | ||
|
|
1985391134 | ||
|
|
09848b42e5 | ||
|
|
a7ae3198bd | ||
|
|
514bf95d27 | ||
|
|
8beb4ddc42 | ||
|
|
68acec4862 |
24
.github/workflows/publish.yml
vendored
Normal file
24
.github/workflows/publish.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: Publish to npm
|
||||||
|
on:
|
||||||
|
workflow_dispatch: # Allow manual triggering
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
contents: read # Required for checkout
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22.14.0
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- name: Publish to npm
|
||||||
|
run: |
|
||||||
|
npm install -g npm@latest
|
||||||
|
npm publish --provenance --access public --registry https://registry.npmjs.org/
|
||||||
182
git-recent
182
git-recent
|
|
@ -1,49 +1,147 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
##
|
#
|
||||||
## git-recent
|
# git recent 2.0 - switching branches, but so fancy
|
||||||
##
|
#
|
||||||
## list all local branches, sorted by last commit, formatted reall purdy
|
# - view recently edited local branches
|
||||||
##
|
# - see unique commits to that branch, and optionally the branch diff (against the main/master/primary branch) with Ctrl-o
|
||||||
|
# - hit Enter to checkout the selected branch.
|
||||||
|
# - text filtering against branch names, too.
|
||||||
|
#
|
||||||
|
|
||||||
# Windows needs more basic format (#8, git-for-windows/git#865)
|
if ! command -v fzf >/dev/null 2>&1; then
|
||||||
case $(uname -s) in
|
echo "Error: fzf is not installed. Please install fzf to use git-recent." >&2
|
||||||
CYGWIN*|MINGW32*|MSYS*)
|
exit 1
|
||||||
branch='%(refname:short)'
|
fi
|
||||||
spacer=' '
|
[ "$(git rev-parse --is-inside-work-tree)" = "true" ] || exit
|
||||||
;;
|
|
||||||
*)
|
|
||||||
branch='%(color:yellow)%(refname:short)%(color:reset)'
|
|
||||||
spacer='%(color:black) %(color:reset)'
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
format="\
|
if [[ "$1" == "--help" ]]; then
|
||||||
%(HEAD) \
|
echo "git-recent: Browse and checkout recently used Git branches."
|
||||||
$branch|\
|
echo
|
||||||
%(color:bold red)%(objectname:short)%(color:reset) \
|
echo "Keybindings:"
|
||||||
%(color:bold green)(%(committerdate:relative))%(color:reset) \
|
echo " Enter: Checkout the selected branch (or jump to its worktree if open elsewhere)"
|
||||||
%(color:bold blue)%(authorname)%(color:reset) \
|
echo " Ctrl-O: Show the diff of the selected branch against the main/master branch"
|
||||||
%(color:yellow)%(upstream:track)%(color:reset)
|
echo " Ctrl-C: Exit"
|
||||||
$spacer|\
|
exit 0
|
||||||
%(contents:subject)
|
fi
|
||||||
$spacer|"
|
|
||||||
|
|
||||||
lessopts="--tabs=4 --quit-if-one-screen --RAW-CONTROL-CHARS --no-init"
|
# ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
git for-each-ref \
|
# The HEAD of the primary branch (eg main or master or w/e), for diffing.
|
||||||
--color=always \
|
# TODO: some branch mgmt approaches don't work well with this. And may prefer `git log --pretty=format:%H --merges -n 1`. See https://github.com/paulirish/git-recent/issues/28
|
||||||
--sort=-committerdate \
|
diff_base=$(git symbolic-ref refs/remotes/origin/HEAD)
|
||||||
"refs/heads/" \
|
|
||||||
--format="$format" \
|
|
||||||
| column -ts '|' \
|
|
||||||
| less "$lessopts"
|
|
||||||
|
|
||||||
# The above command:
|
# Extract branch name (without any trailing text, like the Chromium link)
|
||||||
# for all known branches,
|
define_branchname="branchname=\\\$(echo {1} | cut -d' ' -f1)"
|
||||||
# (force coloring on this, especially since it's being piped)
|
|
||||||
# sort descending by last commit
|
# Colorized hash, author, date, then commit subject followed by commit message body (wrapped and indented).
|
||||||
# show local branches (change to "" to include both local + remote branches)
|
commits_format="%C(red bold)%h %C(bold blue)%an %C(bold green)%ad %Creset%s%w(0,4,4)%+b%w(0,0,0)"
|
||||||
# apply the formatting template above
|
uniqcommits_cmd="sh -c \"$define_branchname; git log --date=human --color=always --format='$commits_format' --no-merges $diff_base..\\\$branchname\""
|
||||||
# break into columns
|
|
||||||
# use the pager only if there's not enough space
|
# Progressive enhancement if you have delta or diff-so-fancy
|
||||||
|
diff_pager_cmd=$(command -v delta || command -v diff-so-fancy)
|
||||||
|
pipe_to_pager=${diff_pager_cmd:+" | $diff_pager_cmd"}
|
||||||
|
diffbranch_cmd="sh -c \"$define_branchname; git diff --color=always $diff_base...\\\$branchname $pipe_to_pager\""
|
||||||
|
|
||||||
|
# Copy to clipboard, variants for mac/linux
|
||||||
|
copy_cmd="echo 'Could not copy {} to clipboard.'"
|
||||||
|
if command -v pbcopy >/dev/null; then
|
||||||
|
copy_cmd="printf '%s' {} | pbcopy"
|
||||||
|
elif command -v wl-copy >/dev/null; then
|
||||||
|
copy_cmd="printf '%s' {} | wl-copy"
|
||||||
|
elif command -v xclip >/dev/null; then
|
||||||
|
copy_cmd="printf '%s' {} | xclip -selection clipboard"
|
||||||
|
elif command -v xsel >/dev/null; then
|
||||||
|
copy_cmd="printf '%s' {} | xsel --clipboard"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_get_worktree_for_branch() {
|
||||||
|
local branch="$1"
|
||||||
|
local current_toplevel
|
||||||
|
current_toplevel=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||||
|
|
||||||
|
# Find the worktree path for the given branch, excluding the current one.
|
||||||
|
# Using substr to handle paths with spaces correctly.
|
||||||
|
git worktree list --porcelain | awk -v target="refs/heads/$branch" -v current="$current_toplevel" '
|
||||||
|
/^worktree / { wt = substr($0, 10) }
|
||||||
|
/^branch / && $2 == target {
|
||||||
|
if (wt != current) {
|
||||||
|
print wt
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
DIM='\033[2m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# if show_cl passed then also run git cl status. (chromium repos)
|
||||||
|
[[ "$1" == "--cl" || "$1" == "-cl" ]] && show_cl=true || show_cl=false
|
||||||
|
|
||||||
|
# if extra arg passed (eg `git recent remotename`), then list those remote branches, rather than local ones
|
||||||
|
[[ -n "$1" && "$show_cl" != true ]] && heads="refs/remotes/$1" || heads="refs/heads"
|
||||||
|
|
||||||
|
|
||||||
|
# fzf git inspiration:
|
||||||
|
# - https://github.com/junegunn/fzf/wiki/Examples#git
|
||||||
|
# - https://github.com/junegunn/fzf/wiki/Examples-(fish)#git
|
||||||
|
# - https://github.com/junegunn/fzf-git.sh (intense.)
|
||||||
|
|
||||||
|
# If there's a GIT_RECENT_QUERY environment variable, use it for non-interactive filtering. (Primarily added for testing: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/g/git-recent.rb#L41-L46)
|
||||||
|
filterarg=${GIT_RECENT_QUERY:+"--filter=$GIT_RECENT_QUERY"}
|
||||||
|
|
||||||
|
# Chromium hackers may want reference to their relevant CL.
|
||||||
|
CL_STATUS=$([ "$show_cl" = true ] && git cl status --fast --no-branch-color | grep 'https://' | sed 's| (.*||')
|
||||||
|
|
||||||
|
# List of branches currently checked out in any worktree (including the current one)
|
||||||
|
WT_BRANCHES=$(git worktree list --porcelain | grep "^branch " | sed 's|^branch refs/heads/||')
|
||||||
|
|
||||||
|
_browse_branches() {
|
||||||
|
git for-each-ref --sort=-authordate "$heads" --format="%(refname:short)" \
|
||||||
|
| while read -r branch_name; do
|
||||||
|
wt_symbol=""
|
||||||
|
if echo "$WT_BRANCHES" | grep -qFx "$branch_name"; then
|
||||||
|
wt_symbol=" ⇶"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$show_cl" != true ]; then
|
||||||
|
printf "$YELLOW%s$DIM%s$NC\n" "$branch_name" "$wt_symbol"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
review_url=$(echo "$CL_STATUS" | grep -E "\b${branch_name} :" | grep -o -E 'https://.*' | sed 's|https://||')
|
||||||
|
# Using fancy integrated hyperlinks: https://iterm2.com/feature-reporting/Hyperlinks_in_Terminal_Emulators.html
|
||||||
|
# TODO: maybe get rid of the crrev.com/c/ text as the link?
|
||||||
|
# TODO: use `git config branch.$(git rev-parse --abbrev-ref HEAD).gerritissue` and gerritserver to avoid using `git cl status`
|
||||||
|
printf "$YELLOW%s$DIM%s \033]8;;%s\a%s\033]8;;\a$NC\n" "$branch_name" "$wt_symbol" "https://$review_url" "$review_url"
|
||||||
|
done \
|
||||||
|
| fzf \
|
||||||
|
$filterarg --ansi -- --layout=reverse --multi --height=90% --min-height=20 \
|
||||||
|
--border-label-pos=2 --border-label '🌲 Branches' --border \
|
||||||
|
--no-hscroll --no-multi \
|
||||||
|
--preview-window='right,70%,border-left,border-rounded' --preview="$uniqcommits_cmd" --preview-label="Commits unique to branch" \
|
||||||
|
--header $'ENTER (checkout)\nCTRL-O (show branch diff)\nCTRL-Y (copy name to clipboard)' \
|
||||||
|
--bind 'preview-scroll-up:preview-up+preview-up+preview-up' \
|
||||||
|
--bind 'preview-scroll-down:preview-down+preview-down+preview-down' \
|
||||||
|
--bind "ctrl-y:execute-silent($copy_cmd)" \
|
||||||
|
--bind "ctrl-o:preview:$diffbranch_cmd"
|
||||||
|
}
|
||||||
|
output="$(_browse_branches)"
|
||||||
|
line_count=$(printf "%s" "$output" | wc -l)
|
||||||
|
|
||||||
|
if [[ -n "$output" ]] && (( line_count == 0 )); then
|
||||||
|
chosen_branch=$(echo "$output" | cut -d' ' -f1)
|
||||||
|
|
||||||
|
wt_path=$(_get_worktree_for_branch "$chosen_branch")
|
||||||
|
|
||||||
|
if [[ -n "$wt_path" ]]; then
|
||||||
|
echo "Branch '$chosen_branch' is open in worktree: $wt_path" >&2
|
||||||
|
echo "$wt_path"
|
||||||
|
else
|
||||||
|
echo "git checkout $chosen_branch" >&2
|
||||||
|
git checkout "$chosen_branch"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$output"
|
||||||
|
fi
|
||||||
|
|
|
||||||
62
git-recent-og
Executable file
62
git-recent-og
Executable file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
##
|
||||||
|
## git-recent
|
||||||
|
##
|
||||||
|
## list all local branches, sorted by last commit, formatted reall purdy
|
||||||
|
##
|
||||||
|
|
||||||
|
# Windows needs more basic format (#8, git-for-windows/git#865)
|
||||||
|
case $(uname -s) in
|
||||||
|
CYGWIN*|MINGW*|MSYS*)
|
||||||
|
branch='%(refname:short)'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
branch='%(color:yellow)%(refname:short)%(color:reset)'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
COUNT=0
|
||||||
|
while getopts "n:" opt; do
|
||||||
|
case ${opt} in
|
||||||
|
n )
|
||||||
|
if ! [[ $OPTARG =~ ^[0-9]{1,}$ ]]; then
|
||||||
|
echo "-n should be an integer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
COUNT=${OPTARG}
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND-1))
|
||||||
|
|
||||||
|
format="\
|
||||||
|
%(HEAD) \
|
||||||
|
$branch|\
|
||||||
|
%(color:bold red)%(objectname:short)%(color:reset) \
|
||||||
|
%(color:bold green)(%(committerdate:relative))%(color:reset) \
|
||||||
|
%(color:bold blue)%(authorname)%(color:reset) \
|
||||||
|
%(color:yellow)%(upstream:track)%(color:reset) \
|
||||||
|
%(contents:subject)"
|
||||||
|
|
||||||
|
lessopts="--tabs=4 --quit-if-one-screen --RAW-CONTROL-CHARS --no-init"
|
||||||
|
|
||||||
|
git for-each-ref \
|
||||||
|
--color=always \
|
||||||
|
--count=$COUNT \
|
||||||
|
--sort=-committerdate \
|
||||||
|
"refs/heads/" \
|
||||||
|
--format="$format" \
|
||||||
|
| column -ts '|' \
|
||||||
|
| less "$lessopts"
|
||||||
|
|
||||||
|
# The above command:
|
||||||
|
# for all known branches,
|
||||||
|
# (force coloring on this, especially since it's being piped)
|
||||||
|
# optionally, specify the number of branches you want to display
|
||||||
|
# sort descending by last commit
|
||||||
|
# show local branches (change to "" to include both local + remote branches)
|
||||||
|
# apply the formatting template above
|
||||||
|
# break into columns
|
||||||
|
# use the pager only if there's not enough space
|
||||||
144
git-watch
Executable file
144
git-watch
Executable file
|
|
@ -0,0 +1,144 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# git-watch: A streaming perspective of git changes and commits.
|
||||||
|
#
|
||||||
|
# Monitors the current repository for:
|
||||||
|
# - New commits (displays 'git show')
|
||||||
|
# - Working directory & index changes (displays 'git diff HEAD' and untracked files)
|
||||||
|
#
|
||||||
|
# Requires 'watchexec' for responsiveness.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Check if we're in a git repo
|
||||||
|
[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ] || {
|
||||||
|
echo "Error: Not a git repository." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for dependencies
|
||||||
|
if ! command -v watchexec >/dev/null 2>&1; then
|
||||||
|
echo "Error: 'watchexec' is required for git-watch. Please install it (e.g., 'brew install watchexec')." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
||||||
|
echo "git-watch: A streaming perspective of git changes and commits."
|
||||||
|
echo
|
||||||
|
echo "Usage: git-watch [--debug]"
|
||||||
|
echo
|
||||||
|
echo "Options:"
|
||||||
|
echo " --debug Log what would be run instead of running it"
|
||||||
|
echo
|
||||||
|
echo "Features:"
|
||||||
|
echo " - Shows 'git show' on new commits"
|
||||||
|
echo " - Shows 'git diff HEAD' on working tree/index changes"
|
||||||
|
echo " - Uses 'watchexec' for high-speed responsiveness"
|
||||||
|
echo " - Uses your fancy pager (delta/diff-so-fancy) without blocking"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEBUG_MODE=false
|
||||||
|
if [[ "$1" == "--debug" || "$2" == "--debug" ]]; then
|
||||||
|
DEBUG_MODE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pager detection (compatible with delta, diff-so-fancy, etc.)
|
||||||
|
pager_cmd=$(command -v delta || command -v diff-so-fancy)
|
||||||
|
if [ -n "$pager_cmd" ]; then
|
||||||
|
if [[ "$pager_cmd" == *"delta"* ]]; then
|
||||||
|
pager_cmd="$pager_cmd --paging=never"
|
||||||
|
fi
|
||||||
|
pipe_to_pager=" | $pager_cmd"
|
||||||
|
fi
|
||||||
|
|
||||||
|
git_run() {
|
||||||
|
if [ "$DEBUG_MODE" = true ]; then
|
||||||
|
echo -e "\033[0;35m[DEBUG] Would run: git --no-pager \"$@\" --color=always$pipe_to_pager\033[0m"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
eval "git --no-pager \"\$@\" --color=always$pipe_to_pager"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cross-platform hashing for change detection
|
||||||
|
hash_cmd=$(command -v shasum || command -v sha1sum || echo "cksum")
|
||||||
|
|
||||||
|
update() {
|
||||||
|
local mode="$1"
|
||||||
|
local git_dir state_file
|
||||||
|
git_dir=$(git rev-parse --git-dir 2>/dev/null)
|
||||||
|
[ -n "$git_dir" ] || return
|
||||||
|
state_file="$git_dir/watch-state"
|
||||||
|
|
||||||
|
local current_head last_head current_state_hash last_state_hash
|
||||||
|
current_head=$(git rev-parse HEAD 2>/dev/null)
|
||||||
|
|
||||||
|
# Capture status and diff for hashing (ignoring untracked files for now)
|
||||||
|
local status_output=$(git status --porcelain --untracked-files=no)
|
||||||
|
local diff_output=$(git diff HEAD 2>/dev/null)
|
||||||
|
# TODO: Re-evaluate untracked file support later.
|
||||||
|
current_state_hash=$( (echo "$status_output"; echo "$diff_output") | $hash_cmd | cut -d' ' -f1)
|
||||||
|
|
||||||
|
if [[ -f "$state_file" && "$mode" != "--initial" ]]; then
|
||||||
|
read -r last_head last_state_hash < "$state_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEBUG_MODE" = true ]; then
|
||||||
|
echo -e "\033[0;35m[DEBUG] Mode: $mode\033[0m"
|
||||||
|
echo -e "\033[0;35m[DEBUG] HEAD: $current_head (Last: ${last_head:-NONE})\033[0m"
|
||||||
|
echo -e "\033[0;35m[DEBUG] State Hash: $current_state_hash (Last: ${last_state_hash:-NONE})\033[0m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local did_something=false
|
||||||
|
|
||||||
|
# 1. Commit detection
|
||||||
|
if [[ "$current_head" != "$last_head" || "$mode" == "--initial" ]]; then
|
||||||
|
echo -e "\n\033[1;34m=== Current Commit: $(git log -1 --format='%h %s' 2>/dev/null) ===\033[0m"
|
||||||
|
git_run show --summary --stat -p
|
||||||
|
did_something=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Working directory detection
|
||||||
|
if [[ "$current_state_hash" != "$last_state_hash" || "$mode" == "--initial" ]]; then
|
||||||
|
if [[ -n "$status_output" ]]; then
|
||||||
|
echo -e "\n\033[1;32m=== Changes ===\033[0m"
|
||||||
|
|
||||||
|
# Show diff of tracked changes
|
||||||
|
if [[ -n "$diff_output" ]]; then
|
||||||
|
git_run diff HEAD
|
||||||
|
fi
|
||||||
|
|
||||||
|
# TODO: Implement surgical untracked file support here if needed.
|
||||||
|
did_something=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update state so we don't repeat this in the next --step
|
||||||
|
if [[ "$did_something" == "true" ]]; then
|
||||||
|
if [ "$DEBUG_MODE" = true ]; then
|
||||||
|
echo -e "\033[0;35m[DEBUG] Would update state file: $current_head $current_state_hash\033[0m"
|
||||||
|
else
|
||||||
|
echo "$current_head $current_state_hash" > "$state_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Internal flag for watchexec to call back into the script
|
||||||
|
if [[ "$1" == "--step" ]]; then
|
||||||
|
update "--step"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Main execution loop
|
||||||
|
update "--initial"
|
||||||
|
|
||||||
|
pass_debug=""
|
||||||
|
if [ "$DEBUG_MODE" = true ]; then
|
||||||
|
pass_debug="--debug"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\033[0;32mWatching with watchexec...\033[0m"
|
||||||
|
# --postpone: don't run immediately on start (we already did update --initial)
|
||||||
|
# --quiet: suppress watchexec status messages
|
||||||
|
# exec is used so that Ctrl-C goes directly to watchexec and terminates the session
|
||||||
|
exec watchexec --quiet --postpone -i .git -w . -w "$(git rev-parse --git-dir)/HEAD" -- "$0" --step $pass_debug
|
||||||
41
man/man1/git-recent.1
Normal file
41
man/man1/git-recent.1
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
.TH GIT-RECENT 1 "February 8, 2025" "git-recent 2.0"
|
||||||
|
.SH NAME
|
||||||
|
git recent \- Browse and checkout recently used Git branches.
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B git recent
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B git recent
|
||||||
|
provides an interactive interface (using
|
||||||
|
.I fzf
|
||||||
|
) to browse your latest local git branches. It displays a list of branches sorted by recency, shows the unique commits for each branch, and allows you to checkout a branch with
|
||||||
|
.B Enter\fR.
|
||||||
|
You can also view the diff of a branch against the main/master branch.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B --help
|
||||||
|
Display a help message.
|
||||||
|
.SH KEYBINDINGS
|
||||||
|
.TP
|
||||||
|
.B Enter
|
||||||
|
Checkout the selected branch. If the branch is already open in a separate git worktree, the script will output the path to jump there. (See README for automatic switching via a small shell wrapper).
|
||||||
|
.TP
|
||||||
|
.B Up/Down arrow keys
|
||||||
|
Navigate the branch list.
|
||||||
|
.TP
|
||||||
|
.B Type
|
||||||
|
Filter branch list with fuzzy text matching
|
||||||
|
.TP
|
||||||
|
.B Ctrl-o
|
||||||
|
Show the diff of the selected branch against the main/master branch.
|
||||||
|
.TP
|
||||||
|
.B Ctrl-C
|
||||||
|
Exit.
|
||||||
|
.SH INSTALLATION
|
||||||
|
.I fzf
|
||||||
|
is required. https://junegunn.github.io/fzf/installation/
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.I fzf(1), git(1), git-open(1), diff-so-fancy(1), delta(1)
|
||||||
|
.br
|
||||||
|
.I git-recent-og
|
||||||
|
(for the non-fzf version)
|
||||||
17
package-lock.json
generated
Normal file
17
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "git-recent",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "git-recent",
|
||||||
|
"version": "2.2.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"git-recent": "git-recent",
|
||||||
|
"git-recent-og": "git-recent-og"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
package.json
10
package.json
|
|
@ -1,16 +1,18 @@
|
||||||
{
|
{
|
||||||
"name": "git-recent",
|
"name": "git-recent",
|
||||||
"version": "1.0.4",
|
"version": "2.2.0",
|
||||||
"description": "List recent git branches, formatted so fancy",
|
"description": "List recent git branches, select one for checkout. See branch commits and diffs, all formatted so fancy",
|
||||||
"bin": {
|
"bin": {
|
||||||
"git-recent": "git-recent"
|
"git-recent": "git-recent",
|
||||||
|
"git-recent-og": "git-recent-og",
|
||||||
|
"git-watch": "git-watch"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/paulirish/git-recent.git"
|
"url": "https://github.com/paulirish/git-recent"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"git",
|
"git",
|
||||||
|
|
|
||||||
91
readme.md
91
readme.md
|
|
@ -1,24 +1,87 @@
|
||||||
# git-recent
|
# git recent
|
||||||
|
|
||||||
Type `git recent` to see your latest local git branches
|
Speedily browse your latest local git branches, `checkout` with Enter. Also view branches unique commits, and optionally the diff against main.
|
||||||
|
|
||||||
## Usage
|
[Demo video of git recent 2.0](https://github.com/user-attachments/assets/441f0b9b-8469-41bd-a826-0bb0cd7c7de8)
|
||||||
|
|
||||||
git recent
|
`git recent` now offers an interactive UI (thx to [fzf](https://github.com/junegunn/fzf)) for browsing recent branches, seeing differences, and `checkout`'ing your selection.
|
||||||
|
|
||||||
|
See a diff of your branch vs main with `ctrl-o`. If you have [delta](https://dandavison.github.io/delta//) it'll use that for formatting, but will fallback, too.
|
||||||
|
|
||||||

|
If you're like me, mostly using classic git commands, then `git recent` provides a nice upgrade for browsing/selecting recent branches. But if you're a TUI fan using [git-fuzzy](https://github.com/bigH/git-fuzzy), [`lazygit`](https://github.com/jesseduffield/lazygit) or [`tig`](https://jonas.github.io/tig/) or [`fzf-git`](https://github.com/junegunn/fzf-git.sh), well… this probably isn't an upgrade. :p (But you can certainly [read the source](/git-recent) quickly!)
|
||||||
|
|
||||||
If you're a Windows user, you need to use [Git Bash](https://git-scm.com/downloads) or similar shell in order to effectively use this utility.
|
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
You can add the `git-recent` location to your path (e.g. add the directory to your `PATH` environment
|
`fzf` is required for 2.0. TBH, it's a fantastic tool; those [shell key bindings](https://junegunn.github.io/fzf/shell-integration/) are _delightful_. That said, if you're dependency-averse, the older version below, [`git recent-og`](#git-recent-og), may be for you.
|
||||||
or copy `git-recent` into an existing included path like `/usr/local/bin` or `~/bin/`).
|
|
||||||
|
|
||||||
You can use also `npm` to install the global binary:
|
* Mac: `brew install fzf`
|
||||||
|
* Linux: `sudo apt-get install fzf`
|
||||||
|
* Windows: `choco install fzf`
|
||||||
|
|
||||||
npm install --global git-recent
|
Then, do one of these:
|
||||||
|
|
||||||
|
* Manual: Grab the `git-recent` script from this repo and put it anywhere in your `$PATH`. Run `chmod +x git-recent`.
|
||||||
|
* Via NPM: `npm install --global git-recent`
|
||||||
|
* Homebrew: `brew install git-recent`
|
||||||
|
|
||||||
|
<!-- homebrew formula: https://github.com/Homebrew/homebrew-core/blob/master/Formula/g/git-recent.rb -->
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
git recent
|
||||||
|
|
||||||
|
Hit `Enter` to checkout the selected branch.
|
||||||
|
|
||||||
|
Type or use arrow keys to navigate your list of branches.
|
||||||
|
|
||||||
|
Hit `ctrl-o` to see the branch diff.
|
||||||
|
|
||||||
|
#### Worktree Support
|
||||||
|
|
||||||
|
If a branch is already open in a separate git worktree, `git recent` will detect it and provide instructions to `cd` there. To enable **automatic** directory switching, add one of these wrapper functions to your shell config:
|
||||||
|
|
||||||
|
##### Bash / Zsh (in `.bashrc` or `.zshrc`)
|
||||||
|
```bash
|
||||||
|
recent() {
|
||||||
|
local o=$(command git-recent "$@")
|
||||||
|
[ -d "$o" ] && cd "$o" || [ -n "$o" ] && echo "$o"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Fish (in `~/.config/fish/config.fish`)
|
||||||
|
```fish
|
||||||
|
function recent
|
||||||
|
set -l o (command git-recent $argv)
|
||||||
|
if test -d "$o"
|
||||||
|
cd $o
|
||||||
|
else if test -n "$o"
|
||||||
|
echo $o
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
# `git recent-og`
|
||||||
|
|
||||||
|
`git recent-og` is the [OG](https://www.urbandictionary.com/define.php?term=OG) `git recent`, released back in 2016. Now it's been renamed to `git recent-og`.
|
||||||
|
|
||||||
|
git recent-og
|
||||||
|
|
||||||
|
Optionally, add `-n<int>` to see the most recent `<n>` branches
|
||||||
|
|
||||||
|
git recent-og -n5
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If you're a Windows user, you need to use [Git Bash](https://git-scm.com/downloads) or similar shell in order to effectively use this utility.
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Install with `npm` or `brew` as above, or copy `git-recent-og` script into an existing path like `/usr/local/bin` or `~/bin/`.
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
## If you like this you may also be interested in...
|
## If you like this you may also be interested in...
|
||||||
|
|
||||||
|
|
@ -29,8 +92,10 @@ You can use also `npm` to install the global binary:
|
||||||
|
|
||||||
Copyright Paul Irish. Licensed under MIT.
|
Copyright Paul Irish. Licensed under MIT.
|
||||||
|
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
- **2016-05-16** - added to [paulirish/dotfiles](https://github.com/paulirish/dotfiles/commit/1ca1ff760832af558447145fa2a367046b1829d2)
|
- **2025-02** - 2.0 upgrade with fzf integration. 1.0 binary is now available as `git recent-og`.
|
||||||
- **2016-08-05** - released in standalone repo and published to npm
|
- **2019-06** - Last bugfix for 1.0 landed. Been stable since then.
|
||||||
|
- **2018-10** - Added count `-n` parameter
|
||||||
|
- **2016-08** - released in standalone repo and published to npm
|
||||||
|
- **2016-05** - added to [paulirish/dotfiles](https://github.com/paulirish/dotfiles/commit/1ca1ff760832af558447145fa2a367046b1829d2)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue