Git development
 help / color / mirror / Atom feed
* [PATCH v3 2/3] Introduce a performance testing framework
From: Thomas Rast @ 2012-02-17 10:25 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <cover.1329472405.git.trast@student.ethz.ch>

This introduces a performance testing framework under t/perf/.  It
tries to be as close to the test-lib.sh infrastructure as possible,
and thus should be easy to get used to for git developers.

The following points were considered for the implementation:

1. You usually want to compare arbitrary revisions/build trees against
   each other.  They may not have the performance test under
   consideration, or even the perf-lib.sh infrastructure.

   To cope with this, the 'run' script lets you specify arbitrary
   build dirs and revisions.  It even automatically builds the revisions
   if it doesn't have them at hand yet.

2. Usually you would not want to run all tests.  It would take too
   long anyway.  The 'run' script lets you specify which tests to run;
   or you can also do it manually.  There is a Makefile for
   discoverability and 'make clean', but it is not meant for
   real-world use.

3. Creating test repos from scratch in every test is extremely
   time-consuming, and shipping or downloading such large/weird repos
   is out of the question.

   We leave this decision to the user.  Two different sizes of test
   repos can be configured, and the scripts just copy one or more of
   those (using hardlinks for the object store).  By default it tries
   to use the build tree's git.git repository.

   This is fairly fast and versatile.  Using a copy instead of a clone
   preserves many properties that the user may want to test for, such
   as lots of loose objects, unpacked refs, etc.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 Makefile                        |   22 ++++-
 t/Makefile                      |   43 ++++++++-
 t/perf/.gitignore               |    2 +
 t/perf/Makefile                 |   15 +++
 t/perf/README                   |  146 +++++++++++++++++++++++++++++
 t/perf/aggregate.perl           |  166 ++++++++++++++++++++++++++++++++
 t/perf/min_time.perl            |   21 +++++
 t/perf/p0000-perf-lib-sanity.sh |   41 ++++++++
 t/perf/p0001-rev-list.sh        |   17 ++++
 t/perf/perf-lib.sh              |  198 +++++++++++++++++++++++++++++++++++++++
 t/perf/run                      |   82 ++++++++++++++++
 t/test-lib.sh                   |   26 ++++-
 12 files changed, 774 insertions(+), 5 deletions(-)
 create mode 100644 t/perf/.gitignore
 create mode 100644 t/perf/Makefile
 create mode 100644 t/perf/README
 create mode 100755 t/perf/aggregate.perl
 create mode 100755 t/perf/min_time.perl
 create mode 100755 t/perf/p0000-perf-lib-sanity.sh
 create mode 100755 t/perf/p0001-rev-list.sh
 create mode 100644 t/perf/perf-lib.sh
 create mode 100755 t/perf/run

diff --git a/Makefile b/Makefile
index a0de4e9..1fb1705 100644
--- a/Makefile
+++ b/Makefile
@@ -2361,6 +2361,10 @@ GIT-BUILD-OPTIONS: FORCE
 	@echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
 	@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 	@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
+	@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
+ifdef GIT_TEST_OPTS
+	@echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
+endif
 ifdef GIT_TEST_CMP
 	@echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
 endif
@@ -2369,7 +2373,18 @@ ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
 endif
 	@echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
 	@echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
-	@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
+ifdef GIT_PERF_REPEAT_COUNT
+	@echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@
+endif
+ifdef GIT_PERF_REPO
+	@echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@
+endif
+ifdef GIT_PERF_LARGE_REPO
+	@echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@
+endif
+ifdef GIT_PERF_MAKE_OPTS
+	@echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
+endif
 
 ### Detect Tck/Tk interpreter path changes
 ifndef NO_TCLTK
@@ -2405,6 +2420,11 @@ export NO_SVN_TESTS
 test: all
 	$(MAKE) -C t/ all
 
+perf: all
+	$(MAKE) -C t/perf/ all
+
+.PHONY: test perf
+
 test-ctype$X: ctype.o
 
 test-date$X: date.o ctype.o
diff --git a/t/Makefile b/t/Makefile
index b5048ab..6091211 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -73,4 +73,45 @@ gitweb-test:
 valgrind:
 	$(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
 
-.PHONY: pre-clean $(T) aggregate-results clean valgrind
+perf:
+	$(MAKE) -C perf/ all
+
+# Smoke testing targets
+-include ../GIT-VERSION-FILE
+uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo unknown')
+uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo unknown')
+
+test-results:
+	mkdir -p test-results
+
+test-results/git-smoke.tar.gz: test-results
+	$(PERL_PATH) ./harness \
+		--archive="test-results/git-smoke.tar.gz" \
+		$(T)
+
+smoke: test-results/git-smoke.tar.gz
+
+SMOKE_UPLOAD_FLAGS =
+ifdef SMOKE_USERNAME
+	SMOKE_UPLOAD_FLAGS += -F username="$(SMOKE_USERNAME)" -F password="$(SMOKE_PASSWORD)"
+endif
+ifdef SMOKE_COMMENT
+	SMOKE_UPLOAD_FLAGS += -F comments="$(SMOKE_COMMENT)"
+endif
+ifdef SMOKE_TAGS
+	SMOKE_UPLOAD_FLAGS += -F tags="$(SMOKE_TAGS)"
+endif
+
+smoke_report: smoke
+	curl \
+		-H "Expect: " \
+		-F project=Git \
+		-F architecture="$(uname_M)" \
+		-F platform="$(uname_S)" \
+		-F revision="$(GIT_VERSION)" \
+		-F report_file=@test-results/git-smoke.tar.gz \
+		$(SMOKE_UPLOAD_FLAGS) \
+		http://smoke.git.nix.is/app/projects/process_add_report/1 \
+	| grep -v ^Redirecting
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind perf
diff --git a/t/perf/.gitignore b/t/perf/.gitignore
new file mode 100644
index 0000000..50f5cc1
--- /dev/null
+++ b/t/perf/.gitignore
@@ -0,0 +1,2 @@
+build/
+test-results/
diff --git a/t/perf/Makefile b/t/perf/Makefile
new file mode 100644
index 0000000..8c47155
--- /dev/null
+++ b/t/perf/Makefile
@@ -0,0 +1,15 @@
+-include ../../config.mak
+export GIT_TEST_OPTIONS
+
+all: perf
+
+perf: pre-clean
+	./run
+
+pre-clean:
+	rm -rf test-results
+
+clean:
+	rm -rf build "trash directory".* test-results
+
+.PHONY: all perf pre-clean clean
diff --git a/t/perf/README b/t/perf/README
new file mode 100644
index 0000000..fdf974a
--- /dev/null
+++ b/t/perf/README
@@ -0,0 +1,146 @@
+Git performance tests
+=====================
+
+This directory holds performance testing scripts for git tools.  The
+first part of this document describes the various ways in which you
+can run them.
+
+When fixing the tools or adding enhancements, you are strongly
+encouraged to add tests in this directory to cover what you are
+trying to fix or enhance.  The later part of this short document
+describes how your test scripts should be organized.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make".  This runs all
+the tests on the current git repository.
+
+    === Running 2 tests in this tree ===
+    [...]
+    Test                                     this tree       
+    ---------------------------------------------------------
+    0001.1: rev-list --all                   0.54(0.51+0.02) 
+    0001.2: rev-list --all --objects         6.14(5.99+0.11) 
+    7810.1: grep worktree, cheap regex       0.16(0.16+0.35) 
+    7810.2: grep worktree, expensive regex   7.90(29.75+0.37)
+    7810.3: grep --cached, cheap regex       3.07(3.02+0.25) 
+    7810.4: grep --cached, expensive regex   9.39(30.57+0.24)
+
+You can compare multiple repositories and even git revisions with the
+'run' script:
+
+    $ ./run . origin/next /path/to/git-tree p0001-rev-list.sh
+
+where . stands for the current git tree.  The full invocation is
+
+    ./run [<revision|directory>...] [--] [<test-script>...]
+
+A '.' argument is implied if you do not pass any other
+revisions/directories.
+
+You can also manually test this or another git build tree, and then
+call the aggregation script to summarize the results:
+
+    $ ./p0001-rev-list.sh
+    [...]
+    $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh
+    [...]
+    $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh
+
+aggregate.perl has the same invocation as 'run', it just does not run
+anything beforehand.
+
+You can set the following variables (also in your config.mak):
+
+    GIT_PERF_REPEAT_COUNT
+	Number of times a test should be repeated for best-of-N
+	measurements.  Defaults to 5.
+
+    GIT_PERF_MAKE_OPTS
+	Options to use when automatically building a git tree for
+	performance testing.  E.g., -j6 would be useful.
+
+    GIT_PERF_REPO
+    GIT_PERF_LARGE_REPO
+	Repositories to copy for the performance tests.  The normal
+	repo should be at least git.git size.  The large repo should
+	probably be about linux-2.6.git size for optimal results.
+	Both default to the git.git you are running from.
+
+You can also pass the options taken by ordinary git tests; the most
+useful one is:
+
+--root=<directory>::
+	Create "trash" directories used to store all temporary data during
+	testing under <directory>, instead of the t/ directory.
+	Using this option with a RAM-based filesystem (such as tmpfs)
+	can massively speed up the test suite.
+
+
+Naming Tests
+------------
+
+The performance test files are named as:
+
+	pNNNN-commandname-details.sh
+
+where N is a decimal digit.  The same conventions for choosing NNNN as
+for normal tests apply.
+
+
+Writing Tests
+-------------
+
+The perf script starts much like a normal test script, except it
+sources perf-lib.sh:
+
+	#!/bin/sh
+	#
+	# Copyright (c) 2005 Junio C Hamano
+	#
+
+	test_description='xxx performance test'
+	. ./perf-lib.sh
+
+After that you will want to use some of the following:
+
+	test_perf_default_repo  # sets up a "normal" repository
+	test_perf_large_repo    # sets up a "large" repository
+
+	test_perf_default_repo sub  # ditto, in a subdir "sub"
+
+        test_checkout_worktree  # if you need the worktree too
+
+At least one of the first two is required!
+
+You can use test_expect_success as usual.  For actual performance
+tests, use
+
+	test_perf 'descriptive string' '
+		command1 &&
+		command2
+	'
+
+test_perf spawns a subshell, for lack of better options.  This means
+that
+
+* you _must_ export all variables that you need in the subshell
+
+* you _must_ flag all variables that you want to persist from the
+  subshell with 'test_export':
+
+	test_perf 'descriptive string' '
+		foo=$(git rev-parse HEAD) &&
+		test_export foo
+	'
+
+  The so-exported variables are automatically marked for export in the
+  shell executing the perf test.  For your convenience, test_export is
+  the same as export in the main shell.
+
+  This feature relies on a bit of magic using 'set' and 'source'.
+  While we have tried to make sure that it can cope with embedded
+  whitespace and other special characters, it will not work with
+  multi-line data.
diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl
new file mode 100755
index 0000000..15f7fc1
--- /dev/null
+++ b/t/perf/aggregate.perl
@@ -0,0 +1,166 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Git;
+
+sub get_times {
+	my $name = shift;
+	open my $fh, "<", $name or return undef;
+	my $line = <$fh>;
+	return undef if not defined $line;
+	close $fh or die "cannot close $name: $!";
+	$line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
+		or die "bad input line: $line";
+	my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+	return ($rt, $4, $5);
+}
+
+sub format_times {
+	my ($r, $u, $s, $firstr) = @_;
+	if (!defined $r) {
+		return "<missing>";
+	}
+	my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s;
+	if (defined $firstr) {
+		if ($firstr > 0) {
+			$out .= sprintf " %+.1f%%", 100.0*($r-$firstr)/$firstr;
+		} elsif ($r == 0) {
+			$out .= " =";
+		} else {
+			$out .= " +inf";
+		}
+	}
+	return $out;
+}
+
+my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests);
+while (scalar @ARGV) {
+	my $arg = $ARGV[0];
+	my $dir;
+	last if -f $arg or $arg eq "--";
+	if (! -d $arg) {
+		my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
+		$dir = "build/".$rev;
+	} else {
+		$arg =~ s{/*$}{};
+		$dir = $arg;
+		$dirabbrevs{$dir} = $dir;
+	}
+	push @dirs, $dir;
+	$dirnames{$dir} = $arg;
+	my $prefix = $dir;
+	$prefix =~ tr/^a-zA-Z0-9/_/c;
+	$prefixes{$dir} = $prefix . '.';
+	shift @ARGV;
+}
+
+if (not @dirs) {
+	@dirs = ('.');
+}
+$dirnames{'.'} = $dirabbrevs{'.'} = "this tree";
+$prefixes{'.'} = '';
+
+shift @ARGV if scalar @ARGV and $ARGV[0] eq "--";
+
+@tests = @ARGV;
+if (not @tests) {
+	@tests = glob "p????-*.sh";
+}
+
+my @subtests;
+my %shorttests;
+for my $t (@tests) {
+	$t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t";
+	my $n = $2;
+	my $fname = "test-results/$t.subtests";
+	open my $fp, "<", $fname or die "cannot open $fname: $!";
+	for (<$fp>) {
+		chomp;
+		/^(\d+)$/ or die "malformed subtest line: $_";
+		push @subtests, "$t.$1";
+		$shorttests{"$t.$1"} = "$n.$1";
+	}
+	close $fp or die "cannot close $fname: $!";
+}
+
+sub read_descr {
+	my $name = shift;
+	open my $fh, "<", $name or return "<error reading description>";
+	my $line = <$fh>;
+	close $fh or die "cannot close $name";
+	chomp $line;
+	return $line;
+}
+
+my %descrs;
+my $descrlen = 4; # "Test"
+for my $t (@subtests) {
+	$descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr");
+	$descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
+}
+
+sub have_duplicate {
+	my %seen;
+	for (@_) {
+		return 1 if exists $seen{$_};
+		$seen{$_} = 1;
+	}
+	return 0;
+}
+sub have_slash {
+	for (@_) {
+		return 1 if m{/};
+	}
+	return 0;
+}
+
+my %newdirabbrevs = %dirabbrevs;
+while (!have_duplicate(values %newdirabbrevs)) {
+	%dirabbrevs = %newdirabbrevs;
+	last if !have_slash(values %dirabbrevs);
+	%newdirabbrevs = %dirabbrevs;
+	for (values %newdirabbrevs) {
+		s{^[^/]*/}{};
+	}
+}
+
+my %times;
+my @colwidth = ((0)x@dirs);
+for my $i (0..$#dirs) {
+	my $d = $dirs[$i];
+	my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
+	$colwidth[$i] = $w if $w > $colwidth[$i];
+}
+for my $t (@subtests) {
+	my $firstr;
+	for my $i (0..$#dirs) {
+		my $d = $dirs[$i];
+		$times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")];
+		my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
+		my $w = length format_times($r,$u,$s,$firstr);
+		$colwidth[$i] = $w if $w > $colwidth[$i];
+		$firstr = $r unless defined $firstr;
+	}
+}
+my $totalwidth = 3*@dirs+$descrlen;
+$totalwidth += $_ for (@colwidth);
+
+printf "%-${descrlen}s", "Test";
+for my $i (0..$#dirs) {
+	my $d = $dirs[$i];
+	printf "   %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
+}
+print "\n";
+print "-"x$totalwidth, "\n";
+for my $t (@subtests) {
+	printf "%-${descrlen}s", $descrs{$t};
+	my $firstr;
+	for my $i (0..$#dirs) {
+		my $d = $dirs[$i];
+		my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
+		printf "   %-$colwidth[$i]s", format_times($r,$u,$s,$firstr);
+		$firstr = $r unless defined $firstr;
+	}
+	print "\n";
+}
diff --git a/t/perf/min_time.perl b/t/perf/min_time.perl
new file mode 100755
index 0000000..c1a2717
--- /dev/null
+++ b/t/perf/min_time.perl
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+my $minrt = 1e100;
+my $min;
+
+while (<>) {
+	# [h:]m:s.xx U.xx S.xx
+	/^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/
+		or die "bad input line: $_";
+	my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3;
+	if ($rt < $minrt) {
+		$min = $_;
+		$minrt = $rt;
+	}
+}
+
+if (!defined $min) {
+	die "no input found";
+}
+
+print $min;
diff --git a/t/perf/p0000-perf-lib-sanity.sh b/t/perf/p0000-perf-lib-sanity.sh
new file mode 100755
index 0000000..2ca4aac
--- /dev/null
+++ b/t/perf/p0000-perf-lib-sanity.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='Tests whether perf-lib facilities work'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'test_perf_default_repo works' '
+	foo=$(git rev-parse HEAD) &&
+	test_export foo
+'
+
+test_checkout_worktree
+
+test_perf 'test_checkout_worktree works' '
+	wt=$(find . | wc -l) &&
+	idx=$(git ls-files | wc -l) &&
+	test $wt -gt $idx
+'
+
+baz=baz
+test_export baz
+
+test_expect_success 'test_export works' '
+	echo "$foo" &&
+	test "$foo" = "$(git rev-parse HEAD)" &&
+	echo "$baz" &&
+	test "$baz" = baz
+'
+
+test_perf 'export a weird var' '
+	bar="weird # variable" &&
+	test_export bar
+'
+
+test_expect_success 'test_export works with weird vars' '
+	echo "$bar" &&
+	test "$bar" = "weird # variable"
+'
+
+test_done
diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh
new file mode 100755
index 0000000..4f71a63
--- /dev/null
+++ b/t/perf/p0001-rev-list.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+test_description="Tests history walking performance"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+test_perf 'rev-list --all' '
+	git rev-list --all >/dev/null
+'
+
+test_perf 'rev-list --all --objects' '
+	git rev-list --all --objects >/dev/null
+'
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
new file mode 100644
index 0000000..07e9b09
--- /dev/null
+++ b/t/perf/perf-lib.sh
@@ -0,0 +1,198 @@
+#!/bin/bash
+#
+# Copyright (c) 2011 Thomas Rast
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/ .
+
+# do the --tee work early; it otherwise confuses our careful
+# GIT_BUILD_DIR mangling
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+	# do not redirect again
+	;;
+*' --tee '*|*' --va'*)
+	mkdir -p test-results
+	BASE=test-results/$(basename "$0" .sh)
+	(GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+	 echo $? > $BASE.exit) | tee $BASE.out
+	test "$(cat $BASE.exit)" = 0
+	exit
+	;;
+esac
+
+TEST_DIRECTORY=$(pwd)/..
+TEST_OUTPUT_DIRECTORY=$(pwd)
+if test -z "$GIT_TEST_INSTALLED"; then
+	perf_results_prefix=
+else
+	perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
+	# make the tested dir absolute
+	GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd)
+fi
+
+TEST_NO_CREATE_REPO=t
+
+. ../test-lib.sh
+
+perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results
+mkdir -p "$perf_results_dir"
+rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests
+
+if test -z "$GIT_PERF_REPEAT_COUNT"; then
+	GIT_PERF_REPEAT_COUNT=3
+fi
+die_if_build_dir_not_repo () {
+	if ! ( cd "$TEST_DIRECTORY/.." &&
+		    git rev-parse --build-dir >/dev/null 2>&1 ); then
+		error "No $1 defined, and your build directory is not a repo"
+	fi
+}
+
+if test -z "$GIT_PERF_REPO"; then
+	die_if_build_dir_not_repo '$GIT_PERF_REPO'
+	GIT_PERF_REPO=$TEST_DIRECTORY/..
+fi
+if test -z "$GIT_PERF_LARGE_REPO"; then
+	die_if_build_dir_not_repo '$GIT_PERF_LARGE_REPO'
+	GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/..
+fi
+
+test_perf_create_repo_from () {
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 parameters to test-create-repo"
+	repo="$1"
+	source="$2"
+	source_git=$source/$(cd "$source" && git rev-parse --git-dir)
+	mkdir -p "$repo/.git"
+	(
+		cd "$repo/.git" &&
+		{ cp -Rl "$source_git/objects" . 2>/dev/null ||
+			cp -R "$source_git/objects" .; } &&
+		for stuff in "$source_git"/*; do
+			case "$stuff" in
+				*/objects|*/hooks|*/config)
+					;;
+				*)
+					cp -R "$stuff" . || break
+					;;
+			esac
+		done &&
+		cd .. &&
+		git init -q &&
+		mv .git/hooks .git/hooks-disabled 2>/dev/null
+	) || error "failed to copy repository '$source' to '$repo'"
+}
+
+# call at least one of these to establish an appropriately-sized repository
+test_perf_default_repo () {
+	test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO"
+}
+test_perf_large_repo () {
+	if test "$GIT_PERF_LARGE_REPO" = "$GIT_BUILD_DIR"; then
+		echo "warning: \$GIT_PERF_LARGE_REPO is \$GIT_BUILD_DIR." >&2
+		echo "warning: This will work, but may not be a sufficiently large repo" >&2
+		echo "warning: for representative measurements." >&2
+	fi
+	test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_LARGE_REPO"
+}
+test_checkout_worktree () {
+	git checkout-index -u -a ||
+	error "git checkout-index failed"
+}
+
+# Performance tests should never fail.  If they do, stop immediately
+immediate=t
+
+test_run_perf_ () {
+	test_cleanup=:
+	test_export_="test_cleanup"
+	export test_cleanup test_export_
+	/usr/bin/time -f "%E %U %S" -o test_time.$i "$SHELL" -c '
+. '"$TEST_DIRECTORY"/../test-lib-functions.sh'
+test_export () {
+	[ $# != 0 ] || return 0
+	test_export_="$test_export_\\|$1"
+	shift
+	test_export "$@"
+}
+'"$1"'
+ret=$?
+set | sed -n "s'"/'/'\\\\''/g"';s/^\\($test_export_\\)/export '"'&'"'/p" >test_vars
+exit $ret' >&3 2>&4
+	eval_ret=$?
+
+	if test $eval_ret = 0 || test -n "$expecting_failure"
+	then
+		test_eval_ "$test_cleanup"
+		source ./test_vars || error "failed to load updated environment"
+	fi
+	if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+		echo ""
+	fi
+	return "$eval_ret"
+}
+
+
+test_perf () {
+	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+	export test_prereq
+	if ! test_skip "$@"
+	then
+		base=$(basename "$0" .sh)
+		echo "$test_count" >>"$perf_results_dir"/$base.subtests
+		echo "$1" >"$perf_results_dir"/$base.$test_count.descr
+		if test -z "$verbose"; then
+			echo -n "perf $test_count - $1:"
+		else
+			echo "perf $test_count - $1:"
+		fi
+		for i in $(seq 1 $GIT_PERF_REPEAT_COUNT); do
+			say >&3 "running: $2"
+			if test_run_perf_ "$2"
+			then
+				if test -z "$verbose"; then
+					echo -n " $i"
+				else
+					echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:"
+				fi
+			else
+				test -z "$verbose" && echo
+				test_failure_ "$@"
+				break
+			fi
+		done
+		if test -z "$verbose"; then
+			echo " ok"
+		else
+			test_ok_ "$1"
+		fi
+		base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
+		"$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
+	fi
+	echo >&3 ""
+}
+
+# We extend test_done to print timings at the end (./run disables this
+# and does it after running everything)
+test_at_end_hook_ () {
+	if test -z "$GIT_PERF_AGGREGATING_LATER"; then
+		( cd "$TEST_DIRECTORY"/perf && ./aggregate.perl $(basename "$0") )
+	fi
+}
+
+test_export () {
+	export "$@"
+}
diff --git a/t/perf/run b/t/perf/run
new file mode 100755
index 0000000..cfd7012
--- /dev/null
+++ b/t/perf/run
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+case "$1" in
+	--help)
+		echo "usage: $0 [other_git_tree...] [--] [test_scripts]"
+		exit 0
+		;;
+esac
+
+die () {
+	echo >&2 "error: $*"
+	exit 1
+}
+
+run_one_dir () {
+	if test $# -eq 0; then
+		set -- p????-*.sh
+	fi
+	echo "=== Running $# tests in ${GIT_TEST_INSTALLED:-this tree} ==="
+	for t in "$@"; do
+		./$t $GIT_TEST_OPTS
+	done
+}
+
+unpack_git_rev () {
+	rev=$1
+	mkdir -p build/$rev
+	(cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) |
+	(cd build/$rev && tar x)
+}
+build_git_rev () {
+	rev=$1
+	cp ../../config.mak build/$rev/config.mak
+	(cd build/$rev && make $GIT_PERF_MAKE_OPTS) ||
+	die "failed to build revision '$mydir'"
+}
+
+run_dirs_helper () {
+	mydir=${1%/}
+	shift
+	while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+		shift
+	done
+	if test $# -gt 0 -a "$1" = --; then
+		shift
+	fi
+	if [ ! -d "$mydir" ]; then
+		rev=$(git rev-parse --verify "$mydir" 2>/dev/null) ||
+		die "'$mydir' is neither a directory nor a valid revision"
+		if [ ! -d build/$rev ]; then
+			unpack_git_rev $rev
+		fi
+		build_git_rev $rev
+		mydir=build/$rev
+	fi
+	if test "$mydir" = .; then
+		unset GIT_TEST_INSTALLED
+	else
+		GIT_TEST_INSTALLED="$mydir/bin-wrappers"
+		export GIT_TEST_INSTALLED
+	fi
+	run_one_dir "$@"
+}
+
+run_dirs () {
+	while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do
+		run_dirs_helper "$@"
+		shift
+	done
+}
+
+GIT_PERF_AGGREGATING_LATER=t
+export GIT_PERF_AGGREGATING_LATER
+
+cd "$(dirname $0)"
+. ../../GIT-BUILD-OPTIONS
+
+if test $# = 0 -o "$1" = -- -o -f "$1"; then
+	set -- . "$@"
+fi
+run_dirs "$@"
+./aggregate.perl "$@"
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1da3f40..d75766a 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -55,6 +55,7 @@ unset $(perl -e '
 		.*_TEST
 		PROVE
 		VALGRIND
+		PERF_AGGREGATING_LATER
 	));
 	my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env);
 	print join("\n", @vars);
@@ -98,6 +99,8 @@ _z40=0000000000000000000000000000000000000000
 LF='
 '
 
+export _x05 _x40 _z40 LF
+
 # Each test should start with something like this, after copyright notices:
 #
 # test_description='Description of this test...
@@ -313,11 +316,16 @@ test_skip () {
 	esac
 }
 
+# stub; perf-lib overrides it
+test_at_end_hook_ () {
+	:
+}
+
 test_done () {
 	GIT_EXIT_OK=t
 
 	if test -z "$HARNESS_ACTIVE"; then
-		test_results_dir="$TEST_DIRECTORY/test-results"
+		test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
 		mkdir -p "$test_results_dir"
 		test_results_path="$test_results_dir/${0%.sh}-$$.counts"
 
@@ -356,6 +364,8 @@ test_done () {
 		cd "$(dirname "$remove_trash")" &&
 		rm -rf "$(basename "$remove_trash")"
 
+		test_at_end_hook_
+
 		exit 0 ;;
 
 	*)
@@ -378,6 +388,12 @@ then
 	# itself.
 	TEST_DIRECTORY=$(pwd)
 fi
+if test -z "$TEST_OUTPUT_DIRECTORY"
+then
+	# Similarly, override this to store the test-results subdir
+	# elsewhere
+	TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY
+fi
 GIT_BUILD_DIR="$TEST_DIRECTORY"/..
 
 if test -n "$valgrind"
@@ -513,7 +529,7 @@ test="trash directory.$(basename "$0" .sh)"
 test -n "$root" && test="$root/$test"
 case "$test" in
 /*) TRASH_DIRECTORY="$test" ;;
- *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+ *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$test" ;;
 esac
 test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
 rm -fr "$test" || {
@@ -525,7 +541,11 @@ rm -fr "$test" || {
 HOME="$TRASH_DIRECTORY"
 export HOME
 
-test_create_repo "$test"
+if test -z "$TEST_NO_CREATE_REPO"; then
+	test_create_repo "$test"
+else
+	mkdir -p "$test"
+fi
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$test" || exit 1
-- 
1.7.9.1.365.ge223f

^ permalink raw reply related

* [PATCH v3 3/3] Add a performance test for git-grep
From: Thomas Rast @ 2012-02-17 10:25 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <cover.1329472405.git.trast@student.ethz.ch>

The only catch is that we don't really know what our repo contains, so
we have to ignore any possible "not found" status from git-grep.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 t/perf/p7810-grep.sh |   23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100755 t/perf/p7810-grep.sh

diff --git a/t/perf/p7810-grep.sh b/t/perf/p7810-grep.sh
new file mode 100755
index 0000000..9f4ade6
--- /dev/null
+++ b/t/perf/p7810-grep.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description="git-grep performance in various modes"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+test_perf 'grep worktree, cheap regex' '
+	git grep some_nonexistent_string || :
+'
+test_perf 'grep worktree, expensive regex' '
+	git grep "^.* *some_nonexistent_string$" || :
+'
+test_perf 'grep --cached, cheap regex' '
+	git grep --cached some_nonexistent_string || :
+'
+test_perf 'grep --cached, expensive regex' '
+	git grep --cached "^.* *some_nonexistent_string$" || :
+'
+
+test_done
-- 
1.7.9.1.365.ge223f

^ permalink raw reply related

* [PATCH v3 1/3] Move the user-facing test library to test-lib-functions.sh
From: Thomas Rast @ 2012-02-17 10:25 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <cover.1329472405.git.trast@student.ethz.ch>

This just moves all the user-facing functions to a separate file and
sources that instead.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---
 t/test-lib-functions.sh |  565 +++++++++++++++++++++++++++++++++++++++++++++++
 t/test-lib.sh           |  552 +--------------------------------------------
 2 files changed, 568 insertions(+), 549 deletions(-)
 create mode 100644 t/test-lib-functions.sh

diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
new file mode 100644
index 0000000..7b3b4be
--- /dev/null
+++ b/t/test-lib-functions.sh
@@ -0,0 +1,565 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/ .
+
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using.
+test_set_editor () {
+	FAKE_EDITOR="$1"
+	export FAKE_EDITOR
+	EDITOR='"$FAKE_EDITOR"'
+	export EDITOR
+}
+
+test_decode_color () {
+	awk '
+		function name(n) {
+			if (n == 0) return "RESET";
+			if (n == 1) return "BOLD";
+			if (n == 30) return "BLACK";
+			if (n == 31) return "RED";
+			if (n == 32) return "GREEN";
+			if (n == 33) return "YELLOW";
+			if (n == 34) return "BLUE";
+			if (n == 35) return "MAGENTA";
+			if (n == 36) return "CYAN";
+			if (n == 37) return "WHITE";
+			if (n == 40) return "BLACK";
+			if (n == 41) return "BRED";
+			if (n == 42) return "BGREEN";
+			if (n == 43) return "BYELLOW";
+			if (n == 44) return "BBLUE";
+			if (n == 45) return "BMAGENTA";
+			if (n == 46) return "BCYAN";
+			if (n == 47) return "BWHITE";
+		}
+		{
+			while (match($0, /\033\[[0-9;]*m/) != 0) {
+				printf "%s<", substr($0, 1, RSTART-1);
+				codes = substr($0, RSTART+2, RLENGTH-3);
+				if (length(codes) == 0)
+					printf "%s", name(0)
+				else {
+					n = split(codes, ary, ";");
+					sep = "";
+					for (i = 1; i <= n; i++) {
+						printf "%s%s", sep, name(ary[i]);
+						sep = ";"
+					}
+				}
+				printf ">";
+				$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
+			}
+			print
+		}
+	'
+}
+
+nul_to_q () {
+	perl -pe 'y/\000/Q/'
+}
+
+q_to_nul () {
+	perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+	tr Q '\015'
+}
+
+q_to_tab () {
+	tr Q '\011'
+}
+
+append_cr () {
+	sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+	tr '\015' Q | sed -e 's/Q$//'
+}
+
+# In some bourne shell implementations, the "unset" builtin returns
+# nonzero status when a variable to be unset was not set in the first
+# place.
+#
+# Use sane_unset when that should not be considered an error.
+
+sane_unset () {
+	unset "$@"
+	return 0
+}
+
+test_tick () {
+	if test -z "${test_tick+set}"
+	then
+		test_tick=1112911993
+	else
+		test_tick=$(($test_tick + 60))
+	fi
+	GIT_COMMITTER_DATE="$test_tick -0700"
+	GIT_AUTHOR_DATE="$test_tick -0700"
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+# Stop execution and start a shell. This is useful for debugging tests and
+# only makes sense together with "-v".
+#
+# Be sure to remove all invocations of this command before submitting.
+
+test_pause () {
+	if test "$verbose" = t; then
+		"$SHELL_PATH" <&6 >&3 2>&4
+	else
+		error >&5 "test_pause requires --verbose"
+	fi
+}
+
+# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+#
+# This will commit a file with the given contents and the given commit
+# message.  It will also add a tag with <message> as name.
+#
+# Both <file> and <contents> default to <message>.
+
+test_commit () {
+	file=${2:-"$1.t"}
+	echo "${3-$1}" > "$file" &&
+	git add "$file" &&
+	test_tick &&
+	git commit -m "$1" &&
+	git tag "$1"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+	test_tick &&
+	git merge -m "$1" "$2" &&
+	git tag "$1"
+}
+
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+	chmod "$@" &&
+	git update-index --add "--chmod=$@"
+}
+
+# Unset a configuration variable, but don't fail if it doesn't exist.
+test_unconfig () {
+	git config --unset-all "$@"
+	config_status=$?
+	case "$config_status" in
+	5) # ok, nothing to unset
+		config_status=0
+		;;
+	esac
+	return $config_status
+}
+
+# Set git config, automatically unsetting it after the test is over.
+test_config () {
+	test_when_finished "test_unconfig '$1'" &&
+	git config "$@"
+}
+
+test_config_global () {
+	test_when_finished "test_unconfig --global '$1'" &&
+	git config --global "$@"
+}
+
+write_script () {
+	{
+		echo "#!${2-"$SHELL_PATH"}" &&
+		cat
+	} >"$1" &&
+	chmod +x "$1"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+#   test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+	satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+	# prerequisites can be concatenated with ','
+	save_IFS=$IFS
+	IFS=,
+	set -- $*
+	IFS=$save_IFS
+
+	total_prereq=0
+	ok_prereq=0
+	missing_prereq=
+
+	for prerequisite
+	do
+		total_prereq=$(($total_prereq + 1))
+		case $satisfied in
+		*" $prerequisite "*)
+			ok_prereq=$(($ok_prereq + 1))
+			;;
+		*)
+			# Keep a list of missing prerequisites
+			if test -z "$missing_prereq"
+			then
+				missing_prereq=$prerequisite
+			else
+				missing_prereq="$prerequisite,$missing_prereq"
+			fi
+		esac
+	done
+
+	test $total_prereq = $ok_prereq
+}
+
+test_declared_prereq () {
+	case ",$test_prereq," in
+	*,$1,*)
+		return 0
+		;;
+	esac
+	return 1
+}
+
+test_expect_failure () {
+	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+	export test_prereq
+	if ! test_skip "$@"
+	then
+		say >&3 "checking known breakage: $2"
+		if test_run_ "$2" expecting_failure
+		then
+			test_known_broken_ok_ "$1"
+		else
+			test_known_broken_failure_ "$1"
+		fi
+	fi
+	echo >&3 ""
+}
+
+test_expect_success () {
+	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+	export test_prereq
+	if ! test_skip "$@"
+	then
+		say >&3 "expecting success: $2"
+		if test_run_ "$2"
+		then
+			test_ok_ "$1"
+		else
+			test_failure_ "$@"
+		fi
+	fi
+	echo >&3 ""
+}
+
+# test_external runs external test scripts that provide continuous
+# test output about their progress, and succeeds/fails on
+# zero/non-zero exit code.  It outputs the test output on stdout even
+# in non-verbose mode, and announces the external script with "# run
+# <n>: ..." before running it.  When providing relative paths, keep in
+# mind that all scripts run in "trash directory".
+# Usage: test_external description command arguments...
+# Example: test_external 'Perl API' perl ../path/to/test.pl
+test_external () {
+	test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
+	test "$#" = 3 ||
+	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
+	descr="$1"
+	shift
+	export test_prereq
+	if ! test_skip "$descr" "$@"
+	then
+		# Announce the script to reduce confusion about the
+		# test output that follows.
+		say_color "" "# run $test_count: $descr ($*)"
+		# Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
+		# to be able to use them in script
+		export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
+		# Run command; redirect its stderr to &4 as in
+		# test_run_, but keep its stdout on our stdout even in
+		# non-verbose mode.
+		"$@" 2>&4
+		if [ "$?" = 0 ]
+		then
+			if test $test_external_has_tap -eq 0; then
+				test_ok_ "$descr"
+			else
+				say_color "" "# test_external test $descr was ok"
+				test_success=$(($test_success + 1))
+			fi
+		else
+			if test $test_external_has_tap -eq 0; then
+				test_failure_ "$descr" "$@"
+			else
+				say_color error "# test_external test $descr failed: $@"
+				test_failure=$(($test_failure + 1))
+			fi
+		fi
+	fi
+}
+
+# Like test_external, but in addition tests that the command generated
+# no output on stderr.
+test_external_without_stderr () {
+	# The temporary file has no (and must have no) security
+	# implications.
+	tmp=${TMPDIR:-/tmp}
+	stderr="$tmp/git-external-stderr.$$.tmp"
+	test_external "$@" 4> "$stderr"
+	[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
+	descr="no stderr: $1"
+	shift
+	say >&3 "# expecting no stderr from previous command"
+	if [ ! -s "$stderr" ]; then
+		rm "$stderr"
+
+		if test $test_external_has_tap -eq 0; then
+			test_ok_ "$descr"
+		else
+			say_color "" "# test_external_without_stderr test $descr was ok"
+			test_success=$(($test_success + 1))
+		fi
+	else
+		if [ "$verbose" = t ]; then
+			output=`echo; echo "# Stderr is:"; cat "$stderr"`
+		else
+			output=
+		fi
+		# rm first in case test_failure exits.
+		rm "$stderr"
+		if test $test_external_has_tap -eq 0; then
+			test_failure_ "$descr" "$@" "$output"
+		else
+			say_color error "# test_external_without_stderr test $descr failed: $@: $output"
+			test_failure=$(($test_failure + 1))
+		fi
+	fi
+}
+
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+	if ! [ -f "$1" ]
+	then
+		echo "File $1 doesn't exist. $*"
+		false
+	fi
+}
+
+test_path_is_dir () {
+	if ! [ -d "$1" ]
+	then
+		echo "Directory $1 doesn't exist. $*"
+		false
+	fi
+}
+
+test_path_is_missing () {
+	if [ -e "$1" ]
+	then
+		echo "Path exists:"
+		ls -ld "$1"
+		if [ $# -ge 1 ]; then
+			echo "$*"
+		fi
+		false
+	fi
+}
+
+# test_line_count checks that a file has the number of lines it
+# ought to. For example:
+#
+#	test_expect_success 'produce exactly one line of output' '
+#		do something >output &&
+#		test_line_count = 1 output
+#	'
+#
+# is like "test $(wc -l <output) = 1" except that it passes the
+# output through when the number of lines is wrong.
+
+test_line_count () {
+	if test $# != 3
+	then
+		error "bug in the test script: not 3 parameters to test_line_count"
+	elif ! test $(wc -l <"$3") "$1" "$2"
+	then
+		echo "test_line_count: line count for $3 !$1 $2"
+		cat "$3"
+		return 1
+	fi
+}
+
+# This is not among top-level (test_expect_success | test_expect_failure)
+# but is a prefix that can be used in the test script, like:
+#
+#	test_expect_success 'complain and die' '
+#           do something &&
+#           do something else &&
+#	    test_must_fail git checkout ../outerspace
+#	'
+#
+# Writing this as "! git checkout ../outerspace" is wrong, because
+# the failure could be due to a segv.  We want a controlled failure.
+
+test_must_fail () {
+	"$@"
+	exit_code=$?
+	if test $exit_code = 0; then
+		echo >&2 "test_must_fail: command succeeded: $*"
+		return 1
+	elif test $exit_code -gt 129 -a $exit_code -le 192; then
+		echo >&2 "test_must_fail: died by signal: $*"
+		return 1
+	elif test $exit_code = 127; then
+		echo >&2 "test_must_fail: command not found: $*"
+		return 1
+	fi
+	return 0
+}
+
+# Similar to test_must_fail, but tolerates success, too.  This is
+# meant to be used in contexts like:
+#
+#	test_expect_success 'some command works without configuration' '
+#		test_might_fail git config --unset all.configuration &&
+#		do something
+#	'
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+	"$@"
+	exit_code=$?
+	if test $exit_code -gt 129 -a $exit_code -le 192; then
+		echo >&2 "test_might_fail: died by signal: $*"
+		return 1
+	elif test $exit_code = 127; then
+		echo >&2 "test_might_fail: command not found: $*"
+		return 1
+	fi
+	return 0
+}
+
+# Similar to test_must_fail and test_might_fail, but check that a
+# given command exited with a given exit code. Meant to be used as:
+#
+#	test_expect_success 'Merge with d/f conflicts' '
+#		test_expect_code 1 git merge "merge msg" B master
+#	'
+
+test_expect_code () {
+	want_code=$1
+	shift
+	"$@"
+	exit_code=$?
+	if test $exit_code = $want_code
+	then
+		return 0
+	fi
+
+	echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
+	return 1
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+#	test_expect_success 'foo works' '
+#		echo expected >expected &&
+#		foo >actual &&
+#		test_cmp expected actual
+#	'
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+	$GIT_TEST_CMP "$@"
+}
+
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+#	test_expect_success 'test core.capslock' '
+#		git config core.capslock true &&
+#		test_when_finished "git config --unset core.capslock" &&
+#		hello world
+#	'
+#
+# That would be roughly equivalent to
+#
+#	test_expect_success 'test core.capslock' '
+#		git config core.capslock true &&
+#		hello world
+#		git config --unset core.capslock
+#	'
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+#
+# Note that under --immediate mode, no clean-up is done to help diagnose
+# what went wrong.
+
+test_when_finished () {
+	test_cleanup="{ $*
+		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
+# Most tests can use the created repository, but some may need to create more.
+# Usage: test_create_repo <directory>
+test_create_repo () {
+	test "$#" = 1 ||
+	error "bug in the test script: not 1 parameter to test-create-repo"
+	repo="$1"
+	mkdir -p "$repo"
+	(
+		cd "$repo" || error "Cannot setup test environment"
+		"$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 ||
+		error "cannot run git init -- have you built things yet?"
+		mv .git/hooks .git/hooks-disabled
+	) || exit
+}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index e28d5fd..1da3f40 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -223,248 +223,9 @@ die () {
 GIT_EXIT_OK=
 trap 'die' EXIT
 
-# The semantics of the editor variables are that of invoking
-# sh -c "$EDITOR \"$@\"" files ...
-#
-# If our trash directory contains shell metacharacters, they will be
-# interpreted if we just set $EDITOR directly, so do a little dance with
-# environment variables to work around this.
-#
-# In particular, quoting isn't enough, as the path may contain the same quote
-# that we're using.
-test_set_editor () {
-	FAKE_EDITOR="$1"
-	export FAKE_EDITOR
-	EDITOR='"$FAKE_EDITOR"'
-	export EDITOR
-}
-
-test_decode_color () {
-	awk '
-		function name(n) {
-			if (n == 0) return "RESET";
-			if (n == 1) return "BOLD";
-			if (n == 30) return "BLACK";
-			if (n == 31) return "RED";
-			if (n == 32) return "GREEN";
-			if (n == 33) return "YELLOW";
-			if (n == 34) return "BLUE";
-			if (n == 35) return "MAGENTA";
-			if (n == 36) return "CYAN";
-			if (n == 37) return "WHITE";
-			if (n == 40) return "BLACK";
-			if (n == 41) return "BRED";
-			if (n == 42) return "BGREEN";
-			if (n == 43) return "BYELLOW";
-			if (n == 44) return "BBLUE";
-			if (n == 45) return "BMAGENTA";
-			if (n == 46) return "BCYAN";
-			if (n == 47) return "BWHITE";
-		}
-		{
-			while (match($0, /\033\[[0-9;]*m/) != 0) {
-				printf "%s<", substr($0, 1, RSTART-1);
-				codes = substr($0, RSTART+2, RLENGTH-3);
-				if (length(codes) == 0)
-					printf "%s", name(0)
-				else {
-					n = split(codes, ary, ";");
-					sep = "";
-					for (i = 1; i <= n; i++) {
-						printf "%s%s", sep, name(ary[i]);
-						sep = ";"
-					}
-				}
-				printf ">";
-				$0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1);
-			}
-			print
-		}
-	'
-}
-
-nul_to_q () {
-	perl -pe 'y/\000/Q/'
-}
-
-q_to_nul () {
-	perl -pe 'y/Q/\000/'
-}
-
-q_to_cr () {
-	tr Q '\015'
-}
-
-q_to_tab () {
-	tr Q '\011'
-}
-
-append_cr () {
-	sed -e 's/$/Q/' | tr Q '\015'
-}
-
-remove_cr () {
-	tr '\015' Q | sed -e 's/Q$//'
-}
-
-# In some bourne shell implementations, the "unset" builtin returns
-# nonzero status when a variable to be unset was not set in the first
-# place.
-#
-# Use sane_unset when that should not be considered an error.
-
-sane_unset () {
-	unset "$@"
-	return 0
-}
-
-test_tick () {
-	if test -z "${test_tick+set}"
-	then
-		test_tick=1112911993
-	else
-		test_tick=$(($test_tick + 60))
-	fi
-	GIT_COMMITTER_DATE="$test_tick -0700"
-	GIT_AUTHOR_DATE="$test_tick -0700"
-	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-}
-
-# Stop execution and start a shell. This is useful for debugging tests and
-# only makes sense together with "-v".
-#
-# Be sure to remove all invocations of this command before submitting.
-
-test_pause () {
-	if test "$verbose" = t; then
-		"$SHELL_PATH" <&6 >&3 2>&4
-	else
-		error >&5 "test_pause requires --verbose"
-	fi
-}
-
-# Call test_commit with the arguments "<message> [<file> [<contents>]]"
-#
-# This will commit a file with the given contents and the given commit
-# message.  It will also add a tag with <message> as name.
-#
-# Both <file> and <contents> default to <message>.
-
-test_commit () {
-	file=${2:-"$1.t"}
-	echo "${3-$1}" > "$file" &&
-	git add "$file" &&
-	test_tick &&
-	git commit -m "$1" &&
-	git tag "$1"
-}
-
-# Call test_merge with the arguments "<message> <commit>", where <commit>
-# can be a tag pointing to the commit-to-merge.
-
-test_merge () {
-	test_tick &&
-	git merge -m "$1" "$2" &&
-	git tag "$1"
-}
-
-# This function helps systems where core.filemode=false is set.
-# Use it instead of plain 'chmod +x' to set or unset the executable bit
-# of a file in the working directory and add it to the index.
-
-test_chmod () {
-	chmod "$@" &&
-	git update-index --add "--chmod=$@"
-}
-
-# Unset a configuration variable, but don't fail if it doesn't exist.
-test_unconfig () {
-	git config --unset-all "$@"
-	config_status=$?
-	case "$config_status" in
-	5) # ok, nothing to unset
-		config_status=0
-		;;
-	esac
-	return $config_status
-}
-
-# Set git config, automatically unsetting it after the test is over.
-test_config () {
-	test_when_finished "test_unconfig '$1'" &&
-	git config "$@"
-}
-
-
-test_config_global () {
-	test_when_finished "test_unconfig --global '$1'" &&
-	git config --global "$@"
-}
-
-write_script () {
-	{
-		echo "#!${2-"$SHELL_PATH"}" &&
-		cat
-	} >"$1" &&
-	chmod +x "$1"
-}
-
-# Use test_set_prereq to tell that a particular prerequisite is available.
-# The prerequisite can later be checked for in two ways:
-#
-# - Explicitly using test_have_prereq.
-#
-# - Implicitly by specifying the prerequisite tag in the calls to
-#   test_expect_{success,failure,code}.
-#
-# The single parameter is the prerequisite tag (a simple word, in all
-# capital letters by convention).
-
-test_set_prereq () {
-	satisfied="$satisfied$1 "
-}
-satisfied=" "
-
-test_have_prereq () {
-	# prerequisites can be concatenated with ','
-	save_IFS=$IFS
-	IFS=,
-	set -- $*
-	IFS=$save_IFS
-
-	total_prereq=0
-	ok_prereq=0
-	missing_prereq=
-
-	for prerequisite
-	do
-		total_prereq=$(($total_prereq + 1))
-		case $satisfied in
-		*" $prerequisite "*)
-			ok_prereq=$(($ok_prereq + 1))
-			;;
-		*)
-			# Keep a list of missing prerequisites
-			if test -z "$missing_prereq"
-			then
-				missing_prereq=$prerequisite
-			else
-				missing_prereq="$prerequisite,$missing_prereq"
-			fi
-		esac
-	done
-
-	test $total_prereq = $ok_prereq
-}
-
-test_declared_prereq () {
-	case ",$test_prereq," in
-	*,$1,*)
-		return 0
-		;;
-	esac
-	return 1
-}
+# The user-facing functions are loaded from a separate file so that
+# test_perf subshells can have them too
+. "${TEST_DIRECTORY:-.}"/test-lib-functions.sh
 
 # You are not expected to call test_ok_ and test_failure_ directly, use
 # the text_expect_* functions instead.
@@ -552,313 +313,6 @@ test_skip () {
 	esac
 }
 
-test_expect_failure () {
-	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
-	test "$#" = 2 ||
-	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
-	export test_prereq
-	if ! test_skip "$@"
-	then
-		say >&3 "checking known breakage: $2"
-		if test_run_ "$2" expecting_failure
-		then
-			test_known_broken_ok_ "$1"
-		else
-			test_known_broken_failure_ "$1"
-		fi
-	fi
-	echo >&3 ""
-}
-
-test_expect_success () {
-	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
-	test "$#" = 2 ||
-	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
-	export test_prereq
-	if ! test_skip "$@"
-	then
-		say >&3 "expecting success: $2"
-		if test_run_ "$2"
-		then
-			test_ok_ "$1"
-		else
-			test_failure_ "$@"
-		fi
-	fi
-	echo >&3 ""
-}
-
-# test_external runs external test scripts that provide continuous
-# test output about their progress, and succeeds/fails on
-# zero/non-zero exit code.  It outputs the test output on stdout even
-# in non-verbose mode, and announces the external script with "# run
-# <n>: ..." before running it.  When providing relative paths, keep in
-# mind that all scripts run in "trash directory".
-# Usage: test_external description command arguments...
-# Example: test_external 'Perl API' perl ../path/to/test.pl
-test_external () {
-	test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq=
-	test "$#" = 3 ||
-	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
-	descr="$1"
-	shift
-	export test_prereq
-	if ! test_skip "$descr" "$@"
-	then
-		# Announce the script to reduce confusion about the
-		# test output that follows.
-		say_color "" "# run $test_count: $descr ($*)"
-		# Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
-		# to be able to use them in script
-		export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
-		# Run command; redirect its stderr to &4 as in
-		# test_run_, but keep its stdout on our stdout even in
-		# non-verbose mode.
-		"$@" 2>&4
-		if [ "$?" = 0 ]
-		then
-			if test $test_external_has_tap -eq 0; then
-				test_ok_ "$descr"
-			else
-				say_color "" "# test_external test $descr was ok"
-				test_success=$(($test_success + 1))
-			fi
-		else
-			if test $test_external_has_tap -eq 0; then
-				test_failure_ "$descr" "$@"
-			else
-				say_color error "# test_external test $descr failed: $@"
-				test_failure=$(($test_failure + 1))
-			fi
-		fi
-	fi
-}
-
-# Like test_external, but in addition tests that the command generated
-# no output on stderr.
-test_external_without_stderr () {
-	# The temporary file has no (and must have no) security
-	# implications.
-	tmp=${TMPDIR:-/tmp}
-	stderr="$tmp/git-external-stderr.$$.tmp"
-	test_external "$@" 4> "$stderr"
-	[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
-	descr="no stderr: $1"
-	shift
-	say >&3 "# expecting no stderr from previous command"
-	if [ ! -s "$stderr" ]; then
-		rm "$stderr"
-
-		if test $test_external_has_tap -eq 0; then
-			test_ok_ "$descr"
-		else
-			say_color "" "# test_external_without_stderr test $descr was ok"
-			test_success=$(($test_success + 1))
-		fi
-	else
-		if [ "$verbose" = t ]; then
-			output=`echo; echo "# Stderr is:"; cat "$stderr"`
-		else
-			output=
-		fi
-		# rm first in case test_failure exits.
-		rm "$stderr"
-		if test $test_external_has_tap -eq 0; then
-			test_failure_ "$descr" "$@" "$output"
-		else
-			say_color error "# test_external_without_stderr test $descr failed: $@: $output"
-			test_failure=$(($test_failure + 1))
-		fi
-	fi
-}
-
-# debugging-friendly alternatives to "test [-f|-d|-e]"
-# The commands test the existence or non-existence of $1. $2 can be
-# given to provide a more precise diagnosis.
-test_path_is_file () {
-	if ! [ -f "$1" ]
-	then
-		echo "File $1 doesn't exist. $*"
-		false
-	fi
-}
-
-test_path_is_dir () {
-	if ! [ -d "$1" ]
-	then
-		echo "Directory $1 doesn't exist. $*"
-		false
-	fi
-}
-
-test_path_is_missing () {
-	if [ -e "$1" ]
-	then
-		echo "Path exists:"
-		ls -ld "$1"
-		if [ $# -ge 1 ]; then
-			echo "$*"
-		fi
-		false
-	fi
-}
-
-# test_line_count checks that a file has the number of lines it
-# ought to. For example:
-#
-#	test_expect_success 'produce exactly one line of output' '
-#		do something >output &&
-#		test_line_count = 1 output
-#	'
-#
-# is like "test $(wc -l <output) = 1" except that it passes the
-# output through when the number of lines is wrong.
-
-test_line_count () {
-	if test $# != 3
-	then
-		error "bug in the test script: not 3 parameters to test_line_count"
-	elif ! test $(wc -l <"$3") "$1" "$2"
-	then
-		echo "test_line_count: line count for $3 !$1 $2"
-		cat "$3"
-		return 1
-	fi
-}
-
-# This is not among top-level (test_expect_success | test_expect_failure)
-# but is a prefix that can be used in the test script, like:
-#
-#	test_expect_success 'complain and die' '
-#           do something &&
-#           do something else &&
-#	    test_must_fail git checkout ../outerspace
-#	'
-#
-# Writing this as "! git checkout ../outerspace" is wrong, because
-# the failure could be due to a segv.  We want a controlled failure.
-
-test_must_fail () {
-	"$@"
-	exit_code=$?
-	if test $exit_code = 0; then
-		echo >&2 "test_must_fail: command succeeded: $*"
-		return 1
-	elif test $exit_code -gt 129 -a $exit_code -le 192; then
-		echo >&2 "test_must_fail: died by signal: $*"
-		return 1
-	elif test $exit_code = 127; then
-		echo >&2 "test_must_fail: command not found: $*"
-		return 1
-	fi
-	return 0
-}
-
-# Similar to test_must_fail, but tolerates success, too.  This is
-# meant to be used in contexts like:
-#
-#	test_expect_success 'some command works without configuration' '
-#		test_might_fail git config --unset all.configuration &&
-#		do something
-#	'
-#
-# Writing "git config --unset all.configuration || :" would be wrong,
-# because we want to notice if it fails due to segv.
-
-test_might_fail () {
-	"$@"
-	exit_code=$?
-	if test $exit_code -gt 129 -a $exit_code -le 192; then
-		echo >&2 "test_might_fail: died by signal: $*"
-		return 1
-	elif test $exit_code = 127; then
-		echo >&2 "test_might_fail: command not found: $*"
-		return 1
-	fi
-	return 0
-}
-
-# Similar to test_must_fail and test_might_fail, but check that a
-# given command exited with a given exit code. Meant to be used as:
-#
-#	test_expect_success 'Merge with d/f conflicts' '
-#		test_expect_code 1 git merge "merge msg" B master
-#	'
-
-test_expect_code () {
-	want_code=$1
-	shift
-	"$@"
-	exit_code=$?
-	if test $exit_code = $want_code
-	then
-		return 0
-	fi
-
-	echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
-	return 1
-}
-
-# test_cmp is a helper function to compare actual and expected output.
-# You can use it like:
-#
-#	test_expect_success 'foo works' '
-#		echo expected >expected &&
-#		foo >actual &&
-#		test_cmp expected actual
-#	'
-#
-# This could be written as either "cmp" or "diff -u", but:
-# - cmp's output is not nearly as easy to read as diff -u
-# - not all diff versions understand "-u"
-
-test_cmp() {
-	$GIT_TEST_CMP "$@"
-}
-
-# This function can be used to schedule some commands to be run
-# unconditionally at the end of the test to restore sanity:
-#
-#	test_expect_success 'test core.capslock' '
-#		git config core.capslock true &&
-#		test_when_finished "git config --unset core.capslock" &&
-#		hello world
-#	'
-#
-# That would be roughly equivalent to
-#
-#	test_expect_success 'test core.capslock' '
-#		git config core.capslock true &&
-#		hello world
-#		git config --unset core.capslock
-#	'
-#
-# except that the greeting and config --unset must both succeed for
-# the test to pass.
-#
-# Note that under --immediate mode, no clean-up is done to help diagnose
-# what went wrong.
-
-test_when_finished () {
-	test_cleanup="{ $*
-		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
-}
-
-# Most tests can use the created repository, but some may need to create more.
-# Usage: test_create_repo <directory>
-test_create_repo () {
-	test "$#" = 1 ||
-	error "bug in the test script: not 1 parameter to test-create-repo"
-	repo="$1"
-	mkdir -p "$repo"
-	(
-		cd "$repo" || error "Cannot setup test environment"
-		"$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 ||
-		error "cannot run git init -- have you built things yet?"
-		mv .git/hooks .git/hooks-disabled
-	) || exit
-}
-
 test_done () {
 	GIT_EXIT_OK=t
 
-- 
1.7.9.1.365.ge223f

^ permalink raw reply related

* [PATCH v3 0/3]
From: Thomas Rast @ 2012-02-17 10:25 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <7v7gzmxw78.fsf@alter.siamese.dyndns.org>

Junio C Hamano wrote:
> > ---
> >  t/test-lib-functions.sh |  835 +++++++++++++++++++++++++++++++++++++++++++++++
> >  t/test-lib.sh           |  552 +-------------------------------
> >  2 files changed, 840 insertions(+), 547 deletions(-)
> >  create mode 100644 t/test-lib-functions.sh
> 
> I would have expected from the log description that the number of deleted
> lines would be about the same as the number of added lines, and the
> difference would primarily come from the addition of "include" aka "dot"
> ". ./test-lib-functions.sh" that becomes necessary in t/test-lib.sh, some
> boilerplate material at the beginning of the new file e.g. "#!/bin/sh",
> and copying (not moving) the same Copyright block to the new file.

There were actually more mistakes lurking :-( so I am resending the
whole series.  I also put in the copyright that you asked for.  I
verified the results by looking at the diff between a reverse git-show
for test-lib.sh and a forward git-show for test-lib-functions.sh,
which looks as follows:

  --- /dev/fd/63	2012-02-17 10:55:32.994197654 +0100
  +++ /dev/fd/62	2012-02-17 10:55:32.994197654 +0100
  @@ -9,17 +9,29 @@
       
       Signed-off-by: Thomas Rast <trast@student.ethz.ch>
   
  -diff --git b/t/test-lib.sh a/t/test-lib.sh
  -index 1da3f40..e28d5fd 100644
  ---- b/t/test-lib.sh
  -+++ a/t/test-lib.sh
  -@@ -223,9 +223,248 @@ die () {
  - GIT_EXIT_OK=
  - trap 'die' EXIT
  - 
  --# The user-facing functions are loaded from a separate file so that
  --# test_perf subshells can have them too
  --. "${TEST_DIRECTORY:-.}"/test-lib-functions.sh
  +diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
  +new file mode 100644
  +index 0000000..7b3b4be
  +--- /dev/null
  ++++ b/t/test-lib-functions.sh
  +@@ -0,0 +1,565 @@
  ++#!/bin/sh
  ++#
  ++# Copyright (c) 2005 Junio C Hamano
  ++#
  ++# This program is free software: you can redistribute it and/or modify
  ++# it under the terms of the GNU General Public License as published by
  ++# the Free Software Foundation, either version 2 of the License, or
  ++# (at your option) any later version.
  ++#
  ++# This program is distributed in the hope that it will be useful,
  ++# but WITHOUT ANY WARRANTY; without even the implied warranty of
  ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  ++# GNU General Public License for more details.
  ++#
  ++# You should have received a copy of the GNU General Public License
  ++# along with this program.  If not, see http://www.gnu.org/licenses/ .
  ++
   +# The semantics of the editor variables are that of invoking
   +# sh -c "$EDITOR \"$@\"" files ...
   +#
  @@ -192,7 +204,6 @@
   +	git config "$@"
   +}
   +
  -+
   +test_config_global () {
   +	test_when_finished "test_unconfig --global '$1'" &&
   +	git config --global "$@"
  @@ -262,13 +273,7 @@
   +	esac
   +	return 1
   +}
  - 
  - # You are not expected to call test_ok_ and test_failure_ directly, use
  - # the text_expect_* functions instead.
  -@@ -313,6 +552,313 @@ test_skip () {
  - 	esac
  - }
  - 
  ++
   +test_expect_failure () {
   +	test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
   +	test "$#" = 2 ||
  @@ -575,7 +580,3 @@
   +		mv .git/hooks .git/hooks-disabled
   +	) || exit
   +}
  -+
  - test_done () {
  - 	GIT_EXIT_OK=t
  - 



Thomas Rast (3):
  Move the user-facing test library to test-lib-functions.sh
  Introduce a performance testing framework
  Add a performance test for git-grep

 Makefile                        |   22 +-
 t/Makefile                      |   43 ++-
 t/perf/.gitignore               |    2 +
 t/perf/Makefile                 |   15 +
 t/perf/README                   |  146 ++++++++++
 t/perf/aggregate.perl           |  166 +++++++++++
 t/perf/min_time.perl            |   21 ++
 t/perf/p0000-perf-lib-sanity.sh |   41 +++
 t/perf/p0001-rev-list.sh        |   17 ++
 t/perf/p7810-grep.sh            |   23 ++
 t/perf/perf-lib.sh              |  198 ++++++++++++++
 t/perf/run                      |   82 ++++++
 t/test-lib-functions.sh         |  565 ++++++++++++++++++++++++++++++++++++++
 t/test-lib.sh                   |  574 ++-------------------------------------
 14 files changed, 1363 insertions(+), 552 deletions(-)
 create mode 100644 t/perf/.gitignore
 create mode 100644 t/perf/Makefile
 create mode 100644 t/perf/README
 create mode 100755 t/perf/aggregate.perl
 create mode 100755 t/perf/min_time.perl
 create mode 100755 t/perf/p0000-perf-lib-sanity.sh
 create mode 100755 t/perf/p0001-rev-list.sh
 create mode 100755 t/perf/p7810-grep.sh
 create mode 100644 t/perf/perf-lib.sh
 create mode 100755 t/perf/run
 create mode 100644 t/test-lib-functions.sh

-- 
1.7.9.1.365.ge223f

^ permalink raw reply

* git-status does not propagate -uall to submodules
From: Thomas Rast @ 2012-02-17  9:18 UTC (permalink / raw)
  To: git; +Cc: Jens Lehmann

Hi,

While helping with the submodule display on #git I noticed that if you
have a submodule with status.showuntrackedfiles=no, and run 'git status
-uall' from the superproject, then this does not propagate into the
submodule's status.  In code:

  $ (cd bar && git config status.showuntrackedfiles)                  
  no                                                                                                 
  $ git ls-files -s                                                   
  100644 926c01b7259c489a422442a8dc5cb5ea7c58f60c 0       .gitmodules                                
  160000 eb5af46e1a938d064c9f7bae9561013654a43316 0       bar                                        
  $ (cd bar && git status -s -unormal)
  ?? otheruntracked                                                                                  
  ?? untracked
  $ git status
  # On branch master
  nothing to commit (working directory clean)

So far that's expected; after all the submodule is configured not to
display untracked files.  But with -uall:

  $ git status -uall
  # On branch master
  nothing to commit (working directory clean)

Shouldn't the -uall propagate, since the user is explicitly asking for
it?  That is, the display should summarize what git-status *with the
same arguments* would show inside the submodules?

-- 
Thomas Rast
trast@{inf,student}.ethz.ch

^ permalink raw reply

* Re: [PATCH v2 1/3] Move the user-facing test library to test-lib-functions.sh
From: Thomas Rast @ 2012-02-17  9:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Thomas Rast, git
In-Reply-To: <7v7gzmxw78.fsf@alter.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

> Thomas Rast <trast@student.ethz.ch> writes:
>
>> This just moves all the user-facing functions to a separate file and
>> sources that instead.
>>
>> Signed-off-by: Thomas Rast <trast@student.ethz.ch>
>> ---
>>  t/test-lib-functions.sh |  835 +++++++++++++++++++++++++++++++++++++++++++++++
>>  t/test-lib.sh           |  552 +-------------------------------
>>  2 files changed, 840 insertions(+), 547 deletions(-)
>>  create mode 100644 t/test-lib-functions.sh
>
> I would have expected from the log description that the number of deleted
> lines would be about the same as the number of added lines, and the
> difference would primarily come from the addition of "include" aka "dot"
> ". ./test-lib-functions.sh" that becomes necessary in t/test-lib.sh, some
> boilerplate material at the beginning of the new file e.g. "#!/bin/sh",
> and copying (not moving) the same Copyright block to the new file.
>
> But 835-552 = 283 feels way way more than that.  What else is going on?

Hum, you're right.  I checked with blame -C -C that I introduced no new
lines, but I must accidentally have duplicated parts of it during
conflict resolution.

-- 
Thomas Rast
trast@{inf,student}.ethz.ch

^ permalink raw reply

* Window generator in git - new feature request
From: Valentin Laskov @ 2012-02-17  9:00 UTC (permalink / raw)
  To: git; +Cc: Fedora Translation Project List

Hi git team!
Hi fedora translators team!

I have an idea and want to share with you.

I don't know what is git in details. I'm translating GUI and documentation of different programs to Bulgarian.
I and translators have a problem and I think git and you can resolve it in a future release.

In translating we have a number of sentences. When this is a documentation, it's easy to translate, especially if the translator 
knows how program works. But if text is from a GUI window, translator must know context used for each text - is this is a command or 
state description; text on a button or pop-up hint for this button... ? Even translator knows how program works, it is possible to 
have texts and windows he never seen.

Imagine that based on a source code, git can generate windows of GUI program with all controls and texts may appear on them and 
these windows can be seen on a web site! This will be a great feature for translators!

... or this may be implemented in a new project? Or may be a virtual mashine, accessed by web, with all "installed" programs with 
"working" controls but without real actions...

Cheers!
Valentin Laskov

--
trans mailing list
trans@lists.fedoraproject.org
https://admin.fedoraproject.org/mailman/listinfo/trans

^ permalink raw reply

* [PATCH] t1305: fix include by absolute path test on Windows
From: Johannes Sixt @ 2012-02-17  8:31 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, Git Mailing List

From: Johannes Sixt <j6t@kdbg.org>

Git on Windows does not understand bash's /c/dir/... POSIXy paths, so that
the test fails. Ensure that the config file mentions the Windows style
absolute path to the file to be included.

Signed-off-by: Johannes Sixt <j6t@kdbg.org>
---
 Feel free to squash this into the patch that adds the test.

 t/t1305-config-include.sh |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index f3e03a0..4b1cbaa 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -5,7 +5,7 @@ test_description='test config file include directives'
 
 test_expect_success 'include file by absolute path' '
 	echo "[test]one = 1" >one &&
-	echo "[include]path = \"$PWD/one\"" >.gitconfig &&
+	echo "[include]path = \"$(pwd)/one\"" >.gitconfig &&
 	echo 1 >expect &&
 	git config test.one >actual &&
 	test_cmp expect actual
-- 
1.7.9.1264.g9b7e2

^ permalink raw reply related

* Gitk local language issue
From: shyamal @ 2012-02-17  8:30 UTC (permalink / raw)
  To: git

Hi,

I am working in Japan now on a windows environment.
I installed GIT on my machine.When I run the application,The menus are in
Japanese.To get the English menu I added 
@set LANG=en 
at the beginning of git.cmd file.This worked like a magic :-)
But when I click the Visualize master' history from the repository menu of
Git Gui, a new interface (Gitk:Websites) opens where all the menus are still
in Japanese.Any idea how to change the menu to  English in Gitk too?

Thanks in advance

Regards,
Shyamal.

--
View this message in context: http://git.661346.n2.nabble.com/Gitk-local-language-issue-tp7293532p7293532.html
Sent from the git mailing list archive at Nabble.com.

^ permalink raw reply

* Re: [PATCH] git-latexdiff: new command in contrib, to use latexdiff and Git
From: Matthieu Moy @ 2012-02-17  8:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Tim Haga, git
In-Reply-To: <7v8vk2zghl.fsf@alter.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

> Honestly speaking, this is looking more like an "useful application for
> latex users who happen to use git to store their document source", and not
> a "useful addition for all git users", to me.
>
> These two viewpoint suggests completely different evolution path for this
> program.  Imagining what the first major new enhancement intended for
> people outside the original audience <git,latex> will be, I have this
> suspicion that "this new version will help people who have their documents
> stored in Mercurial" would be much more realistic (and the end result
> being useful) than "this new version will help git users who do not write
> their documents in latex but in asciidoc".

I agree that the next step may be to allow users of <whatever SCM
outside Git>, but I don't think the way to do that would be to make the
script generic. The script is a quick hack, and all the "clever" parts
of it are calls to Git. If someone were to adapt this for Mercurial or
Bzr, writting a python plugin would be a much better way to go
(Mercurial already has "hg extdiff" doing the hardlinked checkouts for
example, and both would allow better command-line option parsing than
my "case $1 in ... esac").

I normally like code reuse very much, but trying to make a 250 lines
long script generic enough to accept multiple SCMs would be more work
than a rewrite.

OTOH, having this script in contrib/ has several advantages over
maintaining it as a separate one-file project:

- "make install" uses Git's Makefile configuration, so it's easy to
  install.

- It makes it natural to use this mailing list for discussion. The
  script has already improved a lot since I posted it as a patch here.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply

* Re: [PATCH v2] Add a setting to require a filter to be successful
From: Johannes Sixt @ 2012-02-17  7:08 UTC (permalink / raw)
  To: jehan; +Cc: gitster, git
In-Reply-To: <4f3daaf7.e302440a.02ba.fffff463@mx.google.com>

Am 2/17/2012 2:19, schrieb jehan@orb.com:
> @@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
...
>  	ret |= apply_filter(path, src, len, dst, filter);
> +	if (!ret && required)
> +		die("required filter '%s' failed", ca.drv->name);

Wouldn't it be much more helpful if this were:

	die("%s: clean filter '%s' failed", path, ca.drv->name);

Likewise (with s/clean/smudge/) in convert_to_working_tree_internal().

> +	! git checkout -- test.fs

	test_must_fail git checkout -- test.fs

> +	! git add test.fc

	test_must_fail git add test.fc

-- Hannes

^ permalink raw reply

* looking for Linus git video made after Google Tech Talk
From: Neal Kreitzinger @ 2012-02-17  3:31 UTC (permalink / raw)
  To: git

Does anyone have a link to that git training video Linus made *after* the 
Google Tech Talk?  In that second video, part of the slideshow was outside 
the frame so it was only partially readable.  Nevertheless, he explained 
very interesting things like how to review how your merge auto-merged 
same-file edits that didn't conflict.

v/r,
neal 

^ permalink raw reply

* Re: [PATCH 0/8] config-include fixes
From: Jeff King @ 2012-02-17  3:23 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <20120217031723.GA5738@sigill.intra.peff.net>

On Thu, Feb 16, 2012 at 10:17:23PM -0500, Jeff King wrote:

> On Thu, Feb 16, 2012 at 06:50:40PM -0800, Junio C Hamano wrote:
> 
> > I'll push out the re-roll that follows your outline above in 'pu'; I think
> > I got all the conflict resolved correctly, but please eyeball the result.
> 
> Will do.

I see you just pushed it out. The result looks good to me.

> Since you are re-rolling, these are the documentation fixes I had
> squashed in based on your earlier review (though come to think of it,
> the new patches should now also describe `git_config_with_options`).

Looks like you just reverted the "include" patch instead of the merge of
the whole topic.  I'll prepare a few documentation fixups on top, then.

-Peff

^ permalink raw reply

* Re: [PATCH 0/8] config-include fixes
From: Jeff King @ 2012-02-17  3:17 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vty2quq9r.fsf@alter.siamese.dyndns.org>

On Thu, Feb 16, 2012 at 06:50:40PM -0800, Junio C Hamano wrote:

> I'll push out the re-roll that follows your outline above in 'pu'; I think
> I got all the conflict resolved correctly, but please eyeball the result.

Will do.

Since you are re-rolling, these are the documentation fixes I had
squashed in based on your earlier review (though come to think of it,
the new patches should now also describe `git_config_with_options`).

---
diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt
index f428c5c..01f64d1 100644
--- a/Documentation/technical/api-config.txt
+++ b/Documentation/technical/api-config.txt
@@ -11,9 +11,9 @@ General Usage
 Config files are parsed linearly, and each variable found is passed to a
 caller-provided callback function. The callback function is responsible
 for any actions to be taken on the config option, and is free to ignore
-some options (it is not uncommon for the configuration to be parsed
+some options. It is not uncommon for the configuration to be parsed
 several times during the run of a git program, with different callbacks
-picking out different variables useful to themselves).
+picking out different variables useful to themselves.
 
 A config callback function takes three parameters:
 
@@ -47,11 +47,12 @@ will first feed the user-wide one to the callback, and then the
 repo-specific one; by overwriting, the higher-priority repo-specific
 value is left at the end).
 
-There is a special version of `git_config` called `git_config_early`
-that takes an additional parameter to specify the repository config.
-This should be used early in a git program when the repository location
-has not yet been determined (and calling the usual lazy-evaluation
-lookup rules would yield an incorrect location).
+There is a special version of `git_config` called `git_config_early`.
+This version takes an additional parameter to specify the repository
+config, instead of having it looked up via `git_path`. This is useful
+early in a git program before the repository has been found. Unless
+you're working with early setup code, you probably don't want to use
+this.
 
 Reading Specific Files
 ----------------------

^ permalink raw reply related

* Re: [PATCH 0/8] config-include fixes
From: Junio C Hamano @ 2012-02-17  2:50 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20120217001438.GD4756@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Thu, Feb 16, 2012 at 12:11:52PM -0800, Junio C Hamano wrote:
>
>> Jeff King <peff@peff.net> writes:
>> 
>> > I prepared this on top of what you have queued in jk/config-include.
>> > However, all of the cleanup is semantically independent of the topic
>> > (though there are a few minor textual conflicts). If I were re-rolling,
>> > I would put it all at the front, then squash patch 8 into my prior
>> > "implement config includes" patch.
>> 
>> Sorry for being late in answering the "revert or build on top" question; I
>> was mostly offline yesterday afternoon.
>
> No problem. I did it as build-on-top because it's much easier to squash
> and reorder the commits later than it is to pick a re-roll apart into
> multiple commits.
>
> Which way did you want me to go with it?

I'll push out the re-roll that follows your outline above in 'pu'; I think
I got all the conflict resolved correctly, but please eyeball the result.

>> Looking at the rebased result, it strikes me that with_options version
>> 
>>     Furthermore, by providing a more "advanced" interface, we
>>     now have a a natural place to add new options for callers
>>     like git-config, which care about tweaking the specifics of
>>     config lookup, without disturbing the large number of
>>     "simple" users (i.e., every other part of git).
>> 
>> perhaps wants to get a pointer to struct config_lookup_options, instead of
>> us having to add a new parameter to all callsites every time a new need
>> is discovered.
>
> I considered that, but I noticed that the only callers who are really
> going to care about the _with_options version will be in
> builtin/config.c, and they all care about every option.

Ok.

Thanks.

^ permalink raw reply

* What's cooking in git.git (Feb 2012, #06; Thu, 16)
From: Junio C Hamano @ 2012-02-17  2:42 UTC (permalink / raw)
  To: git

What's cooking in git.git (Feb 2012, #06; Thu, 16)
--------------------------------------------------

Here are the topics that have been cooking.  Commits prefixed with '-' are
only in 'pu' (proposed updates) while commits prefixed with '+' are in 'next'.

The first maintenance release v1.7.9.1 ironed out usability kinks in the
new features added in v1.7.9 release.  Topics that add new features and
fixes that have been cooking in 'next' start to graduate to 'master'
again.

You can find the changes described here in the integration branches of the
repositories listed at

    http://git-blame.blogspot.com/p/git-public-repositories.html

--------------------------------------------------
[Graduated to "master"]

* bl/gitweb-project-filter (2012-02-12) 1 commit
  (merged to 'next' on 2012-02-13 at 35366b8)
 + gitweb: Harden and improve $project_filter page title

An update to the new feature to "gitweb" to show list of projects for
intermediate levels in directory hierarchies, already slated for 1.7.10.

* dp/i18n-libcharset (2012-02-13) 1 commit
  (merged to 'next' on 2012-02-13 at 528de77)
 + Makefile: introduce CHARSET_LIB to link with -lcharset

Some systems need to explicitly link -lcharset to get locale_charset().

* jk/git-dir-lookup (2012-02-02) 1 commit
  (merged to 'next' on 2012-02-05 at 1856d74)
 + standardize and improve lookup rules for external local repos

When you have both .../foo and .../foo.git, "git clone .../foo" did not
favor the former but the latter.

* jk/grep-binary-attribute (2012-02-02) 9 commits
  (merged to 'next' on 2012-02-05 at 9dffa7e)
 + grep: pre-load userdiff drivers when threaded
 + grep: load file data after checking binary-ness
 + grep: respect diff attributes for binary-ness
 + grep: cache userdiff_driver in grep_source
 + grep: drop grep_buffer's "name" parameter
 + convert git-grep to use grep_source interface
 + grep: refactor the concept of "grep source" into an object
 + grep: move sha1-reading mutex into low-level code
 + grep: make locking flag global

Fixes a longstanding bug that there was no way to tell "git grep" that a
path may look like text but it is not, which "git diff" can do using the
attributes system. Now "git grep" honors the same "binary" (or "-diff")
attribute.

* jk/userdiff-config-simplify (2012-02-07) 1 commit
  (merged to 'next' on 2012-02-10 at e9854c1)
 + drop odd return value semantics from userdiff_config

Code cleanup.

* jn/ancient-meld-support (2012-02-10) 1 commit
  (merged to 'next' on 2012-02-13 at 28aca31)
 + mergetools/meld: Use --help output to detect --output support

More reliably tell if the given version of "meld" supports --output
option.

* lt/pull-no-edit (2012-02-12) 1 commit
  (merged to 'next' on 2012-02-13 at 352f0cb)
 + "git pull" doesn't know "--edit"

For 1.7.10 where "git merge" by default spawns the editor when it can
automatically commit to record the resulting merge.

* mh/war-on-extra-refs (2012-02-12) 7 commits
  (merged to 'next' on 2012-02-13 at adb7353)
 + refs: remove the extra_refs API
 + clone: do not add alternate references to extra_refs
 + everything_local(): mark alternate refs as complete
 + fetch-pack.c: inline insert_alternate_refs()
 + fetch-pack.c: rename some parameters from "path" to "refname"
 + clone.c: move more code into the "if (refs)" conditional
 + t5700: document a failure of alternates to affect fetch

Internal API clean-up that is very cleanly done.

* nd/pack-objects-parseopt (2012-02-01) 3 commits
  (merged to 'next' on 2012-02-05 at d0dc25d)
 + pack-objects: convert to use parse_options()
 + pack-objects: remove bogus comment
 + pack-objects: do not accept "--index-version=version,"

"pack-objects" learned use parse-options, losing custom command line
parsing code.

--------------------------------------------------
[New Topics]

* cb/maint-rev-list-verify-object (2012-02-13) 1 commit
  (merged to 'next' on 2012-02-16 at a407b9a)
 + git rev-list: fix invalid typecast

Fixes an obscure bug in "rev-list --verify" that skipped verification
depending on the phase of the moon, which dates back to 1.7.8.x series.

* cb/maint-t5541-make-server-port-portable (2012-02-13) 1 commit
  (merged to 'next' on 2012-02-16 at 762eefb)
 + t5541: check error message against the real port number used

Test fix.

* cb/receive-pack-keep-errors (2012-02-13) 1 commit
  (merged to 'next' on 2012-02-16 at 9ff846e)
 + do not override receive-pack errors

Sometimes error status detected by a check in an earlier phase of
receive-pack (the other end of 'git push') was lost by later checks,
resulting in false indication of success.

* cb/transfer-no-progress (2012-02-13) 1 commit
  (merged to 'next' on 2012-02-16 at ff17092)
 + push/fetch/clone --no-progress suppresses progress output

The transport programs ignored --no-progress and showed progress when
sending their output to a terminal.

* jk/diff-highlight (2012-02-13) 5 commits
 - diff-highlight: document some non-optimal cases
 - diff-highlight: match multi-line hunks
 - diff-highlight: refactor to prepare for multi-line hunks
 - diff-highlight: don't highlight whole lines
 - diff-highlight: make perl strict and warnings fatal

Updates diff-highlight (in contrib/).

* zj/decimal-width (2012-02-14) 1 commit
  (merged to 'next' on 2012-02-16 at 72805c4)
 + make lineno_width() from blame reusable for others
 (this branch is used by zj/diff-stat-dyncol.)

Refactoring.

* zj/term-columns (2012-02-13) 1 commit
  (merged to 'next' on 2012-02-16 at fe70c88)
 + pager: find out the terminal width before spawning the pager
 (this branch is used by zj/diff-stat-dyncol.)

Fixes "git -p cmd" for any subcommand that cares about the true terminal
width.

* hv/submodule-recurse-push (2012-02-13) 3 commits
 - push: teach --recurse-submodules the on-demand option
 - Refactor submodule push check to use string list instead of integer
 - Teach revision walking machinery to walk multiple times sequencially

The bottom one was not clearly explained.

* zj/diff-stat-dyncol (2012-02-15) 6 commits
 . diff --stat: use less columns for change counts
 - (squash to the previous -- replace the last line of the log with the following)
 - diff --stat: use the full terminal width
 - (squash to the previous -- replace the log message with this)
 - diff --stat: tests for long filenames and big change counts
 - Merge branches zj/decimal-width and zj/term-columns
 (this branch uses zj/decimal-width and zj/term-columns.)

* jc/diff-stat-scaler (2012-02-14) 1 commit
  (merged to 'next' on 2012-02-16 at 404d336)
 + diff --stat: show bars of same length for paths with same amount of changes

The output from "git diff --stat" for two paths that have the same amount
of changes showed graph bars of different length due to the way we handled
rounding errors.

* jn/gitweb-unborn-head (2012-02-16) 1 commit
 - gitweb: Deal with HEAD pointing to unborn branch in "heads" view

"gitweb" compared non-existent value of HEAD with the names of commit
objects at tips of branches, triggering runtime warnings.

* tr/perftest (2012-02-16) 3 commits
 . Add a performance test for git-grep
 . Introduce a performance testing framework
 . Move the user-facing test library to test-lib-functions.sh

When merged to 'pu' this seems to break quite a lot of tests.  One way it
does so is by removing the write_script helper, but there may be others.

* jb/required-filter (2012-02-16) 1 commit
 - Add a setting to require a filter to be successful

A content filter used to be a way to make the recorded contents "more
useful", but this defines a way to optionally mark a filter "required".

--------------------------------------------------
[Stalled]

* jc/advise-push-default (2011-12-18) 1 commit
 - push: hint to use push.default=upstream when appropriate

Peff had a good suggestion outlining an updated code structure so that
somebody new can try to dip his or her toes in the development. Any
takers?

* ss/git-svn-prompt-sans-terminal (2012-01-04) 3 commits
 - fixup! 15eaaf4
 - git-svn, perl/Git.pm: extend Git::prompt helper for querying users
 - perl/Git.pm: "prompt" helper to honor GIT_ASKPASS and SSH_ASKPASS

The bottom one has been replaced with a rewrite based on comments from
Ævar. The second one needs more work, both in perl/Git.pm and prompt.c, to
give precedence to tty over SSH_ASKPASS when terminal is available.

* jc/split-blob (2012-01-24) 6 commits
 - chunked-object: streaming checkout
 - chunked-object: fallback checkout codepaths
 - bulk-checkin: support chunked-object encoding
 - bulk-checkin: allow the same data to be multiply hashed
 - new representation types in the packstream
 - varint-in-pack: refactor varint encoding/decoding

Not ready.

I finished the streaming checkout codepath, but as explained in 127b177
(bulk-checkin: support chunked-object encoding, 2011-11-30), these are
still early steps of a long and painful journey. At least pack-objects and
fsck need to learn the new encoding for the series to be usable locally,
and then index-pack/unpack-objects needs to learn it to be used remotely.

Given that I heard a lot of noise that people want large files, and that I
was asked by somebody at GitTogether'11 privately for an advice on how to
pay developers (not me) to help adding necessary support, I am somewhat
dissapointed that the original patch series that was sent almost two
months ago still remains here without much comments and updates from the
developer community. I even made the interface to the logic that decides
where to split chunks easily replaceable, and I deliberately made the
logic in the original patch extremely stupid to entice others, especially
the "bup" fanboys, to come up with a better logic, thinking that giving
people an easy target to shoot for, they may be encouraged to help
out. The plan is not working :-(.

* nd/columns (2012-02-08) 15 commits
 . column: Fix some compiler and sparse warnings
 . column: add a corner-case test to t3200
 . columns: minimum coding style fixes
 . tag: add --column
 . column: support piping stdout to external git-column process
 . status: add --column
 . branch: add --column
 . help: reuse print_columns() for help -a
 . column: add column.ui for default column output settings
 . column: support columns with different widths
 . column: add columnar layout
 . Stop starting pager recursively
 . Add git-column and column mode parsing
 . column: add API to print items in columns
 . Save terminal width before setting up pager

Expecting a reroll on top of zj/term-columns topic.

--------------------------------------------------
[Cooking]

* jk/config-include (2012-02-16) 9 commits
 - config: add include directive
 - config: eliminate config_exclusive_filename
 - config: stop using config_exclusive_filename
 - config: provide a version of git_config with more options
 - config: teach git_config_rename_section a file argument
 - config: teach git_config_set_multivar_in_file a default path
 - config: copy the return value of prefix_filename
 - t1300: add missing &&-chaining
 + docs: add a basic description of the config API

An assignment to the include.path pseudo-variable causes the named file
to be included in-place when Git looks up configuration variables.

Reverted the earlier round from 'next'.

* tg/tag-points-at (2012-02-13) 2 commits
  (merged to 'next' on 2012-02-13 at a8f4046)
 + builtin/tag.c: Fix a sparse warning
  (merged to 'next' on 2012-02-10 at 4bff88f)
 + tag: add --points-at list option

Will merge to 'master'.

* jl/maint-submodule-relative (2012-02-09) 2 commits
 - submodules: always use a relative path from gitdir to work tree
 - submodules: always use a relative path to gitdir

The second one looked iffy.

* ld/git-p4-expanded-keywords (2012-02-14) 1 commit
  (merged to 'next' on 2012-02-16 at a9004c5)
 + git-p4: add initial support for RCS keywords

Teach git-p4 to unexpand $RCS$-like keywords that are embedded in
tracked contents in order to reduce unnecessary merge conflicts.

^ permalink raw reply

* Re: diff -G with case insensitivity
From: Junio C Hamano @ 2012-02-17  2:27 UTC (permalink / raw)
  To: Chris Leong; +Cc: git
In-Reply-To: <CAJ6vYjejtZkupy750rvz6HW_0SNPyBVTa78DO4nY8Bi368neQw@mail.gmail.com>

Chris Leong <walkraft@gmail.com> writes:

> Is there any way to run diff -G with a case insensitivity flag?

Hrm, not that I know of.  Do we even do a case insensitive diff regardless
of the -S/-G option?

^ permalink raw reply

* [PATCH v2] Add a setting to require a filter to be successful
From: jehan @ 2012-02-17  1:19 UTC (permalink / raw)
  To: gitster; +Cc: git, jehan
In-Reply-To: <7vobsywck1.fsf@alter.siamese.dyndns.org>

From: Jehan Bing <jehan@orb.com>

By default, if a filter driver fails, the unfiltered content will be
used. This is because the content filtering is done to massage the
content into a shape that is more convenient for the platform,
filesystem, and the user to use. The key phrase here is "more
convenient" and not "turning something unusable into usable".

However, another use of the content filtering is to store the content
that cannot be directly used in the repository (e.g. an UUID that
refers to the true content stored outside git, or an encrypted
content) and turn it into a usable form upon checkout (e.g. download
the external content, decrypt the encrypted content).
In this situation, it is preferable to have git fail instead of using
the unfiltered content.

This patch adds an optional "filter.<filtername>.required"
configuration variable. When missing or set to false, git will use
the unfiltered content if the filter driver fails (old behavior).
When set to true, git will instead abort the current operation.

Signed-off-by: Jehan Bing <jehan@orb.com>
---
Thanks Junio for your comment. This version use your version of
gitattributes.txt and I rewrote the commit message to be
stronger.

-Jehan

 Documentation/gitattributes.txt |   35 +++++++++++++++++++++++--------
 convert.c                       |   28 +++++++++++++++++++++---
 t/t0021-conversion.sh           |   43 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 93 insertions(+), 13 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index a85b187..6abaf9a 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -294,16 +294,23 @@ output is used to update the worktree file.  Similarly, the
 `clean` command is used to convert the contents of worktree file
 upon checkin.
 
-A missing filter driver definition in the config is not an error
-but makes the filter a no-op passthru.
+One use of the content filtering is to massage the content into a shape
+that is more convenient for the platform, filesystem, and the user to use.
 
-The content filtering is done to massage the content into a
-shape that is more convenient for the platform, filesystem, and
-the user to use.  The key phrase here is "more convenient" and not
-"turning something unusable into usable".  In other words, the
-intent is that if someone unsets the filter driver definition,
-or does not have the appropriate filter program, the project
-should still be usable.
+Another use of the content filtering is to store the content that cannot
+be directly used in the repository (e.g. an UUID that refers to the true
+content stored outside git, or an encrypted content) and turn it into a
+usable form upon checkout (e.g. download the external content, decrypt the
+encrypted content).
+
+These two filters behave differently, and by default, a filter is taken as
+the former, massaging the contents into more convenient shape.  A missing
+filter driver definition in the config, or a filter driver that exits with
+a non-zero status, is not an error but makes the filter a no-op passthru.
+
+You can declare that a filter turns a content that by itself is unusable
+into usable by setting filter.<drivername>.required configuration variable
+to `true`.
 
 For example, in .gitattributes, you would assign the `filter`
 attribute for paths.
@@ -335,6 +342,16 @@ input that is already correctly indented.  In this case, the lack of a
 smudge filter means that the clean filter _must_ accept its own output
 without modifying it.
 
+If a filter _must_ succeed in order to make the stored contents usable,
+you can declare that the filter is `required`, in the configuration:
+
+------------------------
+[filter "crypt"]
+	clean = openssl enc ...
+	smudge = openssl enc -d ...
+	required
+------------------------
+
 Sequence "%f" on the filter command line is replaced with the name of
 the file the filter is working on.  A filter might use this in keyword
 substitution.  For example:
diff --git a/convert.c b/convert.c
index 12868ed..6c95a90 100644
--- a/convert.c
+++ b/convert.c
@@ -429,6 +429,7 @@ static struct convert_driver {
 	struct convert_driver *next;
 	const char *smudge;
 	const char *clean;
+	int required;
 } *user_convert, **user_convert_tail;
 
 static int read_convert_config(const char *var, const char *value, void *cb)
@@ -472,6 +473,11 @@ static int read_convert_config(const char *var, const char *value, void *cb)
 	if (!strcmp("clean", ep))
 		return git_config_string(&drv->clean, var, value);
 
+	if (!strcmp("required", ep)) {
+		drv->required = git_config_bool(var, value);
+		return 0;
+	}
+
 	return 0;
 }
 
@@ -747,13 +753,19 @@ int convert_to_git(const char *path, const char *src, size_t len,
 {
 	int ret = 0;
 	const char *filter = NULL;
+	int required = 0;
 	struct conv_attrs ca;
 
 	convert_attrs(&ca, path);
-	if (ca.drv)
+	if (ca.drv) {
 		filter = ca.drv->clean;
+		required = ca.drv->required;
+	}
 
 	ret |= apply_filter(path, src, len, dst, filter);
+	if (!ret && required)
+		die("required filter '%s' failed", ca.drv->name);
+
 	if (ret) {
 		src = dst->buf;
 		len = dst->len;
@@ -771,13 +783,16 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
 					    size_t len, struct strbuf *dst,
 					    int normalizing)
 {
-	int ret = 0;
+	int ret = 0, ret_filter = 0;
 	const char *filter = NULL;
+	int required = 0;
 	struct conv_attrs ca;
 
 	convert_attrs(&ca, path);
-	if (ca.drv)
+	if (ca.drv) {
 		filter = ca.drv->smudge;
+		required = ca.drv->required;
+	}
 
 	ret |= ident_to_worktree(path, src, len, dst, ca.ident);
 	if (ret) {
@@ -796,7 +811,12 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
 			len = dst->len;
 		}
 	}
-	return ret | apply_filter(path, src, len, dst, filter);
+
+	ret_filter = apply_filter(path, src, len, dst, filter);
+	if (!ret_filter && required)
+		die("required filter %s failed", ca.drv->name);
+
+	return ret | ret_filter;
 }
 
 int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index f19e651..f80a59f 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -153,4 +153,47 @@ test_expect_success 'filter shell-escaped filenames' '
 	:
 '
 
+test_expect_success 'required filter success' '
+	git config filter.required.smudge cat &&
+	git config filter.required.clean cat &&
+	git config filter.required.required true &&
+
+	{
+	    echo "*.r filter=required"
+	} >.gitattributes &&
+
+	echo test > test.r &&
+	git add test.r &&
+	rm -f test.r &&
+	git checkout -- test.r
+'
+
+test_expect_success 'required filter smudge failure' '
+	git config filter.failsmudge.smudge false &&
+	git config filter.failsmudge.clean cat &&
+	git config filter.failsmudge.required true &&
+
+	{
+	    echo "*.fs filter=failsmudge"
+	} >.gitattributes &&
+
+	echo test > test.fs &&
+	git add test.fs &&
+	rm -f test.fs &&
+	! git checkout -- test.fs
+'
+
+test_expect_success 'required filter clean failure' '
+	git config filter.failclean.smudge cat &&
+	git config filter.failclean.clean false &&
+	git config filter.failclean.required true &&
+
+	{
+	    echo "*.fc filter=failclean"
+	} >.gitattributes &&
+
+	echo test > test.fc &&
+	! git add test.fc
+'
+
 test_done
-- 
1.7.9

^ permalink raw reply related

* Re: Identify Commit ID from an Extracted Source Snapshot
From: Michael Schubert @ 2012-02-17  0:14 UTC (permalink / raw)
  To: Jonathan Nieder; +Cc: James Walmsley, git@vger.kernel.org
In-Reply-To: <20120216235240.GA20779@burratino>

On 02/17/2012 12:53 AM, Jonathan Nieder wrote:
> Michael Schubert wrote:
> 
>> If your question is more like "how do I tell git to find out where
>> this old code fits in my history and eventually place it there",
>> the answer is: you cannot do it. No VCS will do this and especially
>> not Git.
> 
> Wouldn't it be possible to add the tags you want by walking through
> the commit log to find a matching commit for each tarball?

I read the question like "How do I insert / prepend my zipped
*pre-VCS* version (in)to my Git history in an automated fashion".

> For example:
> 
> 	# Usage: "GIT_DIR=<repository> tag-tars <tarballs>"
> 	# Arguments should be tarballs containing releases in
> 	# reverse-chronological order.
> 	# Should be run in an empty directory, which will be
> 	# used as a workspace.
> 
> 	# save stdin
> 	exec 3<&0
> 
> 	GIT_DIR=$(git rev-parse --resolve-git-dir "$GIT_DIR") || exit 1
> 	GIT_INDEX_FILE=$GIT_DIR/index.tag-tars
> 	export GIT_INDEX_FILE
> 
> 	if test -n "$(git ls-files -c -o | head -1)"
> 	then
> 		echo >&2 'fatal: I need an empty directory to work with'
> 		exit 1
> 	fi
> 
> 	for tar
> 	do
> 		# empty workspace
> 		git ls-files | xargs rm -f --
> 		rm -f "$GIT_INDEX_FILE"
> 
> 		# get tree name for tarball
> 		tar --strip-components=1 -xf "$tar"
> 		git ls-files -o | git update-index --add --stdin
> 		tree=$(git write-tree)
> 
> 		# tag the first commit found matching that tree, if any
> 		git rev-list master |
> 		while read cmit
> 		do
> 			if git diff-tree --quiet $cmit $tree
> 			then
> 				git tag -a ${tar%%.*} $cmit <&3
> 				break
> 			fi
> 		done
> 	done
> 
> Variation using --numstat and a path filter to find the closest commit
> ignoring some files instead of an exact match left as an exercise to
> the reader.

Nice, thanks!

^ permalink raw reply

* Re: [PATCH 0/8] config-include fixes
From: Jeff King @ 2012-02-17  0:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v1upuzgfr.fsf@alter.siamese.dyndns.org>

On Thu, Feb 16, 2012 at 12:11:52PM -0800, Junio C Hamano wrote:

> Jeff King <peff@peff.net> writes:
> 
> > I prepared this on top of what you have queued in jk/config-include.
> > However, all of the cleanup is semantically independent of the topic
> > (though there are a few minor textual conflicts). If I were re-rolling,
> > I would put it all at the front, then squash patch 8 into my prior
> > "implement config includes" patch.
> 
> Sorry for being late in answering the "revert or build on top" question; I
> was mostly offline yesterday afternoon.

No problem. I did it as build-on-top because it's much easier to squash
and reorder the commits later than it is to pick a re-roll apart into
multiple commits.

Which way did you want me to go with it?

> Looking at the rebased result, it strikes me that with_options version
> 
>     Furthermore, by providing a more "advanced" interface, we
>     now have a a natural place to add new options for callers
>     like git-config, which care about tweaking the specifics of
>     config lookup, without disturbing the large number of
>     "simple" users (i.e., every other part of git).
> 
> perhaps wants to get a pointer to struct config_lookup_options, instead of
> us having to add a new parameter to all callsites every time a new need
> is discovered.

I considered that, but I noticed that the only callers who are really
going to care about the _with_options version will be in
builtin/config.c, and they all care about every option. And dealing with
creating a struct for each call seemed like more hassle.

OTOH, I could probably just make a single static global
config_lookup_options, and have all of the option parsing tweak it
directly (i.e., replace the given_config_file and respect_includes
static globals).

-Peff

^ permalink raw reply

* Re: [PATCH] Add an option to require a filter to be successful
From: Junio C Hamano @ 2012-02-17  0:03 UTC (permalink / raw)
  To: Jehan Bing; +Cc: git
In-Reply-To: <1329434328-26621-1-git-send-email-jehan@orb.com>

Jehan Bing <jehan@orb.com> writes:

> By default, if a filter driver fails, the unfiltered content will be
> used. This patch adds a "filter.<name>.required" config option. When
> set to true, git will abort if the filter fails.
>
> A typical usage would be for a "bigfile" filter, where the smudge
> command can fail if the file is not available locally. Without the
> "required", the content of repository, i.e. a reference to the real
> content, will be checked out. Unless one saves the output logs, it
> then fairly easy to lose track of what "bigfile" wasn't checked out
> correctly.
>
> Another example would be for an encrypted repository if the clean
> command (encryption) fails. Without the "required", an unencrypted
> content could be stored in the repository by mistake.

The above describes sample situations where setting the "required" may be
very useful, without saying anything about in what situation it might be
useful to set it to "optional".

Which makes the reader wonder why this is not done as a bugfix patch that
unconditionally propagates the failure from the filter up the callchain.

That is because the first sentence in the message is too weak. It needs to
be followed by something like:

    This is because the content filtering is done to massage the content
    into a shape that is more convenient for the platform, filesystem, and
    the user to use.  The key phrase here is "more convenient" and not
    "turning something unusable into usable".

which is what the part of gitattributes documentation shown in the context
says.

That is a statement of principle.  And according to that principle, your
configuration option should never exist.

If we are changing that principle and making it configurable, I think the
update to the existing documentation should state things a bit stronger.
We shouldn't be saying "Do not use filter to turn unusable contents to
usable" and in the next breath "But you can use it if you set this at the
same time".  That is simply too confusing.

Here is an attempt to rephrase the part that updates the documentation.

Note that filter.<driver>.required is *NOT* an attribute.  An attribute is
something you attach to paths.


 Documentation/gitattributes.txt |   35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 25e46ae..39a2654 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -294,16 +294,23 @@ output is used to update the worktree file.  Similarly, the
 `clean` command is used to convert the contents of worktree file
 upon checkin.
 
-A missing filter driver definition in the config is not an error
-but makes the filter a no-op passthru.
+One use of the content filtering is to massage the content into a shape
+that is more convenient for the platform, filesystem, and the user to use.
 
-The content filtering is done to massage the content into a
-shape that is more convenient for the platform, filesystem, and
-the user to use.  The key phrase here is "more convenient" and not
-"turning something unusable into usable".  In other words, the
-intent is that if someone unsets the filter driver definition,
-or does not have the appropriate filter program, the project
-should still be usable.
+Another use of the content filtering is to store the content that cannot
+be directly used in the repository (e.g. an UUID that refers to the true
+content stored outside git, or an encrypted content) and turn it into a
+usable form upon checkout (e.g. download the external content, decrypt the
+encrypted content).
+
+These two filters behave differently, and by default, a filter is taken as
+the former, massaging the contents into more convenient shape.  A missing
+filter driver definition in the config, or a filter driver that exits with
+a non-zero status, is not an error but makes the filter a no-op passthru.
+
+You can declare that a filter turns a content that by itself is unusable
+into usable by setting filter.<drivername>.required configuration variable
+to `true`.
 
 For example, in .gitattributes, you would assign the `filter`
 attribute for paths.
@@ -335,6 +342,16 @@ input that is already correctly indented.  In this case, the lack of a
 smudge filter means that the clean filter _must_ accept its own output
 without modifying it.
 
+If a filter _must_ succeed in order to make the stored contents usable,
+you can declare that the filter is `required`, in the configuration:
+
+------------------------
+[filter "crypt"]
+	clean = openssl enc ...
+	smudge = openssl enc -d ...
+	required
+------------------------
+
 Sequence "%f" on the filter command line is replaced with the name of
 the file the filter is working on.  A filter might use this in keyword
 substitution.  For example:

^ permalink raw reply related

* Re: Identify Commit ID from an Extracted Source Snapshot
From: Jonathan Nieder @ 2012-02-16 23:53 UTC (permalink / raw)
  To: Michael Schubert; +Cc: James Walmsley, git@vger.kernel.org
In-Reply-To: <4F3D8A7C.2020400@elegosoft.com>

Michael Schubert wrote:

> If your question is more like "how do I tell git to find out where
> this old code fits in my history and eventually place it there",
> the answer is: you cannot do it. No VCS will do this and especially
> not Git.

Wouldn't it be possible to add the tags you want by walking through
the commit log to find a matching commit for each tarball?

For example:

	# Usage: "GIT_DIR=<repository> tag-tars <tarballs>"
	# Arguments should be tarballs containing releases in
	# reverse-chronological order.
	# Should be run in an empty directory, which will be
	# used as a workspace.

	# save stdin
	exec 3<&0

	GIT_DIR=$(git rev-parse --resolve-git-dir "$GIT_DIR") || exit 1
	GIT_INDEX_FILE=$GIT_DIR/index.tag-tars
	export GIT_INDEX_FILE

	if test -n "$(git ls-files -c -o | head -1)"
	then
		echo >&2 'fatal: I need an empty directory to work with'
		exit 1
	fi

	for tar
	do
		# empty workspace
		git ls-files | xargs rm -f --
		rm -f "$GIT_INDEX_FILE"

		# get tree name for tarball
		tar --strip-components=1 -xf "$tar"
		git ls-files -o | git update-index --add --stdin
		tree=$(git write-tree)

		# tag the first commit found matching that tree, if any
		git rev-list master |
		while read cmit
		do
			if git diff-tree --quiet $cmit $tree
			then
				git tag -a ${tar%%.*} $cmit <&3
				break
			fi
		done
	done

Variation using --numstat and a path filter to find the closest commit
ignoring some files instead of an exact match left as an exercise to
the reader.

Hope that helps,
Jonathan

^ permalink raw reply

* Re: Identify Commit ID from an Extracted Source Snapshot
From: Michael Schubert @ 2012-02-16 23:43 UTC (permalink / raw)
  To: Sam Vilain; +Cc: James Walmsley, git@vger.kernel.org
In-Reply-To: <4F3D8EFF.9000806@vilain.net>

On 02/17/2012 12:19 AM, Sam Vilain wrote:
> On 2/16/12 3:00 PM, Michael Schubert wrote:
>> On 02/16/2012 11:06 PM, James Walmsley wrote:
>>> I couldn't find this on google, and I have no idea if its even 
>>> possible. I have several zip files from previous versions of my 
>>> source code. (I imported svn into git). I would like to add TAGS
>>> to git which represent the versions based on the files in my zip 
>>> archives.
>>> 
>>> Does anyone know how to do this?
>> 
>> If it's just about providing the ancient code together with the 
>> (imported) more recent history from SVN, you could create an extra 
>> orphan branch for each zip packet, add the files, commit and 
>> eventually tag.
>> 
>> If your question is more like "how do I tell git to find out where 
>> this old code fits in my history and eventually place it there", 
>> the answer is: you cannot do it. No VCS will do this and
>> especially not Git.
> 
> Once you've got a tree in git which corresponds to the contents of
> the zip file, you can use git diff --stat TREEID COMMITID
> 
> You can get the commitid by obtaining the most recent timestamp for a
> file within the archive, then just using git rev-list --all
> --since=... --until=... to get a window of commit IDs, and hunt
> around until you find the one with the smallest diff.
> 
> It's hardly a straightforward thing, usually because the contents of
> the zip file never quite match the exact contents of source
> control—think autoconf and other files generated for distribution but
> not stored in the history.  So you need to use a fuzzy search.

I totally disregared the "slicing and rebuilding history approach", just
because I didn't think that's what James is asking about. Could be fun.

^ permalink raw reply

* Re: [PATCHv2 1/3] gitweb: Deal with HEAD pointing to unborn branch in "heads" view
From: Junio C Hamano @ 2012-02-16 23:28 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git, rajesh boyapati
In-Reply-To: <201202162341.09712.jnareb@gmail.com>

Jakub Narebski <jnareb@gmail.com> writes:

> On Thu, 16 Feb 2012, Junio C Hamano wrote:
>> Jakub Narebski <jnareb@gmail.com> writes:
>> 
>> > Gitweb has problems if HEAD points to an unborn branch, with no
>> > commits on it yet, but there are other branches present (so it is not
>> > newly initialized repository).
>> 
>> It would be more readable if you rephrase the vague "has problems" with a
>> concrete description of what the problem is.
>
> Sorry about this.
>
> The problem is that gitweb would generate the following warning, writing
> it in web server logs:
>
>   Use of uninitialized value in string eq

When known and easily describable in a short paragraph, let's write both
the cause and the symptom together.

> Should I re-roll this patch with improved commit message?

I was following Peff's 4 step review process, and I was in step #1 (read
the log message---can I understand what this is about without looking at
the patch?), so I haven't reached the diff part of your message ;-)

You added "defined $head_at &&" to protect against the undef comparison
for all uses of $head and I do not see any $head that was left unconverted
by mistake, so the patch looks OK to me (at times I wish gitweb were
written in a language more strict than Perl so that a compiler can catch
such mistakes).

But after trying to write a reroll myself, I have to wonder what would
happen if you have two branches pointing at the same commit as the one at
HEAD.  Why isn't the use of current_head class controlled by comparison
between the name of the ref and the output from "symbolic-ref HEAD"?

-- >8 --
From: Jakub Narebski <jnareb@gmail.com>
Date: Wed, 15 Feb 2012 16:36:41 +0100
Subject: [PATCH] gitweb: Fix "heads" view when there is no current branch

In a repository whose HEAD points to an unborn branch with no commits,
"heads" view and "summary" view (which shows what is shown in "heads"
view) compared the object names of commits at the tip of branches with the
output from "git rev-parse HEAD", which caused comparison of a string with
undef and resulted in a warning in the server log.

This can happen if non-bare repository (with default 'master' branch)
is updated not via committing but by other means like push to it, or
Gerrit.  It can happen also just after running "git checkout --orphan
<new branch>" but before creating any new commit on this branch.

Rewrite the comparison so that it also works when $head points at nothing;
in such a case, no branch can be "the current branch", add a test for it.

While at it rename local variable $head to $head_at, as it points to
current commit rather than current branch name (HEAD contents).

Reported-by: Rajesh Boyapati
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 gitweb/gitweb.perl                     |    4 ++--
 t/t9500-gitweb-standalone-no-errors.sh |    9 +++++++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 3fc7380..af154c3 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -5633,7 +5633,7 @@ sub git_tags_body {
 
 sub git_heads_body {
 	# uses global variable $project
-	my ($headlist, $head, $from, $to, $extra) = @_;
+	my ($headlist, $head_at, $from, $to, $extra) = @_;
 	$from = 0 unless defined $from;
 	$to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
 
@@ -5642,7 +5642,7 @@ sub git_heads_body {
 	for (my $i = $from; $i <= $to; $i++) {
 		my $entry = $headlist->[$i];
 		my %ref = %$entry;
-		my $curr = $ref{'id'} eq $head;
+		my $curr = defined $head_at && $ref{'id'} eq $head_at;
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 0f771c6..81246a6 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -739,4 +739,13 @@ test_expect_success \
 	'echo "\$projects_list_group_categories = 1;" >>gitweb_config.perl &&
 	 gitweb_run'
 
+# ----------------------------------------------------------------------
+# unborn branches
+
+test_expect_success \
+	'unborn HEAD: "summary" page (with "heads" subview)' \
+	'git checkout orphan_branch || git checkout --orphan orphan_branch &&
+	 test_when_finished "git checkout master" &&
+	 gitweb_run "p=.git;a=summary"'
+
 test_done
-- 
1.7.9.1.236.gedc23

^ permalink raw reply related

* Re: Identify Commit ID from an Extracted Source Snapshot
From: Sam Vilain @ 2012-02-16 23:19 UTC (permalink / raw)
  To: Michael Schubert; +Cc: James Walmsley, git@vger.kernel.org
In-Reply-To: <4F3D8A7C.2020400@elegosoft.com>

On 2/16/12 3:00 PM, Michael Schubert wrote:
> On 02/16/2012 11:06 PM, James Walmsley wrote:
>> I couldn't find this on google, and I have no idea if its even
>> possible. I have several zip files from previous versions of my
>> source code. (I imported svn into git). I would like to add TAGS to
>> git which represent the versions based on the files in my zip
>> archives.
>>
>> Does anyone know how to do this?
>
> If it's just about providing the ancient code together with the
> (imported) more recent history from SVN, you could create an extra
> orphan branch for each zip packet, add the files, commit and
> eventually tag.
>
> If your question is more like "how do I tell git to find out where
> this old code fits in my history and eventually place it there",
> the answer is: you cannot do it. No VCS will do this and especially
> not Git.

Once you've got a tree in git which corresponds to the contents of the 
zip file, you can use git diff --stat TREEID COMMITID

You can get the commitid by obtaining the most recent timestamp for a 
file within the archive, then just using git rev-list --all --since=... 
--until=... to get a window of commit IDs, and hunt around until you 
find the one with the smallest diff.

It's hardly a straightforward thing, usually because the contents of the 
zip file never quite match the exact contents of source control—think 
autoconf and other files generated for distribution but not stored in 
the history.  So you need to use a fuzzy search.

Sam

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox