git-rebase: add keep_empty flag

Add a command line switch to git-rebase to allow a user the ability to specify
that they want to keep any commits in a series that are empty.

When git-rebase's type is am, then this option will automatically keep any
commit that has a tree object identical to its parent.

This patch changes the default behavior of interactive rebases as well.  With
this patch, git-rebase -i will produce a revision set passed to
git-revision-editor, in which empty commits are commented out.  Empty commits
may be kept manually by uncommenting them.  If the new --keep-empty option is
used in an interactive rebase the empty commits will automatically all be
uncommented in the editor.

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Neil Horman 2012-04-20 10:36:17 -04:00 committed by Junio C Hamano
parent bedfe86ce6
commit 90e1818f9a
4 changed files with 55 additions and 8 deletions

View File

@ -238,6 +238,10 @@ leave out at most one of A and B, in which case it defaults to HEAD.
will be reset to where it was when the rebase operation was will be reset to where it was when the rebase operation was
started. started.
--keep-empty::
Keep the commits that do not change anything from its
parents in the result.
--skip:: --skip::
Restart the rebasing process by skipping the current patch. Restart the rebasing process by skipping the current patch.

View File

@ -20,11 +20,20 @@ esac
test -n "$rebase_root" && root_flag=--root test -n "$rebase_root" && root_flag=--root
git format-patch -k --stdout --full-index --ignore-if-in-upstream \ if test -n "$keep_empty"
--src-prefix=a/ --dst-prefix=b/ \ then
--no-renames $root_flag "$revisions" | # we have to do this the hard way. git format-patch completely squashes
git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" && # empty commits and even if it didn't the format doesn't really lend
move_to_original_branch # itself well to recording empty patches. fortunately, cherry-pick
# makes this easy
git cherry-pick --allow-empty "$revisions"
else
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
--src-prefix=a/ --dst-prefix=b/ \
--no-renames $root_flag "$revisions" |
git am $git_am_opt --rebasing --resolvemsg="$resolvemsg"
fi && move_to_original_branch
ret=$? ret=$?
test 0 != $ret -a -d "$state_dir" && write_basic_state test 0 != $ret -a -d "$state_dir" && write_basic_state
exit $ret exit $ret

View File

@ -167,6 +167,14 @@ has_action () {
sane_grep '^[^#]' "$1" >/dev/null sane_grep '^[^#]' "$1" >/dev/null
} }
is_empty_commit() {
tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null ||
die "$1: not a commit that can be picked")
ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null ||
ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904)
test "$tree" = "$ptree"
}
# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
# GIT_AUTHOR_DATE exported from the current environment. # GIT_AUTHOR_DATE exported from the current environment.
do_with_author () { do_with_author () {
@ -191,12 +199,19 @@ git_sequence_editor () {
pick_one () { pick_one () {
ff=--ff ff=--ff
case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
case "$force_rebase" in '') ;; ?*) ff= ;; esac case "$force_rebase" in '') ;; ?*) ff= ;; esac
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1" output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
if is_empty_commit "$sha1"
then
empty_args="--allow-empty"
fi
test -d "$rewritten" && test -d "$rewritten" &&
pick_one_preserving_merges "$@" && return pick_one_preserving_merges "$@" && return
output git cherry-pick $ff "$@" output git cherry-pick $empty_args $ff "$@"
} }
pick_one_preserving_merges () { pick_one_preserving_merges () {
@ -780,9 +795,17 @@ git rev-list $merges_option --pretty=oneline --abbrev-commit \
sed -n "s/^>//p" | sed -n "s/^>//p" |
while read -r shortsha1 rest while read -r shortsha1 rest
do do
if test -z "$keep_empty" && is_empty_commit $shortsha1
then
comment_out="# "
else
comment_out=
fi
if test t != "$preserve_merges" if test t != "$preserve_merges"
then then
printf '%s\n' "pick $shortsha1 $rest" >> "$todo" printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
else else
sha1=$(git rev-parse $shortsha1) sha1=$(git rev-parse $shortsha1)
if test -z "$rebase_root" if test -z "$rebase_root"
@ -801,7 +824,7 @@ do
if test f = "$preserve" if test f = "$preserve"
then then
touch "$rewritten"/$sha1 touch "$rewritten"/$sha1
printf '%s\n' "pick $shortsha1 $rest" >> "$todo" printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
fi fi
fi fi
done done
@ -851,6 +874,12 @@ cat >> "$todo" << EOF
# #
EOF EOF
if test -z "$keep_empty"
then
echo "# Note that empty commits are commented out" >>"$todo"
fi
has_action "$todo" || has_action "$todo" ||
die_abort "Nothing to do" die_abort "Nothing to do"

View File

@ -43,6 +43,7 @@ s,strategy=! use the given merge strategy
no-ff! cherry-pick all commits, even if unchanged no-ff! cherry-pick all commits, even if unchanged
m,merge! use merging strategies to rebase m,merge! use merging strategies to rebase
i,interactive! let the user edit the list of commits to rebase i,interactive! let the user edit the list of commits to rebase
k,keep-empty preserve empty commits during rebase
f,force-rebase! force rebase even if branch is up to date f,force-rebase! force rebase even if branch is up to date
X,strategy-option=! pass the argument through to the merge strategy X,strategy-option=! pass the argument through to the merge strategy
stat! display a diffstat of what changed upstream stat! display a diffstat of what changed upstream
@ -97,6 +98,7 @@ state_dir=
action= action=
preserve_merges= preserve_merges=
autosquash= autosquash=
keep_empty=
test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
read_basic_state () { read_basic_state () {
@ -220,6 +222,9 @@ do
-i) -i)
interactive_rebase=explicit interactive_rebase=explicit
;; ;;
-k)
keep_empty=yes
;;
-p) -p)
preserve_merges=t preserve_merges=t
test -z "$interactive_rebase" && interactive_rebase=implied test -z "$interactive_rebase" && interactive_rebase=implied