threads / discuss / 23241

[PATCHv3 1/2] Add git alternate command

Subject: [PATCHv3 1/2] Add git alternate command

## tl;dr

3 messages between Mar 29, 2010 and Mar 29, 2010.

replies: 2people: 1as markdown or json

Chris Packham· Mar 29, 2010, 21:39 UTC · lore

git alternate command

Here is the latest version of my git alternate command. The major difference to the last update is additional tests for deleting alternates and some updates to the test based on Hannes' feedback.

I still am intending to add documentation and to look into Stephens suggestions for changing the UI to use add|delete|show instead of -a|-d.

I'm also interested in any feedback regarding the actual output from the command.

Cheers, Chris

---
 Makefile                 |    1 +
 git-alternate.sh         |  156 ++++++++++++++++++++++++++++++++++++++++++++++
 t/t1430-alternate-cmd.sh |  154 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 311 insertions(+), 0 deletions(-)
Chris Packham· Mar 29, 2010, 21:39 UTC · re: Chris Packham · lore

The current UI around alternates is lacking. There are commands to add an alternate at the time of a clone (e.g. 'git clone --reference') but no commands to see what alternates have been configured or to add an alternate after a repository has been cloned. This patch adds a friendlier UI for displaying and configuring alternates.

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
 Makefile         |    1 +
 git-alternate.sh |  156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+), 0 deletions(-)
 create mode 100755 git-alternate.sh
diff --git a/Makefile b/Makefile
index 3a6c6ea..0dc5e42 100644
--- a/Makefile
+++ b/Makefile
@@ -334,6 +334,7 @@ TEST_PROGRAMS_NEED_X =
 unexport CDPATH
 
 SCRIPT_SH += git-am.sh
+SCRIPT_SH += git-alternate.sh
 SCRIPT_SH += git-bisect.sh
 SCRIPT_SH += git-difftool--helper.sh
 SCRIPT_SH += git-filter-branch.sh
diff --git a/git-alternate.sh b/git-alternate.sh
new file mode 100755
index 0000000..ea9925b
--- /dev/null
+++ b/git-alternate.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+#
+# This file is licensed under the GPL v2
+#
+# Copyright (c) Chris Packham
+
+dashless=$(basename "$0" | sed -e 's/-/ /')
+OPTIONS_KEEPDASHDASH=
+OPTIONS_SPEC="\
+$dashless [-r|--recursive]
+$dashless [-a|--add <dir>]
+$dashless [-f|--force] [-d|delete <dir>]
+--
+r,recursive                    recursively follow alternates
+a,add=                         add dir as an alternate
+d,del=                         delete dir as an alternate
+f,force                        force a delete operation
+"
+. git-sh-setup
+
+#
+# Given the path in $1, which may or may not be a relative path,
+# convert it to an absolute path
+#
+abspath()
+{
+	cd "$1"
+	pwd
+	cd - > /dev/null
+}
+
+#
+# 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()
+{
+	alternates=$GIT_DIR/objects/info/alternates
+	callback=$1
+	shift
+
+	if test -f "$alternates"
+	then
+		while read line
+		do
+			$callback "$line" "$@"
+		done< "$alternates"
+	fi
+}
+
+#
+# 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"
+
+	new_git_dir=${line%%/objects}
+	if test "$recursive" = "true" && test "$GIT_DIR" != "$new_git_dir"
+	then
+	(
+		export GIT_DIR=$new_git_dir
+		show_alternates
+	)
+	fi
+}
+
+# Display alternates currently configured
+show_alternates()
+{
+	walk_alternates show_alternates_walk
+}
+
+#
+# Walk function to check that the specified alternate does not
+# already exist.
+#
+check_current_alternate_walk()
+{
+	if test "$1" = "$2"; then
+		die "fatal: Object store $2 is already used by $GIT_DIR"
+	fi
+}
+
+# Add a new alternate
+add_alternate()
+{
+	if test ! -d "$dir"; then
+		die "fatal: $dir is not a directory"
+	fi
+
+	abs_dir=$(abspath "$dir")
+	walk_alternates check_current_alternate_walk "$abs_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
+	echo "$abs_dir" >> $GIT_DIR/objects/info/alternates
+	say "$abs_dir added as an alternate"
+	say "    use 'git repack -adl' to remove duplicate objects"
+}
+
+# Deletes the name alternate from the alternates file.
+# If there are no more alternates the alternates file will be removed
+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
+
+	alternates=$GIT_DIR/objects/info/alternates
+	new_alternates=$alternates.tmp
+
+	grep -v -F "$dir" <"$alternates" >"$new_alternates"
+	if test -s "$new_alternates"
+	then
+		mv "$new_alternates" "$alternates"
+	else
+		# save the git from repeatedly reading a 0 length file
+		rm -f "$alternates" "$new_alternates"
+	fi
+}
+
+dir=""
+oper=""
+force="false"
+
+# Option parsing
+while test $# != 0
+do
+	case "$1" in
+	-r|--recursive)  recursive="true" ;;
+	-a|--add)        oper="add"; dir="$2"; shift ;;
+	-d|--delete)     oper="del"; dir="$2"; shift ;;
+	-f|--force)      force="true" ;;
+	--)              shift; break ;;
+	*)               usage ;;
+	esac
+	shift
+done
+
+# Now go and do it
+case "$oper" in
+	add) add_alternate ;;
+	del) del_alternate ;;
+	*)   show_alternates ;;
+esac
-- 
1.7.0.3
Chris Packham· Mar 29, 2010, 21:39 UTC · re: Chris Packham · lore

[PATCHv3 2/2] tests for git alternate command

Signed-off-by: Chris Packham <judge.packham@gmail.com>
---
 t/t1430-alternate-cmd.sh |  154 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 154 insertions(+), 0 deletions(-)
 create mode 100644 t/t1430-alternate-cmd.sh
diff --git a/t/t1430-alternate-cmd.sh b/t/t1430-alternate-cmd.sh
new file mode 100644
index 0000000..ba27633
--- /dev/null
+++ b/t/t1430-alternate-cmd.sh
@@ -0,0 +1,154 @@
+#!/bin/sh
+#
+# Copyright (c) Chris Packham
+#
+
+test_description='Test of git alternate command'
+
+. ./test-lib.sh
+
+test_expect_success 'setup for rest of the test' '
+
+	mkdir -p base &&
+	(
+		cd base &&
+		git init &&
+		echo test > a.txt &&
+		echo test > b.txt &&
+		echo test > c.txt &&
+		git add *.txt &&
+		git commit -a -m "Initial Commit"
+	)
+	git clone base A &&
+	git clone A B &&
+	git clone A C &&
+	git clone A D
+	testbase=$(pwd)
+'
+
+test_expect_success 'can add alternate after clone' '
+
+	(
+		cd B &&
+		git alternate -a ../base/.git/objects &&
+
+		echo "$testbase/base/.git/objects" > expect &&
+		cp .git/objects/info/alternates actual   &&
+
+		test_cmp expect actual
+
+	)
+'
+
+test_expect_success 'add same alternate fails (absolute path)' '
+
+	(
+		cd B &&
+		test_must_fail git alternate -a $(pwd)/base/.git/objects
+	)
+'
+
+test_expect_success 'add same alternate fails (relative path)' '
+
+	(
+		cd B &&
+		test_must_fail git alternate -a ../base/.git/objects
+	)
+'
+
+test_expect_success 'can add multiple alternates' '
+
+	(
+		cd C &&
+		git alternate -a ../base/.git/objects &&
+		git alternate -a ../B/.git/objects &&
+
+		{
+			echo "$testbase/base/.git/objects"
+			echo "$testbase/B/.git/objects"
+		} > expect &&
+
+		cp .git/objects/info/alternates actual   &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'can add recursive alternate' '
+
+	(
+		cd D &&
+		git alternate -a ../C/.git/objects
+
+		echo "$testbase/C/.git/objects" > expect &&
+		cp .git/objects/info/alternates actual   &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'simple display' '
+
+	(
+		cd B &&
+		git alternate >actual &&
+		{
+			echo "Object store $testbase/base/.git/objects"
+			echo "    referenced via $testbase/B/.git"
+		} >expect &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'recursive display' '
+
+	(
+		cd D &&
+		git alternate -r >actual &&
+
+		{
+			echo "Object store $testbase/C/.git/objects"
+			echo "    referenced via $testbase/D/.git"
+			echo "Object store $testbase/base/.git/objects"
+			echo "    referenced via $testbase/C/.git"
+			echo "Object store $testbase/B/.git/objects"
+			echo "    referenced via $testbase/C/.git"
+			echo "Object store $testbase/base/.git/objects"
+			echo "    referenced via $testbase/B/.git"
+		} >expect &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'cannot delete unless --forced' '
+	(
+		cd C &&
+		test_must_fail git alternate -d "$testbase/B/.git/objects"
+	)
+'
+
+test_expect_success 'can delete one alternate' '
+	(
+		cd C &&
+		git alternate -f -d "$testbase/B/.git/objects" &&
+
+		echo "$testbase/base/.git/objects" > expect &&
+		cp .git/objects/info/alternates actual   &&
+
+		test_cmp expect actual
+	)
+'
+
+test_expect_success 'last alternate deleted removes file' '
+	(
+		cd C &&
+		git alternate -f -d "$testbase/base/.git/objects" &&
+
+		test ! -e .git/objects/info/alternates
+
+	)
+'
+
+test_done
-- 
1.7.0.3

← back to recent threads