threads / discuss / 976

'git commit' duplicates parents?

Subject: 'git commit' duplicates parents?

## tl;dr

12 messages between Jun 19, 2005 and Jun 20, 2005.

replies: 11people: 3as markdown or json

Jeff Garzik· Jun 19, 2005, 16:27 UTC · lore

I just checked in a change with 'git commit' (no arguments). Two strange things occurred:

1) git-whatchanged does not list the change at all.  However,
	a) I verified that my change is indeed top-of-tree
	b) git-changes-script (attached) does show the change
2) git-changes-script shows the parents in a readable fashion, and it 
shows two duplicate parent entries.  In contrast, other changes do not 
have two parents:
my change:
> commit 4864989199fa62c7044be2258550ddc561411ab6
		^^^ top of tree aka .git/HEAD
Show 7 quoted lines
> tree b40996c7a0a5446875aa3664045af7e377451bf6
> parent 7df551254add79a445d2e47e8f849cef8fee6e38
> parent 7df551254add79a445d2e47e8f849cef8fee6e38
> author Jeff Garzik <jgarzik@pretzel.yyz.us> Sun, 19 Jun 2005 20:06:28 -0400
> committer Jeff Garzik <jgarzik@pobox.com> Sun, 19 Jun 2005 20:06:28 -0400
> 
> fc4/fc:  fix warnings/errors caused by recent changes
a random change not committed by 'git commit':
Show 12 quoted lines
> commit 7df551254add79a445d2e47e8f849cef8fee6e38
> tree 468a43ac3f94b9bf8618b102a7d609e29d3900f5
> parent f7d7fc0322c1770fe7ee836ca2732c2f88e2e1a4
> author David S. Miller <davem@davemloft.net> Sun, 19 Jun 2005 13:01:10 -0700
> committer David S. Miller <davem@davemloft.net> Sun, 19 Jun 2005 13:01:10 -0700
> 
> [TCP]: Fix sysctl_tcp_low_latency
> 
> When enabled, this should disable UCOPY prequeue'ing altogether,
> but it does not due to a missing test.
> 
> Signed-off-by: David S. Miller <davem@davemloft.net>

#!/bin/bash # # Make a log of changes in a GIT branch. # # This script was originally written by (c) Ross Vandegrift. # Adapted to his scripts set by (c) Petr Baudis, 2005. # Major optimizations by (c) Phillip Lougher. # Rendered trivial by Linus Torvalds. # Added -L|-R option by James Bottomley # # options: # script [-L <dir> | -R <dir> |-r <from_sha1> [ -r <to_sha1] ] [<sha1>] # # With no options shows all the revisions from HEAD to the root # -L shows all the changes in the local tree compared to the tree at <dir> # -R shows all the changes in the remote tree at <dir> compared to the local # -r shows all the changes in one commit or between two

tmpfile=/tmp/git_changes.$$ r1= r2=

showcommit() {
	commit="$1"
	echo commit ${commit%:*};
	git-cat-file commit $commit | \
		while read key rest; do
			case "$key" in
			"author"|"committer")
				date=(${rest#*> })
				sec=${date[0]}; tz=${date[1]}
				dtz=${tz/+/+ }; dtz=${dtz/-/- }
				pdate="$(date -Rud "1970-01-01 UTC + $sec sec $dtz" 2>/dev/null)"
				if [ "$pdate" ]; then
					echo $key $rest | sed "s/>.*/> ${pdate/+0000/$tz}/"
				else
					echo $key $rest
				fi
				;;
			"")
				echo; cat
				;;
			*)
				echo $key $rest
				;;
			esac
		done
}
while true; do
	case "$1" in
		-R)	shift;
			diffsearch=+
			remote="$1"
			shift;;
		-L)	shift;
			diffsearch=-
			remote="$1"
			shift;;
		-r)	shift;
			if [ -z "$r1" ]; then
				r1="$1"
			else
				r2="$1"
			fi
			shift;;
		*)	base="$1"
			break;;
	esac
done
if [ -n "$r1" ]; then
	if [ -z "$r2" ]; then
		showcommit $r1
		exit 0
	fi
	diffsearch=+
	remote=`pwd`;
	tobase="$r2";
	base="$r1"
fi
	
if [ -z "$base" ]; then
	base=$(cat .git/HEAD) || exit 1
fi
git-rev-tree $base | sort -rn  > ${tmpfile}.base
if [ -n "$remote" ]; then
	[ -d $remote/.git ] || exit 1
	if [ -z "$tobase" ]; then
		tobase=$(cat $remote/.git/HEAD) || exit 1
	fi
	pushd $remote > /dev/null
	git-rev-tree $tobase | sort -rn > ${tmpfile}.remote
	diff -u ${tmpfile}.base ${tmpfile}.remote | grep "^${diffsearch}[^${diffsearch}]" | cut -c 1- > ${tmpfile}.diff
	rm -f ${tmpfile}.base ${tmpfile}.remote
	mv ${tmpfile}.diff ${tmpfile}.base
	if [ $diffsearch = "-" ]; then
		popd > /dev/null
	fi
fi
[ -s "${tmpfile}.base" ] || exit 0
cat ${tmpfile}.base | while read time commit parents; do
	showcommit $commit
	echo -e "\n--------------------------"

done rm -f ${tmpfile}.base

Jeff Garzik· Jun 19, 2005, 16:32 UTC · re: Jeff Garzik · lore

Re: 'git commit' duplicates parents?

Jeff Garzik wrote:
> I just checked in a change with 'git commit' (no arguments).  Two 
> strange things occurred:
FWIW you can see the problem yourself at

'fc4-fix' branch of rsync://rsync.kernel.org/pub/scm/linux/kernel/git/jgarzik/misc-2.6.git

(just one change checked in, the change referenced in the previous email)
Linus Torvalds· Jun 20, 2005, 02:24 UTC · re: Jeff Garzik · lore

Re: 'git commit' duplicates parents?

On Sun, 19 Jun 2005, Jeff Garzik wrote:
Show 7 quoted lines
> 
> I just checked in a change with 'git commit' (no arguments).  Two 
> strange things occurred:
> 
> 1) git-whatchanged does not list the change at all.  However,
> 	a) I verified that my change is indeed top-of-tree
> 	b) git-changes-script (attached) does show the change
Your commit is a merge. A corrupted one.
Show 10 quoted lines
> 2) git-changes-script shows the parents in a readable fashion, and it 
> shows two duplicate parent entries.  In contrast, other changes do not 
> have two parents:
> 
> my change:
> > commit 4864989199fa62c7044be2258550ddc561411ab6
> 		^^^ top of tree aka .git/HEAD
> > tree b40996c7a0a5446875aa3664045af7e377451bf6
> > parent 7df551254add79a445d2e47e8f849cef8fee6e38
> > parent 7df551254add79a445d2e47e8f849cef8fee6e38
Notice: two times the same head.

You had a MERGE_HEAD in your tree, and "git commit" warned you about it in big bold letters and told you what to do, but you ignored it.

"git commit" said:
                echo "#"
                echo "# It looks like your may be committing a MERGE."
                echo "# If this is not correct, please remove the file"
                echo "# $GIT_DIR/MERGE_HEAD"
                echo "# and try again"
                echo "#"
and if you had just done as it asked you, you'd have been ok.

As to why you had a .git/MERGE_HEAD in your tree, it's probably because your merge scripts haven't kept up with mine.

		Linus
Jeff Garzik· Jun 20, 2005, 02:28 UTC · re: Linus Torvalds · lore

Re: 'git commit' duplicates parents?

Linus Torvalds wrote:
> As to why you had a .git/MERGE_HEAD in your tree, it's probably because 
> your merge scripts haven't kept up with mine.

Nope, I use vanilla latest ones. FWIW my setup is 100% vanilla git plus two small scripts, 'git-switch-tree' and 'git-new-branch', which switch around .git/HEAD.

Doing some experimenting, it seems that git-pull-script does not remove MERGE_HEAD and ORIG_HEAD after its done.

This is reproducible by updating vanilla linux-2.6.git using vanilla git-pull-script. Just a standard update-to-latest-kernel, with no conflicts/merges/etc.

	Jeff
Linus Torvalds· Jun 20, 2005, 02:33 UTC · re: Linus Torvalds · lore

Re: 'git commit' duplicates parents?

On Sun, 19 Jun 2005, Linus Torvalds wrote:
> 
> Your commit is a merge. A corrupted one.

Btw, if possibly, you should just undo it. It's "valid" in the sense that having the same parent duplicated will just be considered to be a merge by a paritcularly strange person, but it's definitely not good practice, and since it _is_ technically a merge, programs that avoid showing merges (like "git-whatchanged" - because it doesn't know what it should show as the "difference") won't show it.

Other programs, like "git-diff-tree -m", which show _all_ sides of a merge, will show the diff twice (because it shows the diff against all parents). Which is also why you see it twice in your git-changes-script.

Again, "git commit" _did_ warn about this, I'm sure, but I actually see why that stupid MERGE_HEAD file was there - a null merge won't remove a stale MERGE_HEAD, so it's probably because you did a "git pull" that was a trivial merge, and that would have left that turd around..

		Linus
Jeff Garzik· Jun 20, 2005, 02:40 UTC · re: Linus Torvalds · lore

Re: 'git commit' duplicates parents?

Linus Torvalds wrote:
> Btw, if possibly, you should just undo it. It's "valid" in the sense that
Any crap like this, I undo it manually (cat previous head to .git/HEAD)
> Again, "git commit" _did_ warn about this, I'm sure, but I actually see 
> why that stupid MERGE_HEAD file was there - a null merge won't remove a 
> stale MERGE_HEAD, so it's probably because you did a "git pull" that was a 
> trivial merge, and that would have left that turd around..

Probably PEBCAK... my missing the big "MERGE_HEAD exists" warning caused the problem, it sounds like.

I simply assumed that the vanilla git scripts would clean up after themselves :)

	Jeff
Linus Torvalds· Jun 20, 2005, 03:00 UTC · re: Jeff Garzik · lore

Re: 'git commit' duplicates parents?

On Sun, 19 Jun 2005, Jeff Garzik wrote:
> 
> I simply assumed that the vanilla git scripts would clean up after 
> themselves :)

Hey, they definitely should. I've pushed out the fixes so far (just pushed out the ".cmitmsg/.editmsg" cleanup).

Keep the complaints coming when something doesn't work the way it should. I'll continue to try to blame your incompetence as much as I humanly can, but hey, some of it is occasionally mine too... ;(

		Linus
Dan Holmsand· Jun 20, 2005, 09:48 UTC · re: Linus Torvalds · lore

Re: 'git commit' duplicates parents?

Linus Torvalds wrote:
> Keep the complaints coming when something doesn't work the way it should.  
> I'll continue to try to blame your incompetence as much as I humanly can,
> but hey, some of it is occasionally mine too... ;(
Well, since it's obviously complaint time :-)

git-resolve-script still seems a bit too eager to write MERGE_HEAD and ORIG_HEAD - they only make sense if there's actually been any merging done, don't they?

Patch below shows what I mean.
/dan
---
[PATCH] Make git-resolve-script less eager to write MERGE_HEAD

MERGE_HEAD and ORIG_HEAD should only be written if there's actually been any merging done.

Signed-off-by: Dan Holmsand <holmsand@gmail.com>
diff --git a/git-resolve-script b/git-resolve-script
--- a/git-resolve-script
+++ b/git-resolve-script
@@ -12,8 +12,6 @@ merge_repo="$3"
 : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
 
 rm -f "$GIT_DIR"/MERGE_HEAD "$GIT_DIR"/ORIG_HEAD
-echo $head > "$GIT_DIR"/ORIG_HEAD
-echo $merge > "$GIT_DIR"/MERGE_HEAD
 
 #
 # The remote name is just used for the message,
@@ -32,15 +30,13 @@ fi
 
 if [ "$common" == "$merge" ]; then
 	echo "Already up-to-date. Yeeah!"
-	rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD"
 	exit 0
 fi
 if [ "$common" == "$head" ]; then
 	echo "Updating from $head to $merge."
 	git-read-tree -u -m $head $merge || exit 1
 	echo $merge > "$GIT_DIR"/HEAD
-	git-diff-tree -p ORIG_HEAD HEAD | git-apply --stat
-	rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD"
+	git-diff-tree -p $head HEAD | git-apply --stat
 	exit 0
 fi
 echo "Trying to merge $merge into $head"
@@ -51,6 +47,8 @@ if [ $? -ne 0 ]; then
 	echo "Simple merge failed, trying Automatic merge"
 	git-merge-cache -o git-merge-one-file-script -a
 	if [ $? -ne 0 ]; then
+		echo $merge > "$GIT_DIR"/MERGE_HEAD
+		echo $head > "$GIT_DIR"/ORIG_HEAD
 		echo "Automatic merge failed, fix up by hand"
 		exit 1
 	fi
@@ -60,4 +58,3 @@ result_commit=$(echo "$merge_msg" | git-
 echo "Committed merge $result_commit"
 echo $result_commit > "$GIT_DIR"/HEAD
 git-diff-tree -p $head $result_commit | git-apply --stat
-rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD"
Linus Torvalds· Jun 20, 2005, 15:11 UTC · re: Dan Holmsand · lore

Re: 'git commit' duplicates parents?

On Mon, 20 Jun 2005, Dan Holmsand wrote:
Show 6 quoted lines
> 
> git-resolve-script still seems a bit too eager to write MERGE_HEAD and 
> ORIG_HEAD - they only make sense if there's actually been any merging 
> done, don't they?
> 
> Patch below shows what I mean.

I considered this, but decided that MERGE_HEAD is potentially very useful for some of the other failure exits. There's a few "exit 1"'s in there, for example when the "git-read-tree -m" fails because of a dirty workspace.

Of course, you can always re-do the merge completely (and maybe that's what people end up doing), but at least in theory you can fix it up and just re-resolve. But in order to do that, you need to know what the MERGE_HEAD was...

So I'm not sure what the right answer is, which is why my fix was the minimally invasive one that only removes the heads on success..

		Linus
Dan Holmsand· Jun 20, 2005, 17:20 UTC · re: Linus Torvalds · lore

Re: 'git commit' duplicates parents?

Linus Torvalds wrote:
Show 14 quoted lines
> 
> On Mon, 20 Jun 2005, Dan Holmsand wrote:
> 
>>git-resolve-script still seems a bit too eager to write MERGE_HEAD and 
>>ORIG_HEAD - they only make sense if there's actually been any merging 
>>done, don't they?
>>
>>Patch below shows what I mean.
> 
> 
> I considered this, but decided that MERGE_HEAD is potentially very useful
> for some of the other failure exits. There's a few "exit 1"'s in there,
> for example when the "git-read-tree -m"  fails because of a dirty
> workspace.
Yeah, but that was exactly what I was after...

As far as I understand it, the git-read-tree -u -m doesn't actually do anything when the workspace is dirty - so there's actually no merging going on, right?

If you react to the dirty-workspace-warnings in such a failed merge by "git commit-ing" (and not noting the MERGE_HEAD warning, which has been known to happen), you'll end up with an unwanted parent in the commit.

Or am I just being stupid :-?
/dan
Linus Torvalds· Jun 20, 2005, 17:44 UTC · re: Dan Holmsand · lore

Re: 'git commit' duplicates parents?

On Mon, 20 Jun 2005, Dan Holmsand wrote:
Show 6 quoted lines
> 
> Yeah, but that was exactly what I was after...
> 
> As far as I understand it, the git-read-tree -u -m doesn't actually do
> anything when the workspace is dirty - so there's actually no merging 
> going on, right?
No, but you can trivially do so by hand afterwards.

For example, git-read-tree -u -m will complain and say "file xyz is dirty, cannot merge" (or something), and what you might decide to do is just

	git-checkout-cache -f -u xyz
	git resolve $(cat .git/HEAD) $(cat .git/MERGE_HEAD) "..."

and note how you needed to know what the merge head was in order to do this.

[ Side note: I should make "git-resolve-script" run "git-rev-parse" on its
  arguments, so that it would expand HEAD and MERGE_HEAD on its own. Maybe 
  somebody who is interested in this script might want to do that? Hint 
  hint. ]

Note how we could make both of us happy by saving that temporary MERGE_HEAD information somewhere _else_ instead. Maybe the answer is to only use "MERGE_HEAD" for the "merge manually" case, and use something else for the "this was the merge you tried to do last" case?

		Linus
Dan Holmsand· Jun 20, 2005, 18:52 UTC · re: Linus Torvalds · lore

Re: 'git commit' duplicates parents?

Linus Torvalds wrote:
Show 5 quoted lines
> 
> [ Side note: I should make "git-resolve-script" run "git-rev-parse" on its
>   arguments, so that it would expand HEAD and MERGE_HEAD on its own. Maybe 
>   somebody who is interested in this script might want to do that? Hint 
>   hint. ]
Got it. Included below.
> Note how we could make both of us happy by saving that temporary 
> MERGE_HEAD information somewhere _else_ instead. Maybe the answer is to 
> only use "MERGE_HEAD" for the "merge manually" case, and use something 
> else for the "this was the merge you tried to do last" case?
I like the "everybody happy" idea...
How about "LAST_MERGE"? And something like this?
/dan
---
[PATCH] git-resolve-script: Add LAST_MERGE and use git-rev-parse

Make git-resolve-script only write MERGE_HEAD if a merge actually occurred. All merge failures leave ORIG_HEAD and LAST_MERGE behind (instead of ORIG_HEAD and MERGE_HEAD).

Use git-rev-parse to expand arguments (and check for bad ones).
Signed-off-by: Dan Holmsand <holmsand@gmail.com>
diff --git a/git-resolve-script b/git-resolve-script
--- a/git-resolve-script
+++ b/git-resolve-script
@@ -4,26 +4,31 @@
 #
 # Resolve two trees.
 #
-head="$1"
-merge="$2"
+head=$(git-rev-parse --revs-only "$1")
+merge=$(git-rev-parse --revs-only "$2")
 merge_repo="$3"
 
 : ${GIT_DIR=.git}
 : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
 
-rm -f "$GIT_DIR"/MERGE_HEAD "$GIT_DIR"/ORIG_HEAD
-echo $head > "$GIT_DIR"/ORIG_HEAD
-echo $merge > "$GIT_DIR"/MERGE_HEAD
+dropheads() {
+	rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD" \
+		"$GIT_DIR/LAST_MERGE" || exit 1
+}
 
 #
 # The remote name is just used for the message,
 # but we do want it.
 #
-if [ "$merge_repo" == "" ]; then
+if [ -z "$head" -o -z "$merge" -o -z "$merge_repo" ]; then
 	echo "git-resolve-script <head> <remote> <merge-repo-name>"
 	exit 1
 fi
 
+dropheads
+echo $head > "$GIT_DIR"/ORIG_HEAD
+echo $merge > "$GIT_DIR"/LAST_MERGE
+
 common=$(git-merge-base $head $merge)
 if [ -z "$common" ]; then
 	echo "Unable to find common commit between" $merge $head
@@ -32,7 +37,7 @@ fi
 
 if [ "$common" == "$merge" ]; then
 	echo "Already up-to-date. Yeeah!"
-	rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD"
+	dropheads
 	exit 0
 fi
 if [ "$common" == "$head" ]; then
@@ -40,7 +45,7 @@ if [ "$common" == "$head" ]; then
 	git-read-tree -u -m $head $merge || exit 1
 	echo $merge > "$GIT_DIR"/HEAD
 	git-diff-tree -p ORIG_HEAD HEAD | git-apply --stat
-	rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD"
+	dropheads
 	exit 0
 fi
 echo "Trying to merge $merge into $head"
@@ -51,6 +56,7 @@ if [ $? -ne 0 ]; then
 	echo "Simple merge failed, trying Automatic merge"
 	git-merge-cache -o git-merge-one-file-script -a
 	if [ $? -ne 0 ]; then
+		echo $merge > "$GIT_DIR"/MERGE_HEAD
 		echo "Automatic merge failed, fix up by hand"
 		exit 1
 	fi
@@ -60,4 +66,4 @@ result_commit=$(echo "$merge_msg" | git-
 echo "Committed merge $result_commit"
 echo $result_commit > "$GIT_DIR"/HEAD
 git-diff-tree -p $head $result_commit | git-apply --stat
-rm -f -- "$GIT_DIR/ORIG_HEAD" "$GIT_DIR/MERGE_HEAD"
+dropheads

← back to recent threads