git-commit-vandalism/t/t3421-rebase-topology-linear.sh
Elijah Newren d48e5e21da rebase (interactive-backend): make --keep-empty the default
Different rebase backends have different treatment for commits which
start empty (i.e. have no changes relative to their parent), and the
--keep-empty option was added at some point to allow adjusting behavior.
The handling of commits which start empty is actually quite similar to
commit b00bf1c9a8 (git-rebase: make --allow-empty-message the default,
2018-06-27), which pointed out that the behavior for various backends is
often more happenstance than design.  The specific change made in that
commit is actually quite relevant as well and much of the logic there
directly applies here.

It makes a lot of sense in 'git commit' to error out on the creation of
empty commits, unless an override flag is provided.  However, once
someone determines that there is a rare case that merits using the
manual override to create such a commit, it is somewhere between
annoying and harmful to have to take extra steps to keep such
intentional commits around.  Granted, empty commits are quite rare,
which is why handling of them doesn't get considered much and folks tend
to defer to existing (accidental) behavior and assume there was a reason
for it, leading them to just add flags (--keep-empty in this case) that
allow them to override the bad defaults.  Fix the interactive backend so
that --keep-empty is the default, much like we did with
--allow-empty-message.  The am backend should also be fixed to have
--keep-empty semantics for commits that start empty, but that is not
included in this patch other than a testcase documenting the failure.

Note that there was one test in t3421 which appears to have been written
expecting --keep-empty to not be the default as correct behavior.  This
test was introduced in commit 00b8be5a4d ("add tests for rebasing of
empty commits", 2013-06-06), which was part of a series focusing on
rebase topology and which had an interesting original cover letter at
https://lore.kernel.org/git/1347949878-12578-1-git-send-email-martinvonz@gmail.com/
which noted
    Your input especially appreciated on whether you agree with the
    intent of the test cases.
and then went into a long example about how one of the many tests added
had several questions about whether it was correct.  As such, I believe
most the tests in that series were about testing rebase topology with as
many different flags as possible and were not trying to state in general
how those flags should behave otherwise.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-16 15:40:42 -08:00

381 lines
8.7 KiB
Bash
Executable File

#!/bin/sh
test_description='basic rebase topology tests'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-rebase.sh
# a---b---c
# \
# d---e
test_expect_success 'setup' '
test_commit a &&
test_commit b &&
test_commit c &&
git checkout b &&
test_commit d &&
test_commit e
'
test_run_rebase () {
result=$1
shift
test_expect_$result "simple rebase $*" "
reset_rebase &&
git rebase $* c e &&
test_cmp_rev c HEAD~2 &&
test_linear_range 'd e' c..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_expect_success 'setup branches and remote tracking' '
git tag -l >tags &&
for tag in $(cat tags)
do
git branch branch-$tag $tag || return 1
done &&
git remote add origin "file://$PWD" &&
git fetch origin
'
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* is no-op if upstream is an ancestor" "
reset_rebase &&
git rebase $* b e &&
test_cmp_rev e HEAD
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* -f rewrites even if upstream is an ancestor" "
reset_rebase &&
git rebase $* -f b e &&
test_cmp_rev ! e HEAD &&
test_cmp_rev b HEAD~2 &&
test_linear_range 'd e' b..
"
}
test_run_rebase success ''
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* -f rewrites even if remote upstream is an ancestor" "
reset_rebase &&
git rebase $* -f branch-b branch-e &&
test_cmp_rev ! branch-e origin/branch-e &&
test_cmp_rev branch-b HEAD~2 &&
test_linear_range 'd e' branch-b..
"
}
test_run_rebase success ''
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* fast-forwards from ancestor of upstream" "
reset_rebase &&
git rebase $* e b &&
test_cmp_rev e HEAD
"
}
test_run_rebase success ''
test_run_rebase success --fork-point
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
# f
# /
# a---b---c---g---h
# \
# d---gp--i
#
# gp = cherry-picked g
# h = reverted g
#
# Reverted patches are there for tests to be able to check if a commit
# that introduced the same change as another commit is
# dropped. Without reverted commits, we could get false positives
# because applying the patch succeeds, but simply results in no
# changes.
test_expect_success 'setup of linear history for range selection tests' '
git checkout c &&
test_commit g &&
revert h g &&
git checkout d &&
cherry_pick gp g &&
test_commit i &&
git checkout b &&
test_commit f
'
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* drops patches in upstream" "
reset_rebase &&
git rebase $* h i &&
test_cmp_rev h HEAD~2 &&
test_linear_range 'd i' h..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* can drop last patch if in upstream" "
reset_rebase &&
git rebase $* h gp &&
test_cmp_rev h HEAD^ &&
test_linear_range 'd' h..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --onto drops patches in upstream" "
reset_rebase &&
git rebase $* --onto f h i &&
test_cmp_rev f HEAD~2 &&
test_linear_range 'd i' f..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --onto does not drop patches in onto" "
reset_rebase &&
git rebase $* --onto h f i &&
test_cmp_rev h HEAD~3 &&
test_linear_range 'd gp i' h..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
# a---b---c---j!
# \
# d---k!--l
#
# ! = empty
test_expect_success 'setup of linear history for empty commit tests' '
git checkout c &&
make_empty j &&
git checkout d &&
make_empty k &&
test_commit l
'
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* keeps begin-empty commits" "
reset_rebase &&
git rebase $* j l &&
test_cmp_rev c HEAD~4 &&
test_linear_range 'j d k l' c..
"
}
test_run_rebase failure ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --keep-empty" "
reset_rebase &&
git rebase $* --keep-empty c l &&
test_cmp_rev c HEAD~3 &&
test_linear_range 'd k l' c..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --keep-empty keeps empty even if already in upstream" "
reset_rebase &&
git rebase $* --keep-empty j l &&
test_cmp_rev j HEAD~3 &&
test_linear_range 'd k l' j..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase success --rebase-merges
# m
# /
# a---b---c---g
#
# x---y---bp
#
# bp = cherry-picked b
# m = reverted b
#
# Reverted patches are there for tests to be able to check if a commit
# that introduced the same change as another commit is
# dropped. Without reverted commits, we could get false positives
# because applying the patch succeeds, but simply results in no
# changes.
test_expect_success 'setup of linear history for test involving root' '
git checkout b &&
revert m b &&
git checkout --orphan disjoint &&
git rm -rf . &&
test_commit x &&
test_commit y &&
cherry_pick bp b
'
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --onto --root" "
reset_rebase &&
git rebase $* --onto c --root y &&
test_cmp_rev c HEAD~2 &&
test_linear_range 'x y' c..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* without --onto --root with disjoint history" "
reset_rebase &&
git rebase $* c y &&
test_cmp_rev c HEAD~2 &&
test_linear_range 'x y' c..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --onto --root drops patch in onto" "
reset_rebase &&
git rebase $* --onto m --root bp &&
test_cmp_rev m HEAD~2 &&
test_linear_range 'x y' m..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --onto --root with merge-base does not go to root" "
reset_rebase &&
git rebase $* --onto m --root g &&
test_cmp_rev m HEAD~2 &&
test_linear_range 'c g' m..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* without --onto --root with disjoint history drops patch in onto" "
reset_rebase &&
git rebase $* m bp &&
test_cmp_rev m HEAD~2 &&
test_linear_range 'x y' m..
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* --root on linear history is a no-op" "
reset_rebase &&
git rebase $* --root c &&
test_cmp_rev c HEAD
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase failure -p
test_run_rebase () {
result=$1
shift
test_expect_$result "rebase $* -f --root on linear history causes re-write" "
reset_rebase &&
git rebase $* -f --root c &&
test_cmp_rev ! a HEAD~2 &&
test_linear_range 'a b c' HEAD
"
}
test_run_rebase success ''
test_run_rebase success -m
test_run_rebase success -i
test_have_prereq !REBASE_P || test_run_rebase success -p
test_done