git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Johannes Sixt <j.sixt@viscovery.net>
To: Martin von Zweigbergk <martinvonz@gmail.com>
Cc: git@vger.kernel.org
Subject: Re: [RFC PATCH] add t3420-rebase-topology
Date: Tue, 18 Sep 2012 09:53:23 +0200	[thread overview]
Message-ID: <50582873.603@viscovery.net> (raw)
In-Reply-To: <1347949878-12578-1-git-send-email-martinvonz@gmail.com>

Am 9/18/2012 8:31, schrieb Martin von Zweigbergk:
> Add more test cases to check that the topology after a rebase is as
> expected. Conflicts are not considered, but patch-equivalence is.
> ---
> 
> Tests pass and fail as indicated by the suffix
> (_success/_failure). Your input especially appreciated on whether you
> agree with the intent of the test cases. For example, do you agree
> that 'rebase --onto does not re-apply patches in onto' is desirable?
> And if you do, then do you also agree that 'rebase --root --onto
> ignores patch in onto' is desirable? How about 'rebase --root is not a
> no-op'? One might think that --force would be necessary, but on the
> other hand, if that was the case, the only point (AFAICT) of "git
> rebase --root <branch>" without --force would be to linearize history,
> so I instead made the test case confirm that --root without --onto
> effectively behaves as if --force was also passed.
> 
> Feedback on the structure/setup and style is of course also
> appreciated.
> 
>  t/t3420-rebase-topology.sh | 348 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 348 insertions(+)
>  create mode 100755 t/t3420-rebase-topology.sh
> 
> diff --git a/t/t3420-rebase-topology.sh b/t/t3420-rebase-topology.sh
> new file mode 100755
> index 0000000..024a2b4
> --- /dev/null
> +++ b/t/t3420-rebase-topology.sh
> @@ -0,0 +1,348 @@
> +#!/bin/sh
> +
> +test_description='effect of rebase on topology'
> +. ./test-lib.sh
> +
> +
> +#       q---C---r
> +#      /
> +# a---b---c---d!--e---p
> +#      \
> +#       f---g!--h
> +#        \
> +#         j-------E---k
> +#          \       \
> +#           n---H---w
> +#
> +# x---y---B
> +#
> +#
> +# ! = empty
> +# uppercase = cherry-picked
> +# p = reverted e
> +#
> +# TODO:
> +# prune graph to what's needed
> +
> +empty () {

Is it "is_empty" or "make_empty"/"empty_commit"?

> +	git commit --allow-empty -m $1 &&
> +	git tag $1
> +}

Obviously the latter.

> +
> +cherry_pick () {
> +	git cherry-pick -n $1 &&
> +	git commit -m $2 &&
> +	git tag $2
> +}
> +
> +revert () {
> +	git revert -n $1 &&
> +	git commit -m $2 &&
> +	git tag $2
> +}
> +
> +
> +test_expect_success 'setup' '
> +	test_commit a &&
> +	test_commit b &&
> +	test_commit c &&
> +	empty d &&
> +	test_commit e &&
> +	revert e p &&
> +	git checkout b &&
> +	test_commit f &&
> +	empty g &&
> +	test_commit h &&
> +	git checkout f &&
> +	test_commit j &&
> +	cherry_pick e E &&
> +	test_commit k &&
> +	git checkout j &&
> +	test_commit n &&
> +	cherry_pick h H &&
> +	git merge -m w E &&
> +	git tag w &&
> +	git checkout b &&
> +	test_commit q &&
> +	cherry_pick c C &&
> +	test_commit r &&
> +	git checkout --orphan disjoint &&
> +	git rm -rf . &&
> +	test_commit x &&
> +	test_commit y &&
> +	cherry_pick b B
> +'
> +
> +reset () {
> +	git rebase --abort
> +	git reset --hard
> +}

The 'rebase --abort' can fail, but there is no && chain. Good. Using this
function instead of the individual commands in the tests does not break
the && chain there. Good.

These following three functions should be and can be consistent with the
order of their arguments: expected actual.

> +test_range () {
> +	test "$(git log --reverse --topo-order --format=%s "$1" | xargs)" = "$2"

	expected=$1
	set -- $(git log --reverse --topo-order --format=%s "$2")
	test "expected" = "$*"

> +}
> +
> +test_revisions () {
> +	expected="$1"
> +	shift
> +	test "$(git log --format=%s --no-walk=unsorted "$@" | xargs)" = "$expected"

	set -- $(git log --format=%s --no-walk=unsorted "$@")
	test "expected" = "$*"

> +}
> +
> +same_revision () {
> +	test "$(git rev-parse $1)" = "$(git rev-parse $2)"
> +}

'test_same_revision'?

> +
> +# the following 5 (?) tests copy t3400 tests, but check the history rather than status code and/or stdout
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' c j &&
> +	same_revision HEAD~2 c &&
> +	test_range c.. "f j"
> +'
> +}
> +test_expect_success 'simple rebase' "$(run)"
> +test_expect_success 'simple rebase -m' "$(run -m)"
> +test_expect_success 'simple rebase -i' "$(run -i)"
> +test_expect_success 'simple rebase -p' "$(run -p)"

Since here and in the following tests the test cases and test descriptions
vary in the same way, wouldn't it make sense to factor the description out
as well?

test_run_rebase () {
	test_expect_success "simple rebase $*" "
		reset &&
		git rebase $* c j &&
		same_revision HEAD~2 c &&
		test_range c.. 'f j'
	"
}

test_run_rebase ''
test_run_rebase -m
test_run_rebase -i
test_run_rebase -p

(Watch your quoting, though.)

Oh, I see: some tests expect failure. How about:

test_run_rebase () {
	result=$1
	shift
	test_expect_$result "simple rebase $*" "..."
}
test_run_rebase success ''
etc.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' b j &&
> +	same_revision HEAD j
> +'
> +}
> +test_expect_success 'rebase is no-op if upstream is an ancestor' "$(run)"
> +test_expect_success 'rebase -m is no-op if upstream is an ancestor' "$(run -m)"
> +test_expect_success 'rebase -i is no-op if upstream is an ancestor' "$(run -i)"
> +test_expect_success 'rebase -p is no-op if upstream is an ancestor' "$(run -p)"

OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --force b j &&
> +	! same_revision HEAD j &&
> +	test_range b.. "f j"
> +'
> +}
> +test_expect_success 'rebase --force' "$(run)"
> +test_expect_success 'rebase -m --force' "$(run -m)"
> +test_expect_success 'rebase -i --force' "$(run -i)"
> +test_expect_failure 'rebase -p --force' "$(run -p)"

Do you mean "rebase --force rewrites even if upstream is an ancestor"?
That would make sense.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' j b &&
> +	same_revision HEAD j
> +'
> +}
> +test_expect_success 'rebase fast-forwards if an ancestor of upstream' "$(run)"
> +test_expect_success 'rebase -m fast-forwards if an ancestor of upstream' "$(run -m)"
> +test_expect_success 'rebase -i fast-forwards if an ancestor of upstream' "$(run -i)"
> +test_expect_success 'rebase -p fast-forwards if an ancestor of upstream' "$(run -p)"

OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' p k &&
> +	test_range p.. "f j k"
> +'
> +}
> +test_expect_success 'rebase ignores patch in upstream' "$(run)"
> +test_expect_failure 'rebase -m ignores patch in upstream' "$(run -m)"
> +test_expect_success 'rebase -i ignores patch in upstream' "$(run -i)"
> +test_expect_success 'rebase -p ignores patch in upstream' "$(run -p)"

"drops" instead of "ignores"? OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' c h &&
> +	test_range c.. "f h"
> +'
> +}
> +test_expect_success 'rebase ignores empty commit' "$(run)"
> +test_expect_success 'rebase -m ignores empty commit' "$(run -m)"
> +test_expect_success 'rebase -i ignores empty commit' "$(run -i)"
> +test_expect_success 'rebase -p ignores empty commit' "$(run -p)"

"drops" instead of "ignores"? OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --keep-empty c h &&
> +	test_range c.. "f g h"
> +'
> +}
> +test_expect_success 'rebase --keep-empty' "$(run)"
> +test_expect_failure 'rebase -m --keep-empty' "$(run -m)"
> +test_expect_success 'rebase -i --keep-empty' "$(run -i)"
> +test_expect_failure 'rebase -p --keep-empty' "$(run -p)"

OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --keep-empty p h &&
> +	test_range p.. "f g h"
> +'
> +}
> +test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' "$(run)"
> +test_expect_failure 'rebase -m --keep-empty keeps empty even if already in upstream' "$(run -m)"
> +test_expect_failure 'rebase -i --keep-empty keeps empty even if already in upstream' "$(run -i)"
> +test_expect_failure 'rebase -p --keep-empty keeps empty even if already in upstream' "$(run -p)"

"is in upstream" is decided by the patch text. If an empty commit is
already in upstream, this adds another one with the same or a different
commit message and authorship information. Dubious, but since it is
opt-in, it should be OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' E w &&
> +	test_range E.. "n H"
> +'
> +}
> +test_expect_success 'rebase after merge' "$(run)"
> +test_expect_success 'rebase -m after merge' "$(run -m)"
> +test_expect_success 'rebase -i after merge' "$(run -i)"

OK.

> +
> +test_expect_success 'rebase -p is no-op in history with merges' '
> +	reset &&
> +	git rebase -p j w &&
> +	same_revision HEAD w
> +'
> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' j w &&
> +	test_range j.. "E n H" || test_range j.. "n H E"
> +'

Chaining tests with || is dangerous: you do not know whether the first
failed because the condition is not satisfied or because of some other
failure.

Why is this needed in the first place? Shouldn't the history be
deterministic, provided that the commit timestamps are all distinct?

> +}
> +test_expect_success 'rebase of history with merges is linearized' "$(run)"
> +test_expect_success 'rebase -m of history with merges is linearized' "$(run -m)"
> +test_expect_success 'rebase -i of history with merges is linearized' "$(run -i)"

OK.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --onto p h k &&
> +	test_range p.. "j k"
> +'
> +}
> +test_expect_failure 'rebase --onto does not re-apply patches in onto' "$(run)"
> +test_expect_failure 'rebase -m --onto does not re-apply patches in onto' "$(run -m)"
> +test_expect_failure 'rebase -i --onto does not re-apply patches in onto' "$(run -i)"
> +test_expect_failure 'rebase -p --onto does not re-apply patches in onto' "$(run -p)"

Makes sense.

> +
> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --onto f d r &&
> +	test_range f.. "q C r"
> +'
> +}
> +test_expect_failure 'rebase --onto does not lose patches in upstream' "$(run)"
> +test_expect_success 'rebase -m --onto does not lose patches in upstream' "$(run -m)"
> +test_expect_failure 'rebase -i --onto does not lose patches in upstream' "$(run -i)"
> +test_expect_failure 'rebase -p --onto does not lose patches in upstream' "$(run -p)"

Makes sense.

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --root c &&
> +	! same_revision HEAD c &&
> +	test_range c "a b c"
> +'
> +}
> +test_expect_success 'rebase --root is not a no-op' "$(run)"
> +test_expect_success 'rebase -m --root is not a no-op' "$(run -m)"
> +test_expect_success 'rebase -i --root is not a no-op' "$(run -i)"
> +test_expect_success 'rebase -p --root is not a no-op' "$(run -p)"

Why? Is it more like "--root implies --force"?

> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --root --onto e y &&
> +	test_range e.. "x y"
> +'
> +}
> +test_expect_success 'rebase --root --onto' "$(run)"
> +test_expect_failure 'rebase -m --root --onto' "$(run -m)"
> +test_expect_success 'rebase -i --root --onto' "$(run -i)"
> +test_expect_success 'rebase -p --root --onto' "$(run -p)"

Where does this rebase start? Ah, --root stands in for the "upstream"
argument, hence, y is the tip to rebase. Right? Then it makes sense.

> +
> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --root --onto e B &&
> +	test_range e.. "x y"
> +'
> +}
> +test_expect_success 'rebase --root --onto ignores patch in onto' "$(run)"
> +test_expect_failure 'rebase -m --root --onto ignores patch in onto' "$(run -m)"
> +test_expect_success 'rebase -i --root --onto ignores patch in onto' "$(run -i)"
> +test_expect_success 'rebase -p --root --onto ignores patch in onto' "$(run -p)"

Makes sense.

> +
> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' e y &&
> +	test_range e.. "x y"
> +'
> +}
> +test_expect_success 'rebase without --root works on disjoint history' "$(run)"
> +test_expect_failure 'rebase -m without --root works on disjoint history' "$(run -m)"
> +test_expect_success 'rebase -i without --root works on disjoint history' "$(run -i)"
> +test_expect_failure 'rebase -p without --root works on disjoint history' "$(run -p)"

OK.

> +
> +
> +run () {
> +echo '
> +	reset &&
> +	git rebase '"$@"' --root --onto p k &&
> +	test_range p.. "f j k"
> +'
> +}
> +test_expect_success 'rebase --root --onto with merge-base ignores --root' "$(run)"
> +test_expect_failure 'rebase -m --root --onto with merge-base ignores --root' "$(run -m)"
> +test_expect_success 'rebase -i --root --onto with merge-base ignores --root' "$(run -i)"
> +test_expect_success 'rebase -p --root --onto with merge-base ignores --root' "$(run -p)"

Makes sense.

> +
> +test_expect_success 'rebase -p re-creates merge from upstream' '
> +	reset &&
> +	git rebase -p k w &&
> +	same_revision HEAD^ H &&
> +	same_revision HEAD^2 k
> +'

IMO, this tests the wrong thing. You have this history:

 ---j-------E---k
     \       \
      n---H---w

where E is the second parent of w. What does it mean to rebase w onto k?
IMO, it is a meaningless operation, and the outcome is irrelevant.

It would make sense to test that this history results after the upstream
at H moved forward:

 ---j-------E---k
     \       \
      n---H   \
           \   \
            z---w'

That is, w began a topic by mergeing the sidebranch E; then upstream
advanced to z, and now you rebase the topic to the new upstream.

> +
> +test_expect_success 'rebase -p re-creates internal merge' '
> +	reset &&
> +	git rebase -p c w &&
> +	test_revisions "f j n E H w" HEAD~4 HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD

You must also test for c; otherwise the test would succeed if rebase did
nothing at all.

This comment applies to all other tests as well, even the "regular" rebase
tests above. (But I noticed only when I read this test.)

> +'
> +
> +test_expect_success 'rebase -p rebuilds history around dropped commit matching upstream' '
> +	reset &&
> +	git rebase -p h w &&
> +	test_revisions "j E n w" HEAD~2 HEAD^2 HEAD^ HEAD
> +'
> +
> +test_expect_success 'rebase -p drops merge commit when one entire side is dropped' '
> +	reset &&
> +	git rebase -p p w &&
> +	test_range p.. "f j n H"
> +'
> +
> +test_expect_failure 'rebase -p --onto drops commit in <onto>' '
> +	reset &&
> +	git rebase -p --onto p f w &&
> +	test_range p.. "j n H"
> +'
> +
> +test_expect_success 'rebase -p with two paths to $from' '
> +	reset &&
> +	git rebase -p --onto c j w &&
> +	test_revisions "c n E H w" HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
> +'

I didn't look at these remaining -p tests because I ran out of time.


After this plethora of tests, can we get rid of some or many from other
test scripts? (t34* tests are the ones that take the longest on Windows to
run.)

A nice summary of the rebase behavior. I like it.

-- Hannes

  parent reply	other threads:[~2012-09-18  7:53 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-09-18  6:31 [RFC PATCH] add t3420-rebase-topology Martin von Zweigbergk
2012-09-18  7:51 ` Junio C Hamano
2012-09-21 17:06   ` Martin von Zweigbergk
2012-09-18  7:53 ` Johannes Sixt [this message]
2012-09-26 17:07   ` Martin von Zweigbergk
2012-09-27 12:20     ` Chris Webb
2012-09-28 18:03       ` Martin von Zweigbergk
2012-09-29  8:08         ` Chris Webb
2013-05-29  6:39 ` [PATCH v2 0/7] Rebase topology test Martin von Zweigbergk
2013-05-29  6:39   ` [PATCH v2 1/7] add simple tests of consistency across rebase types Martin von Zweigbergk
2013-06-03 17:16     ` Martin von Zweigbergk
2013-06-03 18:05       ` Junio C Hamano
2013-06-03 18:12         ` Martin von Zweigbergk
2013-05-29  6:39   ` [PATCH v2 2/7] add tests for rebasing with patch-equivalence present Martin von Zweigbergk
2013-05-29  7:09     ` Johannes Sixt
2013-05-30  5:30       ` Martin von Zweigbergk
2013-05-30  5:41         ` Felipe Contreras
2013-05-30  6:14           ` Martin von Zweigbergk
2013-05-30  6:40             ` Felipe Contreras
2013-05-30  6:46               ` Martin von Zweigbergk
2013-05-30 12:54         ` Johannes Sixt
2013-05-30 15:01           ` Martin von Zweigbergk
2013-05-29  6:39   ` [PATCH v2 3/7] add tests for rebasing of empty commits Martin von Zweigbergk
2013-05-29  6:39   ` [PATCH v2 4/7] add tests for rebasing root Martin von Zweigbergk
2013-05-29  7:31     ` Johannes Sixt
2013-05-30  5:51       ` Martin von Zweigbergk
2013-05-29  6:39   ` [PATCH v2 5/7] add tests for rebasing merged history Martin von Zweigbergk
2013-05-29  7:57     ` Johannes Sixt
2013-05-31  5:42       ` Martin von Zweigbergk
2013-05-29 10:33     ` Johannes Sixt
2013-05-29  6:39   ` [PATCH v2 6/7] t3406: modernize style Martin von Zweigbergk
2013-05-29  6:39   ` [PATCH v2 7/7] tests: move test for rebase messages from t3400 to t3406 Martin von Zweigbergk
2013-05-29  7:10   ` [PATCH v2 0/7] Rebase topology test Felipe Contreras
2013-05-29 12:50     ` Ramkumar Ramachandra
2013-05-29 13:54       ` Felipe Contreras
2013-05-31  6:49   ` [PATCH v3 " Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 1/7] add simple tests of consistency across rebase types Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 2/7] add tests for rebasing with patch-equivalence present Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 3/7] add tests for rebasing of empty commits Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 4/7] add tests for rebasing root Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 5/7] add tests for rebasing merged history Martin von Zweigbergk
2013-05-31 12:19       ` Johannes Sixt
2013-06-01 21:36         ` [PATCH v4 " Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 6/7] t3406: modernize style Martin von Zweigbergk
2013-05-31  6:49     ` [PATCH v3 7/7] tests: move test for rebase messages from t3400 to t3406 Martin von Zweigbergk
2013-06-03 20:42     ` [PATCH v5 0/7] Rebase topology test Martin von Zweigbergk
2013-06-03 20:42       ` [PATCH v5 1/7] add simple tests of consistency across rebase types Martin von Zweigbergk
2013-06-03 22:28         ` Junio C Hamano
2013-06-04  5:14           ` Martin von Zweigbergk
2013-06-04  5:49             ` Junio C Hamano
2013-06-04  6:15             ` Johannes Sixt
2013-06-05  4:31               ` Martin von Zweigbergk
2013-06-03 20:42       ` [PATCH v5 2/7] add tests for rebasing with patch-equivalence present Martin von Zweigbergk
2013-06-03 20:42       ` [PATCH v5 3/7] add tests for rebasing of empty commits Martin von Zweigbergk
2013-06-03 20:42       ` [PATCH v5 4/7] add tests for rebasing root Martin von Zweigbergk
2013-06-03 20:42       ` [PATCH v5 5/7] add tests for rebasing merged history Martin von Zweigbergk
2013-06-04 17:18         ` Junio C Hamano
2013-06-05  5:44           ` Martin von Zweigbergk
2013-06-05  6:12           ` Johannes Sixt
2013-06-03 20:42       ` [PATCH v5 6/7] t3406: modernize style Martin von Zweigbergk
2013-06-03 20:42       ` [PATCH v5 7/7] tests: move test for rebase messages from t3400 to t3406 Martin von Zweigbergk
2013-06-07  6:11       ` [PATCH v6 0/8] Rebase topology test Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 1/7] add simple tests of consistency across rebase types Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 2/7] add tests for rebasing with patch-equivalence present Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 3/7] add tests for rebasing of empty commits Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 4/7] add tests for rebasing root Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 5/7] add tests for rebasing merged history Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 6/7] t3406: modernize style Martin von Zweigbergk
2013-06-07  6:11         ` [PATCH v6 7/7] tests: move test for rebase messages from t3400 to t3406 Martin von Zweigbergk
2013-06-07 16:43         ` [PATCH v6 0/8] Rebase topology test Junio C Hamano
2013-06-07 19:37         ` Johannes Sixt
2013-06-18  7:28         ` [PATCH mz/rebase-tests] rebase topology tests: fix commit names on case-insensitive file systems Johannes Sixt
2013-06-18 15:45           ` Junio C Hamano
2013-06-18 15:53           ` Martin von Zweigbergk
2013-06-19  5:52             ` Johannes Sixt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=50582873.603@viscovery.net \
    --to=j.sixt@viscovery.net \
    --cc=git@vger.kernel.org \
    --cc=martinvonz@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).