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

Re: Question about .git/objects/info/alternates

From
Junio C Hamano <gitster@pobox.com>
Date
Mar 24, 2010, 19:58 UTC
Message-ID
<7vljdhh4po.fsf@alter.siamese.dyndns.org>
In-Reply-To
<a038bef51003241153g33445607qb3ab750e08b0584@mail.gmail.com>
Chris Packham <judge.packham@gmail.com> writes:
Show 6 quoted lines
> git repack -a did the correct thing.
>
> It occurs to me that the UI around alternates is a bit lacking i.e.
> there isn't a git command to display the alternates in use or to add
> them to an existing repository (or at least I couldn't find one
> skimming the docs or googling).
That's an understatement.  Thanks for starting the effort.

I will likely to have quite a few style issues with the script implementation, and am undecided if this should be a new command or an option to some existing command, but it's time we have a management facility for alternates.

Show 12 quoted lines
> diff --git a/Makefile b/Makefile
> index 3a6c6ea..1a7b084 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -334,6 +334,7 @@ TEST_PROGRAMS_NEED_X =
>  unexport CDPATH
>
>  SCRIPT_SH += git-am.sh
> +SCRIPT_SH += git-alternates.sh
>  SCRIPT_SH += git-bisect.sh
>  SCRIPT_SH += git-difftool--helper.sh
>  SCRIPT_SH += git-filter-branch.sh

You probably need one entry in the command-list.txt to classify where in the main manual page git(1) for this command to appear. I would suggest imitating "git config".

Show 17 quoted lines
> diff --git a/git-alternates.sh b/git-alternates.sh
> new file mode 100755
> index 0000000..74ec707
> --- /dev/null
> +++ b/git-alternates.sh
> @@ -0,0 +1,159 @@
> +#!/bin/sh
> ...
> +#
> +# Runs through the alternates file calling the callback function $1
> +# with the name of the alternate as the first argument to the callback
> +# any additional arguments are passed to the callback function.
> +#
> +walk_alternates()
> +{
> +    local alternates=$GIT_DIR/objects/info/alternates
> +    local callback=$1

We want to use this on platforms with ksh and dash, so please refrain from bash-ism features. "local" does not mix well with "#!/bin/sh".

> +    shift
> +
> +    if [ -e $alternates ]; then
We tend to avoid '[' and write it like this:
	if test -f "$alternates"
        then
        	...

Also notice that an indent is one tab and one tabstop is 8 places, and alternates need to be quoted in case $GIT_DIR has IFS whitespace in it.

> +        while read line
> +        do
> +            $callback $line $*

As the path to an alternate object store can contain IFS whitespace, line needs to be quoted. Also you call walk_alternates with things like "$dir" that could also be a path with IFS whitespace, it needs to be quoted properly. I suspect callback is only your own shell function, so it does not have to be quoted, but it is Ok to quote it for consistency. I.e.

	"$callback" "$line" "$@"

You are loooooose in quoting throughout your script, so I won't bother to point all of them out in the rest of the message. You also are loose in checking error returns (what you failed to write $newalternates file, for example), which needs to be fixed before the final version.

> +        done < $alternates

This is correct per POSIX even if $alternates itself has IFS whitespace in it, but newer bash on some platforms have an obnoxious "safety feature" (see 3fa7c3d work around an obnoxious bash "safety feature" on OpenBSD, 2010-01-26) that can cause it to barf on this. It is unfortunate but we need to quote it like this:

	done <"$alternates"

Also notice that there is no SP after < or > redirection (it is just a style thing).

Show 12 quoted lines
> +# Walk function to display one alternate object store and, if the user
> +# has specified -r, recursively call show_alternates on the git
> +# repository that the object store belongs to.
> +#
> +show_alternates_walk()
> +{
> +    say "Object store $1"
> +    say "    referenced via $GIT_DIR"
> +
> +    local new_git_dir=${line%%/objects}
> +    if [ "$recursive" == "true" -a "$GIT_DIR" != "$new_git_dir" ]
> +    then
	if test "$recursive" = true && test "$GIT_DIR" != "$new_git_dir"
        then
        	...
> +        GIT_DIR=$new_git_dir show_alternates

This is probably depending on a bug in bash and is not portable. See 76c9c0d (rebase -i: Export GIT_AUTHOR_* variables explicitly, 2010-01-22)

Show 5 quoted lines
> +add_alternate()
> +{
> +    if test ! -d $dir; then
> +        die "fatal: $dir is not a directory"
> +    fi

This will not work with relative alternates. In two repositories A and B, where B borrows from A:

	A/.git/objects/
        B/.git/objects/info/alternates

the alternates file in B can point at A's .git/objects, relative to its own .git/objects/.

Show 9 quoted lines
> +    walk_alternates check_current_alternate_walk $dir
> +
> +    # At this point we know that $dir is a directory that exists
> +    # and that its not already being used as an alternate. We could
> +    # go further and verify that $dir has valid objects.
> +
> +    # if we're still going we can safely add the alternate
> +    touch $GIT_DIR/objects/info/alternates
> +    echo "$(readlink -f $dir)" >> $GIT_DIR/objects/info/alternates
What is this touch for?

Is readlink(1) portable enough across platforms? A more fundamental question is if resolving symbolic link like this behind user is a sensible thing to do, especially as you are ...

> +    say "$dir added as an alternate"
... lying to the user here, if $dir indeed is a symbolic link.
Show 6 quoted lines
> +rewrite_alternates()
> +{
> +    if test "$1" != "$2"; then
> +        echo $2 >> $3
> +    fi
> +}
That's a misleading name for this helper function.
Show 15 quoted lines
> +del_alternate()
> +{
> +    if test ! $force = "true"; then
> +        say "Not forced, use"
> +        say "   'git repack -a' to fetch missing objects, then "
> +        say "   '$dashless -f -d $dir' to remove the alternate"
> +        die
> +    fi
> +
> +    local alternates=$GIT_DIR/objects/info/alternates
> +
> +    new_alts_file=$(mktemp $alternates-XXXXXX)
> +    touch $new_alts_file
> +
> +    walk_alternates rewrite_alternates $dir $new_alts_file
I think this is a more expensive way to say:
	grep -v -F "$dir" <"$alternates" >"$new_alternates"
Show 6 quoted lines
> +    mv $new_alts_file $alternates
> +
> +    # save the git from repeatedly reading a 0 length file
> +    if test $(stat -c "%s" $alternates) -eq 0; then
> +        rm $alternates
> +    fi
Good point, but it would be better to do something like this:
	grep -v -F "$dir" <"$alternates" >"$new_alternates"
	if test -s "$new_alternates"
        then
        	mv "$new_alternates" "$alternates"
	else
        	rm -f "$alternates"
	fi
without making it less portable by using "stat".
Previous: Jonathan NiederNext: Chris Packham
Message 5 of 17 in “Question about .git/objects/info/alternates”
  1. Chris PackhamMar 22, 2010
  2. Jonathan NiederMar 23, 2010
  3. Chris PackhamMar 24, 2010
  4. Jonathan NiederMar 24, 2010
  5. Junio C HamanoMar 24, 2010
  6. Chris PackhamMar 24, 2010
  7. Chris PackhamMar 25, 2010
  8. 1/2 Add git alternate commandChris Packham, Mar 25, 2010
  9. Junio C HamanoMar 29, 2010
  10. Chris PackhamMar 31, 2010
  11. 2/2 tests for git alternate commandChris Packham, Mar 25, 2010
  12. Johannes SixtMar 25, 2010
  13. Chris PackhamMar 25, 2010
  14. Miklos VajnaMar 26, 2010
  15. Johannes SixtMar 26, 2010
  16. Stephen BoydMar 24, 2010
  17. Chris PackhamMar 24, 2010

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.