Loading git-change-commit 0 → 100755 +146 −0 Original line number Diff line number Diff line #!/bin/bash OPTS_SPEC="\ git change-commit [-h] [--start|--continue|--reset|--abort] [REVISION] [[--] PATH [PATH]] change-commit does an interactive rebase keeping the current changes in the working directory, allowing edits to be made to commits using changes made in the working directory. If more than one revision is given, only the first will be used. This comes with the standard warning: DO NOT CHANGE COMMITS THAT HAVE BEEN (OR COULD HAVE BEEN) PUSHED OR FETCHED TO OTHER REPOSITORIES OUTSIDE OF YOUR CONTROL. (Unless you really, *really* know what you are doing.) -- h,help show this message and exit start begin the rebase, this is the default action and is optional. Requires: REVISION reset reset the working directory to the state it was in before starting. Optional: REVISION, PATH... continue continue to the next commit marked for editing abort quit the changes and return to the originally checked out commit/head " parse() { eval "$(git rev-parse --parseopt -- "$@" <<< "$OPTS_SPEC" || echo return $?)"; } die() { EXIT_STATUS=${EXIT_STATUE-$?} [ "x$1" = x--usage ] && parse --help echo "$*" exit $EXIT_STATUS } >&2 error() { printf "WARNING: %s\n" "$*"; } >&2 REPO_DIR=`git rev-parse --git-dir` || die STASH_FILE=$REPO_DIR/change-commit/stash STASH_FILE_BACKUP=${STASH_FILE}_pref is_active() { [ -d $REPO_DIR/rebase-merge ]; } is_ours() { [ -e $STASH_FILE ]; } confirm() { local prompt=$1 ans while true; do read -p "$prompt (y|N) " ans case $ans in y*|Y*) return 0 ;; n*|N*|'') return 1 ;; *) prompt="Sorry, what did you mean?" ;; esac done } action_start() { local stash=$(git stash create) || die [ -n "$REVISION" ] || die "need a revision to edit" git reset --hard git rebase -i $REVISION~ 2>/dev/null mkdir -p $(dirname $STASH_FILE) if ! echo "$stash" > $STASH_FILE; then error "could not write stash ID to $STASH_FILE, aborting" git rebase --abort fi cp $STASH_FILE $STASH_FILE_BACKUP [ -n "$stash" ] && git stash apply $stash } action_continue() { local stash=$(git stash create) || die echo "$stash" >> $STASH_FILE || die git reset --hard git rebase --continue || die [ -n "$stash" ] && git stash apply $stash is_active || rm $STASH_FILE } action_reset() { local stash=$(head -n1 $STASH_FILE_BACKUP) || die if [ ${#PATHS[*]} -gt 0 ]; then git checkout ${REVISION:-$stash} -- "${PATHS[@]}" elif [ -n "$REVISION" ]; then error "This will reset the working directory to match $REVISION, are" \ "you sure this is what you intended?" error "You will still be able to get the original working directory" \ "by running --reset without any further arguments." confirm "Reset to ${REVISION::10}?" || return 0 local saved_head=$(git symbolic-ref -q HEAD || cat $REPO_DIR/HEAD) git reset --hard HEAD git checkout $REVISION git reset $saved_head else git reset --hard [ -n "$stash" ] && git stash apply $stash fi } action_abort() { local stash=$(head -n1 $STASH_FILE) git rebase --abort [ -n "$stash" ] && git stash apply $stash rm $STASH_FILE } # normalise cmdline arguments parse "$@" || exit $? ACTION=action_start while [ $# -gt 0 ]; do case $1 in --continue) is_active && is_ours || \ die "--continue given, but no active changes being made" ACTION=action_continue ;; --reset) is_ours || \ die "--reset given, but no stashed changes available" ACTION=action_reset ;; --abort) is_ours || \ die "--abort given, but no active changes being made" ACTION=action_abort ;; --start) is_active && die "already doing a rebase or change" ACTION=action_start ;; *) REVISION=$(git rev-parse --revs-only "$@" | head -n1) PATHS=( $(git rev-parse --sq --no-revs --no-flags "$@") ) break ;; esac shift done $ACTION exit $? Loading
git-change-commit 0 → 100755 +146 −0 Original line number Diff line number Diff line #!/bin/bash OPTS_SPEC="\ git change-commit [-h] [--start|--continue|--reset|--abort] [REVISION] [[--] PATH [PATH]] change-commit does an interactive rebase keeping the current changes in the working directory, allowing edits to be made to commits using changes made in the working directory. If more than one revision is given, only the first will be used. This comes with the standard warning: DO NOT CHANGE COMMITS THAT HAVE BEEN (OR COULD HAVE BEEN) PUSHED OR FETCHED TO OTHER REPOSITORIES OUTSIDE OF YOUR CONTROL. (Unless you really, *really* know what you are doing.) -- h,help show this message and exit start begin the rebase, this is the default action and is optional. Requires: REVISION reset reset the working directory to the state it was in before starting. Optional: REVISION, PATH... continue continue to the next commit marked for editing abort quit the changes and return to the originally checked out commit/head " parse() { eval "$(git rev-parse --parseopt -- "$@" <<< "$OPTS_SPEC" || echo return $?)"; } die() { EXIT_STATUS=${EXIT_STATUE-$?} [ "x$1" = x--usage ] && parse --help echo "$*" exit $EXIT_STATUS } >&2 error() { printf "WARNING: %s\n" "$*"; } >&2 REPO_DIR=`git rev-parse --git-dir` || die STASH_FILE=$REPO_DIR/change-commit/stash STASH_FILE_BACKUP=${STASH_FILE}_pref is_active() { [ -d $REPO_DIR/rebase-merge ]; } is_ours() { [ -e $STASH_FILE ]; } confirm() { local prompt=$1 ans while true; do read -p "$prompt (y|N) " ans case $ans in y*|Y*) return 0 ;; n*|N*|'') return 1 ;; *) prompt="Sorry, what did you mean?" ;; esac done } action_start() { local stash=$(git stash create) || die [ -n "$REVISION" ] || die "need a revision to edit" git reset --hard git rebase -i $REVISION~ 2>/dev/null mkdir -p $(dirname $STASH_FILE) if ! echo "$stash" > $STASH_FILE; then error "could not write stash ID to $STASH_FILE, aborting" git rebase --abort fi cp $STASH_FILE $STASH_FILE_BACKUP [ -n "$stash" ] && git stash apply $stash } action_continue() { local stash=$(git stash create) || die echo "$stash" >> $STASH_FILE || die git reset --hard git rebase --continue || die [ -n "$stash" ] && git stash apply $stash is_active || rm $STASH_FILE } action_reset() { local stash=$(head -n1 $STASH_FILE_BACKUP) || die if [ ${#PATHS[*]} -gt 0 ]; then git checkout ${REVISION:-$stash} -- "${PATHS[@]}" elif [ -n "$REVISION" ]; then error "This will reset the working directory to match $REVISION, are" \ "you sure this is what you intended?" error "You will still be able to get the original working directory" \ "by running --reset without any further arguments." confirm "Reset to ${REVISION::10}?" || return 0 local saved_head=$(git symbolic-ref -q HEAD || cat $REPO_DIR/HEAD) git reset --hard HEAD git checkout $REVISION git reset $saved_head else git reset --hard [ -n "$stash" ] && git stash apply $stash fi } action_abort() { local stash=$(head -n1 $STASH_FILE) git rebase --abort [ -n "$stash" ] && git stash apply $stash rm $STASH_FILE } # normalise cmdline arguments parse "$@" || exit $? ACTION=action_start while [ $# -gt 0 ]; do case $1 in --continue) is_active && is_ours || \ die "--continue given, but no active changes being made" ACTION=action_continue ;; --reset) is_ours || \ die "--reset given, but no stashed changes available" ACTION=action_reset ;; --abort) is_ours || \ die "--abort given, but no active changes being made" ACTION=action_abort ;; --start) is_active && die "already doing a rebase or change" ACTION=action_start ;; *) REVISION=$(git rev-parse --revs-only "$@" | head -n1) PATHS=( $(git rev-parse --sq --no-revs --no-flags "$@") ) break ;; esac shift done $ACTION exit $?