Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 35 additions & 35 deletions git-fixup
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ grok_diff='/^--- .*/p ;
# Produce suggestion of commits by finding the sections of files with changes
# staged (U1 to diff is used to give some context for when adding items to
# lists etc) and looking up the previous commits touching those sections.
function fixup_candidates_lines () {
fixup_candidates_lines () {
git diff --cached -U1 --no-prefix | sed -n "$grok_diff" | (
file=''
while read -r offs len ; do
if [ "$offs" = '---' ] ; then
if test "$offs" = '---'; then
file="$len"
else
if [ "$len" != '0' ] ; then
if [ "$file" != '/dev/null' ] ; then
if test "$len" != '0'; then
if test "$file" != '/dev/null' ; then
git blame -sl -L "$offs,+$len" "$rev_range" -- "$file"
fi
fi
Expand All @@ -51,7 +51,7 @@ function fixup_candidates_lines () {

# Produce suggestion of commits by taking the latest commit to each file with
# staged changes
function fixup_candidates_files () {
fixup_candidates_files () {
git diff --cached --name-only | (
while read -r file; do
git rev-list -n 1 -E --invert-grep --grep='^(fixup|squash)' "$rev_range" -- "$file"
Expand All @@ -60,24 +60,24 @@ function fixup_candidates_files () {
}

# Produce suggestion of all commits in $rev_range
function fixup_candidates_all_commits () {
fixup_candidates_all_commits () {
git rev-list "$rev_range" | sed 's/^/F /g'
}

# Pretty print details of a commit
function print_sha () {
print_sha () {
local sha=$1
local type=$2

git --no-pager log --format="%H [$type] %s <%ae>" -n 1 "$sha"
}

# Call git commit
function call_commit() {
call_commit () {
local flag=$op
local target=$1

if [ "$op" = "amend" ] ; then
if test "$op" = "amend"; then
flag=fixup
target="amend:$target"
fi
Expand All @@ -87,43 +87,43 @@ function call_commit() {
}

# Call git rebase
function call_rebase() {
call_rebase () {
local target=$1

# If our target-commit has a parent, we call a rebase with that
# shellcheck disable=SC1083
if git rev-parse --quiet --verify "$target"~1^{commit} ; then
if git rev-parse --quiet --verify "$target"~1^{commit}; then
git rebase --interactive --autosquash "$target~1"
# If our target-commit exists but has no parents, it must be the very first commit
# the repo. We simply call a rebase with --root
elif git rev-parse --quiet --verify "$target"^{commit} ; then
elif git rev-parse --quiet --verify "$target"^{commit}; then
git rebase --interactive --autosquash --root
fi
}

# Print list of fixup/squash candidates
function print_candidates() {
print_candidates () {
(
if [ "$show_all" = "false" ] ; then
if [ "$show_all" = "false" ]; then
fixup_candidates_lines
fixup_candidates_files
else
fixup_candidates_all_commits
fi
) | sort -uk2 | while read -r type sha; do
if [ -n "$sha" ] ; then
) | sort -uk2 | while read -r type sha; do
if test -n "$sha"; then
print_sha "$sha" "$type"
fi
done
}

function fallback_menu() {
fallback_menu () {
(
IFS=$'\n'
read -d '' -ra options
PS3="Which commit should I $op? "
select line in "${options[@]}"; do
if [ -z "$line" ] ; then
if test -z "$line"; then
declare -a args=("$REPLY")
case ${args[0]} in
quit|q)
Expand All @@ -132,7 +132,7 @@ function fallback_menu() {
;;
show|s)
idx=$((args[1] - 1))
if [ "$idx" -ge 0 ] ; then
if test "$idx" -ge 0; then
git show "${options[$idx]%% *}" >&2
fi
;;
Expand All @@ -157,7 +157,7 @@ function fallback_menu() {
}

show_menu () {
if [ -n "$fixup_menu" ] ; then
if test -n "$fixup_menu"; then
eval command "$fixup_menu"
else
fallback_menu
Expand All @@ -173,7 +173,7 @@ create_commit=${GITFIXUPCOMMIT:-$(git config --default=false --type bool fixup.c
base=${GITFIXUPBASE:-$(git config --default="" fixup.base)}
show_all=false

while [ $# -gt 0 ] ; do
while test $# -gt 0; do
case "$1" in
-s|--squash)
op="squash"
Expand Down Expand Up @@ -201,7 +201,7 @@ while [ $# -gt 0 ] ; do
;;
-b|--base)
shift
if [ $# -eq 0 ] ; then
if [ $# -eq 0 ]; then
die "--base requires an argument"
fi
base="$1"
Expand All @@ -218,58 +218,58 @@ while [ $# -gt 0 ] ; do
done

target="$1"
if [ $# -gt 1 ] ; then
if test $# -gt 1; then
die "Pass only one ref, please"
fi

if [ -n "$target" ] ; then
if test -n "$target"; then
call_commit "$target"
if [ "$rebase" = "true" ] ; then
if test "$rebase" = "true"; then
call_rebase "$target"
fi
exit
fi

if git diff --cached --quiet ; then
if git diff --cached --quiet; then
die 'No staged changes. Use git add -p to add them.'
fi

cd_to_toplevel

if [ "$base" = "closest" ] ; then
if test "$base" = "closest"; then
base=$(git for-each-ref \
--merged HEAD~1 \
--sort=-committerdate \
refs/heads/ \
--count 1 \
--format='%(objectname)' \
refs/heads/ \
)
if [ -z "$base" ] ; then
if test -z "$base"; then
die "Could not find the ancestor branch"
fi
fi

if [ -z "$base" ] ; then
if test -z "$base"; then
upstream=$(git rev-parse "@{upstream}" 2>/dev/null)
head=$(git rev-parse HEAD 2>/dev/null)
if [ -n "$upstream" ] && [ "$upstream" != "$head" ] ; then
if test -n "$upstream" && test "$upstream" != "$head"; then
base="$upstream"
fi
fi

if [ -n "$base" ] ; then
if test -n "$base"; then
rev_range="$base..HEAD"
else
rev_range="HEAD"
fi

if [ "$create_commit" = "true" ] ; then
if test "$create_commit" = "true"; then
target=$(print_candidates | show_menu)
if [ -z "$target" ] ; then
if test -z "$target"; then
exit
fi
call_commit "${target%% *}"
if [ "$rebase" = "true" ] ; then
if test "$rebase" = "true"; then
call_rebase "${target%% *}"
fi
else
Expand Down
Loading