2

When I do:

git pull --rebase --autostash

Sometimes I get a message that there was a conflict applying the stash and I'll need to merge it manually.

What's concerning to me is that the exit status is 0.

How do I get a non-zero exit status if the autostash didn't reapply cleanly?

2
  • Related: stackoverflow.com/questions/29517440/… Commented Sep 27, 2018 at 13:23
  • I'd suggest reporting this as a bug (well, first make sure you're on the most recent Git, 2.19-ish). I really dislike autostash though—it makes more sense to just commit, then rebase including the commit. You can do a soft or mixed reset (or two) as needed to restore the index and branch tip.
    – torek
    Commented Sep 27, 2018 at 15:40

2 Answers 2

1

With non-zero exit code you cannot distinguish pull error from stash pop error.

My advice is to avoid autostash. It seems convenient when it works but is problematic when it doesn't. And if you do things like

git stash push
git pull --rebase
git stash pop

you can create a bash script or a git alias:

git alias.pull-autostash '!git stash push && git pull --rebase && git stash pop'

Usage:

git pull-autostash
3
  • How to avoid No local changes to save and then poping something else? See: git stash push --allow-empty
    – Tom Hale
    Commented Sep 29, 2018 at 12:52
  • I actually recommend avoiding git pull entirely, for the same reason you recommend avoiding --autostash...
    – torek
    Commented Sep 29, 2018 at 16:16
  • @torek Yep, I saw that advice in many of your answers. Thanks!
    – phd
    Commented Sep 29, 2018 at 17:19
1

My script to work around this issue is non-trivial. I post it here to both help others and in the hope for comments for improvement.

Call this ~/bin/git-pull-autostash and invoke it as git pull-autostash:


#!/bin/bash

# Work around 0 exit if autostash doesn't apply
# https://stackoverflow.com/questions/52538050/exit-status-is-0-but-autostash-requires-manual-merging
# Work around 0 exit if no stash is created
# https://stackoverflow.com/questions/52568548/git-stash-exits-0-but-no-stash-created

set -euo pipefail

if [ -z "$(git status --porcelain  --untracked-files=no)" ]; then
  # Working directory and index are clean
  git pull --rebase "$@"
  exit 0
fi

# If we get to here, a stash is required.

exit_code_autostash_unpopped=4
exit_code_autostash_error=70  # https://unix.stackexchange.com/a/254747/143394

stash_msg="pull-autostash on $(git rev-parse --short @)"

get_stash_top() {
  local top
  if top=$(git rev-parse stash@\{0\} 2>/dev/null); then
    echo "$top"
  fi
  # Null output if there is no stash
}

prev_stash_top=$(get_stash_top)
git stash push -m "$stash_msg" > /dev/null
new_stash_top=$(get_stash_top)
stash_desc="${new_stash_top:0:7}: \"$stash_msg\""

# Minimise race conditions - have trap function ready before it is required
warn_pop_required() {
  local exit_code=$?  # Non-zero if invoked from trap
  if [[ -n ${pop_required-} ]]; then
    git stash list >&2
    printf '\nWARNING: autostash %s remains to be applied\n' "$stash_desc" >&2
    exit "$exit_code_autostash_unpopped"
  fi
  exit "$exit_code"
}
trap warn_pop_required EXIT


if [[ $new_stash_top != "$prev_stash_top" ]]; then
  # A stash was created
  pop_required=true  # flag for trap function
  printf 'Created stash %s\n' "$stash_desc"
fi


pop_stash() {
  local exit_code=$?
  if [[ $(get_stash_top) != "$new_stash_top" ]]; then
    printf 'WARNING: autostash %s is no longer at the top of the stack\n' "$stash_desc" >&2
    exit "$exit_code_autostash_error"
  else
    git stash pop --quiet
    unset pop_required
    printf 'Successfully popped stash %s\n' "$stash_desc"
  fi
  if [[ $exit_code -ne 0 ]]; then  # We were called from a signal
    exit "$exit_code"
  fi
}
trap pop_stash INT ERR TERM QUIT  # Pop stash on termination during pull

git pull --no-autostash --rebase "$@"
trap - INT ERR TERM QUIT  # Don't try to pop stash twice
pop_stash

Not the answer you're looking for? Browse other questions tagged or ask your own question.