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

Re: [PATCH] Use perl instead of tac

From
JSJörg Sommer <joerg@alea.gnuu.de>
Date
May 4, 2008, 22:13 UTC
Message-ID
<20080504221319.GA14584@alea.gnuu.de>
In-Reply-To
<5374CF8E-3E6E-480B-A23B-13BE85C7ABCF@silverinsanity.com>
Hallo Brian,
Brian Gernhardt schrieb am Wed 30. Apr, 11:25 (-0400):
Show 13 quoted lines
> On Apr 30, 2008, at 5:02 AM, Jörg Sommer wrote:
>
>>> I also dislike the large lists this is carrying around in shell
>>> variables.  If I'm reading it correctly, the tag list could be  
>>> replaced
>>> by invocations of "git describe --exact-match".
>>
>> Yes. How to get all tags of a commit?
>>
>> % git tag foo v1.5.5
>> % git describe --exact-match 9d831805195ba40b62f632acc6bb6e53d3
>> warning: tag 'v1.5.5' is really 'foo' here
>> v1.5.5

And how can I get only tags no annotated tags? We can't recreate annotated tags with git rebase.

> Those concerns being: overrunning the length of a shell variable,

Are you shure there is any such bounding? I didn't saw anything about the size of a variables in IEEE 1003.1-2004, but didn't look very carfully. All my shells (bash, dash, zsh) can handle more than 16777217 characters:

dash -c 'a=a; while :; do a=$a$a; echo $a | wc -c; done'

This would be a new version of create_extended_todo_list() without tac. What do you think about it? Is it better readable? But there's a bug.

create_extended_todo_list () {
	# The idea of this function is to
	# 1. build a list of tags
	# 2. go through the list and
	#    * issue a reset command to the parent of the commit, if the last
	#      commit was not the parent of the current commit,
	#    * issue a pick command for simple commits,
	#    * issue for each merge commit a merge command with the hashs of
	#      the parent commits,
	#    * register each parent of a merge and issue a mark command
	#      (without an ID) after the commit for each registered commit and
	#    * issue a tag command, if the commit is in the tag list.
	# 3. Then go through the created list and
	#    * add an ID to each mark command and
	#    * replace all occurences of the hash in reset and merge commands
	#      by the mark ID
	test -e "$DOTEST"/cetl.tmp \
		&& die "Someone else uses our filename cetl.tmp." \
			"That's not nice"
	if test t = "${PRESERVE_TAGS:-}"
	then
		tag_list=$(git show-ref --abbrev=7 --tags | \
			(
			while read sha1 tag
			do
				tag=${tag#refs/tags/}
				if test ${last_sha1:-0000} = $sha1
				then
					saved_tags="$saved_tags:$tag"
				else
					printf "%s" "${last_sha1:+ $last_sha1#$saved_tags}"
					last_sha1=$sha1
					saved_tags=$tag
				fi
			done
			echo "${last_sha1:+ $last_sha1:$saved_tags}"
			) )
	else
		tag_list=
	fi
	mark_these_commits=
	while IFS=_ read commit parents subject
	do
		first_parent=${parents%% *}
		if test "${last_commit:-$SHORTUPSTREAM}" != $first_parent
		then
			test "$first_parent" = $SHORTUPSTREAM &&
				first_parent=$SHORTONTO
			echo reset $first_parent
		fi
		unset first_parent
		last_commit=$commit
		case "$parents" in
		*' '*)
			new_parents=
			for p in $parents
			do
				mark_these_commits=$(insert_value_at_key_into_list \
					"$commit" "$p" "$mark_these_commits")
				if test "$p" = $SHORTUPSTREAM
				then
					new_parents="$new_parents $SHORTONTO"
				else
					new_parents="$new_parents $p"
				fi
			done
			unset p
			echo merge $commit ${new_parents# * }
			unset new_parents
			;;
		*)
			echo "pick $commit $subject"
			;;
		esac
		if tmp=$(get_value_from_list $commit "$tag_list")
		then
			for t in $(echo $tmp | tr : ' ')
			do
				echo tag $t
			done
		fi
	done > "$DOTEST"/cetl.tmp
	unset commit parents subject
	commit_mark_list=
	next_mark=0
	last_commit=
	while read cmd args
	do
		case "$cmd" in
		pick)
			this_commit="${args%% *}"
			;;
		reset)
			this_commit=$args
			if tmp=$(get_value_from_list $args "$commit_mark_list")
			then
				args=":$tmp"
			fi
			;;
		merge)
			new_args=
			for i in ${args#* }
			do
				if tmp=$(get_value_from_list $i \
					"$commit_mark_list")
				then
					new_args="$new_args :$tmp"
				else
					new_args="$new_args $i"
				fi
			done
			this_commit="${args%% *}"
			args="$this_commit ${new_args# }"
			;;
		esac
		if tmp=$(get_value_from_list "$last_commit" \
			"$mark_these_commits") && \
			test "${this_commit:-$last_commit}" != $tmp
		then
			if tmp=$(get_value_from_list "$last_commit" \
				"$commit_mark_list")
			then
				test "$last_cmd" = reset -o "$last_cmd" = tag \
					|| echo mark ":$tmp"
			else
				commit_mark_list=$(insert_value_at_key_into_list \
					$next_mark $last_commit "$commit_mark_list")
				echo mark ":$next_mark"
				next_mark=$(($next_mark + 1))
			fi
		fi
		last_commit=${this_commit:-$last_commit}
		unset this_commit
		echo "$cmd $args"
		last_cmd=$cmd
	done < "$DOTEST"/cetl.tmp
	rm "$DOTEST"/cetl.tmp
	unset last_cmd last_commit next_mark cmd args tmp commit_mark_list \
		mark_these_commits
}

The problem is the mark command. If you walk from ONTO to HEAD trough the list, you must know for a commit, if it is used _later_. But don't create a mark if it is used immediately, e.g. pick; merge not pick; mark; merge.

If you walk from HEAD to ONTO, this is much easier. I delayed the mark for the first head of a merge and checked if the next commit is this commit. This way I keep the todo list clean and don't get something like this for --first-parent:

pick abc pick def mark :0 merge 012 foreign-branch

Bye, Jörg.
-- 
Es ist außerdem ein weit verbreiteter Irrtum das USENET ‚helfen‘ soll.
Tatsächlich wurde USENET nachweislich zur persönlichen Belustigung
seiner Erfinder geschaffen.
Jörg Klemenz <joerg@gmx.net>, <b4ai4o$1u8vmt$2@ID-21915.news.dfncis.de>
Previous: Brian GernhardtNext: Brian Gernhardt
Message 24 of 30 in “Use of tac in git-rebase--interactive”
  1. Brian GernhardtApr 27, 2008
  2. Jeff KingApr 27, 2008
  3. Use perl instead of tacBrian Gernhardt, Apr 27, 2008
  4. rebase--interactive: Replace unportable 'tac' by a sed script.Johannes Sixt, Apr 28, 2008
  5. Junio C HamanoApr 28, 2008
  6. Paolo BonziniApr 28, 2008
  7. Jeff KingApr 28, 2008
  8. Andreas EricssonApr 28, 2008
  9. Jörg SommerApr 28, 2008
  10. Randal L. SchwartzApr 28, 2008
  11. David SymondsApr 28, 2008
  12. Paolo BonziniApr 28, 2008
  13. Brian GernhardtApr 28, 2008
  14. Johannes SchindelinApr 28, 2008
  15. Jörg SommerApr 28, 2008
  16. Avery PennarunApr 28, 2008
  17. Matthieu MoyApr 28, 2008
  18. Brian GernhardtApr 28, 2008
  19. Brian GernhardtApr 28, 2008
  20. Jörg SommerApr 30, 2008
  21. Jörg SommerApr 30, 2008
  22. Brian GernhardtApr 30, 2008
  23. Brian GernhardtApr 30, 2008
  24. Jörg SommerMay 4, 2008
  25. Brian GernhardtMay 6, 2008
  26. Johannes SchindelinApr 28, 2008
  27. Brian GernhardtApr 28, 2008
  28. Johannes SchindelinApr 28, 2008
  29. しらいしななこApr 27, 2008
  30. Junio C HamanoApr 30, 2008

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.