git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Odd rebase behavior

From
David A. Greene <greened@obbligato.org>
Date
Dec 16, 2015, 03:17 UTC
Message-ID
<877fkf9j5h.fsf@waller.obbligato.org>
Hi,

The attached tests do not do what I expected them to do. I commented out the tests involving the new rebase empty commit behavior I just sent. The uncommented tests show the strange behavior.

According to the rebase man page, rebase gathers commits as in "git log <upstream>..HEAD." However, that is not what happens in the tests below. Some of the commits disappear.

The test basically does this:
- Setup a master project and a subproject, merged via a subtree-like
  merge (this is how git-subtree does it).
- Add some commits to the subproject directory after the subtree merge,
  to create some history not in the original subproject.
- filter-branch --subdirectory-filter to extract commits from the
  subproject directory.
- Rebase those commits back on to the original subproject repository.

The above loses all commits made after the subproject is merged into the main project.

Note that the rebase is a little wonky. filter-branch creates a disconnected graph and the rebase is invoked with <upstream>=master. I'm not sure how rebase is supposed to operate in this case (if it is supported at all) but it definitely is not doing the master..HEAD thing.

Replacing "master" with "--root" causes rebase to do the right thing.

This seems like a bug to me, even with the strange <upstream> on a disconnected graph. At the very least git should not silently lose commits.

I can think of two ways this could be resolved:
- Forbid this kind of operation and error our with a message (when
  <upstream> and HEAD do not share ancestry)
- Make it work as if --root were specified
Thoughts?
                         -David
--->8---
#!/bin/sh
test_description='git rebase tests for empty commits

This test runs git rebase and tests handling of empty commits. ' . ./test-lib.sh

addfile() {
    name=$1
    echo $(basename ${name}) > ${name}
    ${git} add ${name}
    ${git} commit -m "Add $(basename ${name})"
}
check_equal()
{
	test_debug 'echo'
	test_debug "echo \"check a:\" \"{$1}\""
	test_debug "echo \"      b:\" \"{$2}\""
	if [ "$1" = "$2" ]; then
		return 0
	else
		return 1
	fi
}
last_commit_message()
{
	git log --pretty=format:%s -1
}
test_expect_success 'setup' '
	test_commit README &&
	mkdir files &&
	cd files &&
	git init &&
	test_commit master1 &&
	test_commit master2 &&
	test_commit master3 &&
	cd .. &&
	test_debug "echo Add project master to master" &&
	git fetch files master &&
	git branch files-master FETCH_HEAD &&
	test_debug "echo Add subtree master to master via subtree" &&
	git read-tree --prefix=files_subtree files-master &&
	git checkout -- files_subtree &&
	tree=$(git write-tree) &&
	head=$(git rev-parse HEAD) &&
	rev=$(git rev-parse --verify files-master^0) &&
	commit=$(git commit-tree -p ${head} -p ${rev} -m "Add subproject master" ${tree}) &&
	git reset ${commit} &&
	cd files_subtree &&
	test_commit master4 &&
	cd .. &&
	test_commit files_subtree/master5
'
# Does not preserve master4 and master5.
test_expect_success 'Rebase default' '
	git checkout -b rebase-default master &&
	git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
	git commit -m "Empty commit" --allow-empty &&
	git rebase -Xsubtree=files_subtree  --preserve-merges --onto files-master master &&
	check_equal "$(last_commit_message)" "files_subtree/master5"
'
# Does not preserve master4, master5 and empty.
test_expect_success 'Rebase --keep-empty' '
	git checkout -b rebase-keep-empty master &&
	git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
	git commit -m "Empty commit" --allow-empty &&
	git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
	check_equal "$(last_commit_message)" "Empty commit"
'

# Does not preserve master4 and master5. #test_expect_success 'Rebase --keep-redundant' ' # git checkout -b rebase-keep-redundant master && # git filter-branch --prune-empty -f --subdirectory-filter files_subtree && # git commit -m "Empty commit" --allow-empty && # git rebase -Xsubtree=files_subtree --keep-redundant --preserve-merges --onto files-master master && # check_equal "$(last_commit_message)" "files_subtree/master5" #'

# Does not preserve master4, master5 and empty. #test_expect_success 'Rebase --keep-empty --keep-redundant' ' # git checkout -b rebase-keep-empty-keep-redundant master && # git filter-branch --prune-empty -f --subdirectory-filter files_subtree && # git commit -m "Empty commit" --allow-empty && # git rebase -Xsubtree=files_subtree --keep-empty --keep-redundant --preserve-merges --onto files-master master && # check_equal "$(last_commit_message)" "Empty commit" #'

test_done
Next: John Keeping
Message 1 of 6 in “Odd rebase behavior”
  1. David A. GreeneDec 16, 2015
  2. John KeepingDec 16, 2015
  3. David A. GreeneDec 18, 2015
  4. John KeepingDec 18, 2015
  5. Johannes SixtDec 18, 2015
  6. John KeepingDec 19, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.