Skip to content

Instantly share code, notes, and snippets.

@willnationsdev
Created May 30, 2025 18:24
Show Gist options
  • Save willnationsdev/27fec8f258a8d2fbdefde3f6913bedfe to your computer and use it in GitHub Desktop.
Save willnationsdev/27fec8f258a8d2fbdefde3f6913bedfe to your computer and use it in GitHub Desktop.
Helpful custom git commands. Place these in your user profile's `.mygit` directory & ensure it is in your PATH.
#!/bin/sh
# Uncommits last change while preserving changes.
# Follow up with `git recommit` (after staging) if you want to commit again with an identical commit message.
#
# Note: Others often term this `git undo` or similar, but to support OPTIONAL compatibility
# with git-branchless which has a far more robust `git undo`, I use `git back`.
git reset --soft HEAD^
#!/bin/bash
# Source: `https://phelipetls.github.io/posts/favorite-custom-git-commands/`
# Requires installation of `fzf` from `https://github.com/junegunn/fzf`. Example: `[choco|scoop|winget] install fzf`.
set -eu
branch=$(git recent -S | fzf --height 40% --preview 'git log --date=short --pretty="%h %s %ad" {}')
if [[ -n "$branch" ]]; then
git checkout "$branch"
fi
#!/bin/sh
# A shortcut for printing a detailed, global git graph, similar to most git GUIs.
#
# The `%C(auto)` bit ensures that, if you have poshified syntax highlighting
# from e.g. installing the `posh-git` module for PowerShell (`pwsh`), you still
# get those highlights applied for the output.
git log --graph --all --tags --decorate --abbrev-commit --date=short --pretty='%C(auto)%h %d %s %C(red)%ad %C(bold blue)%an'
#!/usr/bin/env perl
# Sourced from `https://github.com/mfontani/git-recent`.
# willnationsdev: This script has been modified to generate the help text
# explicitly, otherwise it does not work when run from `pwsh` on Windows.
use strict;
use warnings;
use Getopt::Std qw<getopts>;
# Copyright 2018 Marco Fontani <[email protected]>
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
$Getopt::Std::STANDARD_HELP_VERSION = 1;
our $VERSION = '0.2';
{
my %opts;
getopts('sASh', \%opts) or exit 1;
if ($opts{h}) {
print <<'HELP';
Usage:
git recent [-s] [-A] [-S] [N]
DESCRIPTION
git-recent Shows N (default to 10) "unique recent branches" the user
has moved to/from, from the most recently moved to to the least recently moved
to, in the format:
BRANCH_NAME [whitespace] SOURCE
whereby SOURCE is whence the branch came from: from the reflog, or from
the list of all local branches. Use -S if you don't want the SOURCE to be
shown.
You can use N=0 (i.e. git recent 0) to not restrict the output, and have
it show all branches in order:
- Branches and SHAs from the reflog, from most recently to least recently checked
out.
- All other local branches
OPTIONS
-s Skip showing SHA-like names (only show actual branch names)
-A Do NOT show branches from local branch list; show reflog only
-S Suppress the SOURCE column (just show branch names)
-h Show this help message
EXAMPLE USAGE
git recent -s | fzf | awk '{ print $1 }' | xargs git checkout
HELP
exit 0;
}
my $HOW_MANY = shift // 10; # 0 for "all"
my @branches = branches_from_reflog();
@branches = with_all_branches(@branches) if !$opts{A};
@branches = grep { $_->[0] !~ /^[a-f0-9]{11,40}$/ } @branches
if $opts{s};
@branches = @branches[0..$HOW_MANY-1]
if $HOW_MANY;
@branches = grep { defined && length $_->[0] } @branches;
exit 0 if !@branches;
if (!$opts{S}) {
my $max = max_length(map { $_->[0] } @branches) + 1;
printf "%-${max}s%s\n", @$_ for @branches;
exit 0;
}
print "$_->[0]\n" for @branches;
exit 0;
}
sub max_length {
my $max = 0;
for (@_) {
$max = length if length > $max;
}
return $max;
}
sub branches_from_reflog {
my @lines = qx!git reflog 2>&1!;
if (scalar @lines == 1 && $lines[0] =~ m!Fatal!) {
warn $lines[0];
exit 1;
}
my %exists;
my %branches;
my @recent;
for my $line (@lines) {
chomp $line;
next if $line !~ /checkout:[ ]moving[ ]from[ ](?<branch_from>\S+)[ ]to[ ](?<branch_to>\S+)/xms;
my ($from, $to) = ($+{branch_from}, $+{branch_to});
$exists{$from} //= qx!git rev-parse --verify $from 2>/dev/null!;
$exists{$to} //= qx!git rev-parse --verify $to 2>/dev/null!;
$branches{$from}++ if exists $exists{$from};
$branches{$to}++ if exists $exists{$to};
push @recent, [ $to, "git reflog 'to' - $line" ]
if $exists{$to}
&& !scalar grep { $_->[0] eq $to } @recent;
push @recent, [ $from, "git reflog 'from' - $line" ]
if $exists{$from}
&& !scalar grep { $_->[0] eq $from } @recent;
}
return @recent;
}
sub with_all_branches {
my (@branches) = @_;
my @all_branches = map { s!\A\s*[*]?\s*!!xms; $_ } qx!git branch!;
for my $branch (@all_branches) {
chomp $branch;
push @branches, [ $branch, "git branch - $branch" ]
if !scalar grep { $_->[0] eq $branch } @branches;
}
return @branches;
}
__END__
=head1 NAME
git-recent [-s] [-A] [-S] [N]
=head1 DESCRIPTION
C<git-recent> Shows C<N> (default to C<10>) "unique recent branches" the user
has moved to/from, from the most recently moved to to the least recently moved
to, in the format:
BRANCH_NAME [whitespace] SOURCE
... whereby C<SOURCE> is whence the branch came from: from the reflog, or from
the list of all local branches. Use C<-S> if you don't want the C<SOURCE> to be
shown.
You can use C<N=0> (i.e. C<git recent 0>) to not restrict the output, and have
it show all branches in order:
=over 4
=item *
Branches and SHAs from the reflog, from most recently to least recently checked
out.
=item *
All other local branches
=back
Use the C<-s> option to B<skip> showing (plausible) SHAs which were checked
out, and only show actual branches.
This will B<not> work the way you expect if you named a branch using the same
set of characters (hex, C<a-f0-9>) and the same length (C<40>) that a SHA has.
Use the C<-A> option to NOT show branches from the local branch list, and only
show branches from the reflog.
=head1 How to use
Useful as a Git alias in C<~/.gitconfig>, used in conjunction with C<fzf>.
C<cor> stands for "check out recent":
[alias]
cor = !git checkout $( git recent 0 -s | fzf +s | awk '{ print $1 }' )
corsha = !git checkout $( git recent 0 | fzf +s | awk '{ print $1 }' )
#!/bin/sh
# Source: `https://phelipetls.github.io/posts/favorite-custom-git-commands/`.
git commit --reedit-message ORIG_HEAD
#!/bin/sh
# Source: `https://github.com/nvie/git-toolbelt/blob/main/git-spinoff`.
set -eu
#
# Inspired by Magit's super useful `magit-branch-spinoff` command.
# See also https://magit.vc/manual/magit/Branch-Commands.html
#
usage () {
echo "usage: git spinoff [-h] <new-name> [<base>]" >&2
echo >&2
echo "Creates and checks out a new branch starting at and tracking the" >&2
echo "current branch. That branch in turn is reset to the last commit it" >&2
echo "shares with its upstream. If the current branch has no upstream or no" >&2
echo "unpushed commits, then the new branch is created anyway and the" >&2
echo "previously current branch is not touched." >&2
echo >&2
echo "This is useful to create a feature branch after work has already" >&2
echo "began on the old branch (likely but not necessarily \"main\")." >&2
echo >&2
echo "Options:" >&2
echo "-h Show this help" >&2
}
while getopts h flag; do
case "$flag" in
h) usage; exit 2;;
esac
done
shift $(($OPTIND - 1))
if [ $# -lt 1 ] || [ $# -gt 2 ]; then
usage
exit 2
fi
new_name="$1"
rawbase="${2:-}"
if [ -z "$rawbase" ]; then
base="$(git current-branch)"
else
base="$rawbase"
fi
base_sha="$(git sha -s "$base")"
#
# NOTE:
# The flag -B is the transactional equivalent of
# $ git branch -f <branch> [<start point>]
# $ git checkout <branch>
#
git checkout -q --track -B "$new_name" "$base"
rtb="$(git remote-tracking-branch "$base")"
if [ -n "$rtb" ]; then
merge_base="$(git merge-base "$base" "$rtb")"
git branch -vf "$base" "$merge_base"
fi
if [ "$(git sha -s "$base")" != "$base_sha" ]; then
echo "$base reset to $(git sha -s "$base") (was $base_sha)"
else
echo "$base not touched"
fi
#!/bin/sh
# Prints the detailed message attached to an annotated tag.
# Usage: `git tag-msg <tagname>`
git tag -l --format='%(contents)' $1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment