* [PATCH] Add an option to require a filter to be successful
From: Jehan Bing @ 2012-02-16 23:18 UTC (permalink / raw)
To: git; +Cc: jehan
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.
Signed-off-by: Jehan Bing <jehan@orb.com>
---
Documentation/gitattributes.txt | 14 ++++++++++++
convert.c | 28 +++++++++++++++++++++---
t/t0021-conversion.sh | 43 +++++++++++++++++++++++++++++++++++++++
3 files changed, 81 insertions(+), 4 deletions(-)
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index a85b187..4d1af93 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -305,6 +305,10 @@ intent is that if someone unsets the filter driver definition,
or does not have the appropriate filter program, the project
should still be usable.
+The exception is if the filter definition has the `required`
+attribute set to `true`. In that case, the filter must apply
+successfully or git will abort the current operation.
+
For example, in .gitattributes, you would assign the `filter`
attribute for paths.
@@ -335,6 +339,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 you do not wish git to continue if `clean` or `smudge` fail, you can
+add a `required` attribute to the filter:
+
+------------------------
+[filter "crypt"]
+ clean = openssl enc ...
+ smudge = openssl enc -d ...
+ required = true
+------------------------
+
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-16 23:00 UTC (permalink / raw)
To: James Walmsley; +Cc: git@vger.kernel.org
In-Reply-To: <BEDA323D25EF6045A68DAB9FD91A0BF123A85B38@DB3PRD0402MB118.eurprd04.prod.outlook.com>
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.
^ permalink raw reply
* Re: [PATCHv2 1/3] gitweb: Deal with HEAD pointing to unborn branch in "heads" view
From: Jakub Narebski @ 2012-02-16 22:41 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, rajesh boyapati
In-Reply-To: <7vr4xuy12f.fsf@alter.siamese.dyndns.org>
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
> Also, drop the " (so it is ...)" part, which does not add much useful
> information. Your next paragraph describes how a repository can come to
> this state anyway.
O.K.
Anyway the description that repository might be in such a strange state
might be more important that the patch in itself...
> > 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.
> >
> > This commit adds test and fixes the issue of being on unborn branch
> > (of HEAD not pointing to a commit) in "heads" view, and also in
> > "summary" view -- which includes "heads" excerpt as subview.
>
> The reader has not seen anything more than "has problems" at this point,
> so "fixes the issue of ..." is not very helpful. You could have just said
> "adds tests and fixes it", if you said that the unspecified "problems"
> apear in "heads" and "summary" view at the beginning of the log message.
O.K.
Should I re-roll this patch with improved commit message?
--
Jakub Narebski
Poland
^ permalink raw reply
* Re: [PATCH v2 1/3] Move the user-facing test library to test-lib-functions.sh
From: Junio C Hamano @ 2012-02-16 22:14 UTC (permalink / raw)
To: Thomas Rast; +Cc: git
In-Reply-To: <d5127b0051d354fc0c02666b972b853d1736d09c.1329428159.git.trast@student.ethz.ch>
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?
^ permalink raw reply
* Identify Commit ID from an Extracted Source Snapshot
From: James Walmsley @ 2012-02-16 22:06 UTC (permalink / raw)
To: git@vger.kernel.org
Hi
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?
Thanks for any help in advance,
GIT is amazing!
James Walmsley
^ permalink raw reply
* Re: [PATCHv2 0/8] gitweb: Faster and improved project search
From: Jakub Narebski @ 2012-02-16 21:53 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vmx8iy0ix.fsf@alter.siamese.dyndns.org>
On Thu, 16 Feb 2012, Junio C Hamano wrote:
> Jakub Narebski <jnareb@gmail.com> writes:
>
> > [Cc-ing Junio because of his involvement in discussion about first
> > patch in previous version of this series.]
> >
> > First three patches in this series are mainly about speeding up
> > project search (and perhaps in the future also project pagination).
> > Well, first one is unification, refactoring and future-proofing.
> > The second and third patch could be squashed together; second adds
> > @fill_only, but third actually uses it.
> >
> > Next set of patches is about highlighting matched part, making it
> > easier to recognize why project was selected, what we were searching
> > for (though better page title would also help second issue).
> >
> > Well, fourth patch (first in set mentioned above) is here for the
> > commit message, otherwise it could have been squashed with next one.
> >
> > Last patch in this series is beginning of using esc_html_match_hl()
> > for other searches in gitweb -- the easiest part.
>
> Notice that you never said anything about what you wanted to achieve with
> this entire series? " -- the easiest part." does not mean anything.
> The easiest part of what?
Gitweb, besides project search, supports searching in a give repository
of a commit message ('commit', 'author', 'committer'), of files ('grep')
and of changes ('pickaxe'). From those 'grep' was easiest. This is
mentioned in comments in 8/8.
What I'd like to achieve is unification of match highlighting code, which
reduces code duplication. esc_html_match_hl() is also better than current
hand-written code: it highlights all matches, and not only last match.
> Where in the series do the "faster" and "improved" come from? What do you
> exactly mean by "faster" and "improved"? In which commit would we find
> the answers to the questions like:
>
> - What operation was slow and how you tackled that slowness?
> - What are the benchmark results?
Those series make project search faster because they reduce number of
git commands that gitweb runs when generating project search results.
Before number of commands was 2*<number of projects> + 1, after 3/8
it is 2*<number of results> + 1.
For search that selects 2 repositories out of 12 total
* Before (warm cache):
"This page took 0.867151 seconds and 27 git commands to generate."
* After (warm cache):
"This page took 0.673643 seconds and 5 git commands to generate."
The improvement in performance is even better for larger number of
repositories, and for cold cache / evicted cache.
Should I do benchmark of git_project_list_body() subroutine before
and after?
> In general, "improve" is such a loaded word (after all, patches sent to
> the list are almost always meant to "improve" things) that it almost does
> not convey a single bit of information. Are you fixing bugs? Are you
> tidying up an unreadable piece of code? Are you fixing styles? Are you
> producing prettier output? Are you refactoring cut-and-pasted repetition
> into a common helper function? Are you adding a new feature?
I'm sorry about vague "improved". What I meant is that I have added
highlighting of matched part in project search, just like e.g. Google
does unobtrusive highlighting of search phrase in page snippets.
This is especially important because project search matches either
project name or project description, and it might be not obvious why
the match... especially that match on description might be in shortened
chopped-out part.
I'll improve commit messages and cover letter in a re-roll.
--
Jakub Narebski
Poland
^ permalink raw reply
* [PATCH v2 2/3] Introduce a performance testing framework
From: Thomas Rast @ 2012-02-16 21:41 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1329428159.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 | 22 ++++-
12 files changed, 770 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 ec70ef2..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);
@@ -315,13 +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"
@@ -360,6 +364,8 @@ test_done () {
cd "$(dirname "$remove_trash")" &&
rm -rf "$(basename "$remove_trash")"
+ test_at_end_hook_
+
exit 0 ;;
*)
@@ -382,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"
@@ -517,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" || {
@@ -529,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.334.gd1409
^ permalink raw reply related
* [PATCH v2 1/3] Move the user-facing test library to test-lib-functions.sh
From: Thomas Rast @ 2012-02-16 21:41 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1329428159.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 | 835 +++++++++++++++++++++++++++++++++++++++++++++++
t/test-lib.sh | 552 +-------------------------------
2 files changed, 840 insertions(+), 547 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..580f767
--- /dev/null
+++ b/t/test-lib-functions.sh
@@ -0,0 +1,835 @@
+# 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
+}
+
+# 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 "$@"
+}
+
+# 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
+}
+
+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..ec70ef2 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -98,6 +98,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...
@@ -223,248 +225,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,311 +315,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 () {
--
1.7.9.1.334.gd1409
^ permalink raw reply related
* [PATCH v2 3/3] Add a performance test for git-grep
From: Thomas Rast @ 2012-02-16 21:41 UTC (permalink / raw)
To: git
In-Reply-To: <cover.1329428159.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.334.gd1409
^ permalink raw reply related
* [PATCH v2 0/3] Adding a performance framework
From: Thomas Rast @ 2012-02-16 21:41 UTC (permalink / raw)
To: git
This is a reroll of the original RFC at
http://thread.gmane.org/gmane.comp.version-control.git/187127
Basically, a perf framework that uses little perf scripts written in
the style we already use in the test suite.
As Junio pointed out, the line between GIT_BUILD_DIR and
GIT_TEST_INSTALLED was not very clear cut or well adhered to, so I
threw out the overrides for the former. This also made
GIT-TEST-OPTIONS moot.
There are no other changes, though I did a rebase on current next.
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 | 835 +++++++++++++++++++++++++++++++++++++++
t/test-lib.sh | 574 ++--------------------------
14 files changed, 1633 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.334.gd1409
^ permalink raw reply
* Re: [PATCH] git-latexdiff: new command in contrib, to use latexdiff and Git
From: Junio C Hamano @ 2012-02-16 21:04 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Matthieu Moy, 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.
Sorry, un-proofread draft escaped.
Please replace "for all git users" with "to git to help users who happen
to have latex documents in their repositories."
^ permalink raw reply
* git-svn: simple user-level versions question, plus help request
From: Richard Holmes @ 2012-02-16 20:57 UTC (permalink / raw)
To: git
Hi all,
I've enjoyed using git-svn in the past, and it's been great until I
had to update my local svn executable to support the server's adoption
of sslv3. I updated and configured my svn client with ra_serf and
then I could use svn standalone again.
When I went back to a git-svn project, I found that I could no longer
interact with the svn server via git-svn. I tried recreate the
git-svn tree by blowing away my working copy and starting over:
git svn init --stdlayout https://<redacted>/svn/path/to/repo
and got the familiar ssl error:
Initialized empty Git repository in /Volumes/foo/bar/.git/
RA layer request failed: OPTIONS of
'https://<redacted>/svn/path/to/repo': SSL handshake failed: SSL
error: bad decompression (https://<redacted>) at
/usr/local/Cellar/git/1.7.8.2/libexec/git-core/git-svn line 2299
(Yes, I'm using MacOS Lion and Homebrew to install git). So, it
appears that the svn with the ra_serf is not being used by git-svn.
Obvious questions:
1) Can I force git to use "my" version of svn for git-svn?
2) If so, how / where is this configured?
3) If not, what else can I do to get git-svn to talk to my svn server?
After looking at the respective versions, it appears that git svn
shows svn version 1.6.16 and my svn (standalone) client shows svn
version 1.6.17 (and its associated RA modules):
$ git svn --version
git-svn version 1.7.8.2 (svn 1.6.16)
$ svn --version
svn, version 1.6.17 (r1128011)
compiled Aug 26 2011, 09:41:54
Copyright (C) 2000-2009 CollabNet.
Subversion is open source software, see http://subversion.apache.org/
This product includes software developed by CollabNet (http://www.Collab.Net/).
The following repository access (RA) modules are available:
* ra_neon : Module for accessing a repository via WebDAV protocol using Neon.
- handles 'http' scheme
- handles 'https' scheme
* ra_svn : Module for accessing a repository using the svn network protocol.
- with Cyrus SASL authentication
- handles 'svn' scheme
* ra_local : Module for accessing a repository on local disk.
- handles 'file' scheme
* ra_serf : Module for accessing a repository via WebDAV protocol using serf.
- handles 'http' scheme
- handles 'https' scheme
$ which git
/usr/local/bin/git
$ which svn
/opt/subversion/bin/svn
Sorry for the long post, but any help would be appreciated. Doesn't
seem to be a common problem (judging from lack of discussion on
google), so I'd appreciate any pointers, either to documentation about
git's svn path / configuration, problem tickets, or ways to determine
if I've done something wonky to my environment to seriously confuse
git / svn.
Thanks!
-Richard.
^ permalink raw reply
* Re: [PATCHv2 0/8] gitweb: Faster and improved project search
From: Junio C Hamano @ 2012-02-16 20:40 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git
In-Reply-To: <1329338332-30358-1-git-send-email-jnareb@gmail.com>
Jakub Narebski <jnareb@gmail.com> writes:
> [Cc-ing Junio because of his involvement in discussion about first
> patch in previous version of this series.]
>
> First three patches in this series are mainly about speeding up
> project search (and perhaps in the future also project pagination).
> Well, first one is unification, refactoring and future-proofing.
> The second and third patch could be squashed together; second adds
> @fill_only, but third actually uses it.
>
> Next set of patches is about highlighting matched part, making it
> easier to recognize why project was selected, what we were searching
> for (though better page title would also help second issue).
>
> Well, fourth patch (first in set mentioned above) is here for the
> commit message, otherwise it could have been squashed with next one.
>
> Last patch in this series is beginning of using esc_html_match_hl()
> for other searches in gitweb -- the easiest part.
Notice that you never said anything about what you wanted to achieve with
this entire series? " -- the easiest part." does not mean anything.
The easiest part of what?
Where in the series do the "faster" and "improved" come from? What do you
exactly mean by "faster" and "improved"? In which commit would we find
the answers to the questions like:
- What operation was slow and how you tackled that slowness?
- What are the benchmark results?
In general, "improve" is such a loaded word (after all, patches sent to
the list are almost always meant to "improve" things) that it almost does
not convey a single bit of information. Are you fixing bugs? Are you
tidying up an unreadable piece of code? Are you fixing styles? Are you
producing prettier output? Are you refactoring cut-and-pasted repetition
into a common helper function? Are you adding a new feature?
^ 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 20:29 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git, rajesh boyapati
In-Reply-To: <1329320203-20272-2-git-send-email-jnareb@gmail.com>
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.
Also, drop the " (so it is ...)" part, which does not add much useful
information. Your next paragraph describes how a repository can come to
this state anyway.
> 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.
>
> This commit adds test and fixes the issue of being on unborn branch
> (of HEAD not pointing to a commit) in "heads" view, and also in
> "summary" view -- which includes "heads" excerpt as subview.
The reader has not seen anything more than "has problems" at this point,
so "fixes the issue of ..." is not very helpful. You could have just said
"adds tests and fixes it", if you said that the unspecified "problems"
apear in "heads" and "summary" view at the beginning of the log message.
Thanks.
^ permalink raw reply
* Re: git status: small difference between stating whole repository and small subdirectory
From: Junio C Hamano @ 2012-02-16 20:15 UTC (permalink / raw)
To: Thomas Rast
Cc: Piotr Krukowiecki, Jeff King, Git Mailing List,
Nguyen Thai Ngoc Duy
In-Reply-To: <87d39eswkx.fsf@thomas.inf.ethz.ch>
Thomas Rast <trast@inf.ethz.ch> writes:
> ... If my memory of pack organization serves
> right, the commit objects involved would essentially be spread across
> the whole pack
No they are crumped together in a contiguous section in a packfile, so
that "git log" without any pathspec can go faster without consulting tree
objects.
^ permalink raw reply
* Re: [PATCH 0/8] config-include fixes
From: Junio C Hamano @ 2012-02-16 20:11 UTC (permalink / raw)
To: Jeff King; +Cc: git
In-Reply-To: <20120216080102.GA11793@sigill.intra.peff.net>
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.
> [7/8]: config: eliminate config_exclusive_filename
>
> This is all cleanup which makes config_exclusive_filename go away. It's
> not strictly necessary for this series, but it's something I've been
> wanting to clean up for a while. And it does fix a few minor bugs (see
> patch 6/8). And the refactoring in 5/8 lays the groundwork for 8/8.
>
> [8/8]: config: do not respect includes for single-file --list
>
> The actual fix for the regression in my config-include patch.
Thanks.
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.
^ permalink raw reply
* Re: [PATCH] git-latexdiff: new command in contrib, to use latexdiff and Git
From: Junio C Hamano @ 2012-02-16 20:10 UTC (permalink / raw)
To: Matthieu Moy; +Cc: Tim Haga, git
In-Reply-To: <vpq39abrxav.fsf@bauges.imag.fr>
Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
> Tim Haga <timhaga@ebene6.org> writes:
>
>> While testing your script on my office machine i discovered that the
>> following might be a problem:
>>
>>> +if [ "$view" = 1 ] || [ "$view" = maybe ] && [ "$output" = "" ]; then
>>> + xpdf "$pdffile"
>>> +fi
>>
>> Xpdf is not installed on all machines (e.g. it's not installed on my
>> office machine), so maybe it would be a good idea to use a environment
>> variable instead?
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".
For that reason, I suspect that in the longer term, the tool will benefit
more if I do not take this patch and the tool lives standalone.
^ permalink raw reply
* Re: [PATCH 1/3 v5] diff --stat: tests for long filenames and big change counts
From: Junio C Hamano @ 2012-02-16 20:01 UTC (permalink / raw)
To: Zbigniew Jędrzejewski-Szmek; +Cc: git, Michael J Gruber, pclouds
In-Reply-To: <4F3CD318.1040600@in.waw.pl>
Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> writes:
> On 02/15/2012 06:12 PM, Junio C Hamano wrote:
>> Zbigniew Jędrzejewski-Szmek<zbyszek@in.waw.pl> writes:
>>
>>> Eleven tests for various combinations of a long filename and/or big
>>> change count and ways to specify widths for diff --stat.
>>> ---
>>
>> Sign-off?
>
>> Hrm, this does not seem to pass, making the result of applying [1/3] fail;
>> I see that the elided name is shown much shorter than the above expects.
>
> Hi,
>
> I'm sorry for not properly testing the patch with tests. I somehow
> convinced myself that the tests pass. This whole series needs more
> work, even after squashing in your two patches.
It is nothing to be sorry about if a series needs more polishing; that is
what the review discussions are for.
I've queued the series after restructuring it, and merged except for [3/3]
to 'pu', which conflicts too heavily with the nd/diffstat-gramnum topic
that is already in 'master'. I'd say we should concentrate on your first
two patches without the "num-width" stuff and get them in first, and then
later consider if rerolling the [3/3] patch is worth it after the dust
settles.
Thanks.
^ permalink raw reply
* Re: [PATCH v2] git-latexdiff: new command in contrib, to use latexdiff and Git
From: David Aguilar @ 2012-02-16 19:24 UTC (permalink / raw)
To: Matthieu Moy; +Cc: git, gitster
In-Reply-To: <1329381560-15853-1-git-send-email-Matthieu.Moy@imag.fr>
On Thu, Feb 16, 2012 at 12:39 AM, Matthieu Moy <Matthieu.Moy@imag.fr> wrote:
> +
> +verbose () {
> + if [ "$verbose" = 1 ]; then
> + printf "%s ..." "$@"
> + fi
> +}
In addition to preferring "test" over "[", we also prefer to write
these on several lines. It's probably time to update the CodingStyle
document with these notes.
e.g.
if test "$verbose" = 1
then
.... do stuff
fi
(with hard-tabs, not spaces (unlike my example))
--
David
^ permalink raw reply
* Re: git status: small difference between stating whole repository and small subdirectory
From: Jeff King @ 2012-02-16 19:20 UTC (permalink / raw)
To: Piotr Krukowiecki; +Cc: Thomas Rast, Git Mailing List, Nguyen Thai Ngoc Duy
In-Reply-To: <CAA01Cso5y23UMguEe0vwOc6kR3-DjuC8-LTMDsMeeOKU4rVGvg@mail.gmail.com>
On Thu, Feb 16, 2012 at 02:37:47PM +0100, Piotr Krukowiecki wrote:
> >> $ time git status -- .
> >> real 0m2.503s
> >> user 0m0.160s
> >> sys 0m0.096s
> >>
> >> $ time git status
> >> real 0m9.663s
> >> user 0m0.232s
> >> sys 0m0.556s
> >
> > Did you drop caches here, too?
>
> Yes I did - with cache the status takes something like 0.1-0.3s on whole repo.
OK, then that makes sense. It's pretty much just I/O on the filesystem
and on the object db.
You can break status down a little more to see which is which. Try "git
update-index --refresh" to see just how expensive the lstat and index
handling is.
And then try "git diff-index HEAD" for an idea of how expensive it is to
just read the objects and compare to the index.
> > Not really. You're showing an I/O problem, and repacking is git's way of
> > reducing I/O.
>
> So if I understand correctly, the reason is because git must compare
> workspace files with packed objects - and the problem is
> reading/seeking/searching in the packs?
Mostly reading (we keep a sorted index and access the packfiles via
mmap, so we only touch the pages we need). But you're also paying to
lstat() the directory tree, too. And you're paying to load (probably)
the whole index into memory, although it's relatively compact compared
to the actual file data.
> Is there a way to make packs better? I think most operations are on
> workdir files - so maybe it'd be possible to tell gc/repack/whatever
> to optimize access to files which I currently have in workdir?
It already does optimize for that case. If you can make it even better,
I'm sure people would be happy to see the numbers.
Mostly I think it is just the case that disk I/O is slow, and the
operation you're asking for has to do a certain amount of it. What kind
of disk/filesystem are you pulling off of?
It's not a fuse filesystem by any chance, is it? I have a repo on an
encfs-mounted filesystem, and the lstat times are absolutely horrific.
-Peff
^ permalink raw reply
* Re: [PATCH] completion: --list option for git-branch
From: Junio C Hamano @ 2012-02-16 18:07 UTC (permalink / raw)
To: Zbigniew Jędrzejewski-Szmek; +Cc: Ralf Thielow, spearce, git
In-Reply-To: <4F3CCC4A.5010100@in.waw.pl>
Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> writes:
> Normally one would use just a bare 'git branch' to list branches.
> Why would you want to use --list in an interactive environment (as
> opposed to a script)? Isn't it better not to clutter the completion
> options with something that the user actually has not need for?
$ git branch jc/\*
fatal: 'jc/*' is not a valid branch name.
$ git branch --list jc/\*
jc/advise-i18n
jc/advise-push-default
...
^ permalink raw reply
* Re: [PATCH v3] git-latexdiff: new command in contrib, to use latexdiff and Git
From: Matthieu Moy @ 2012-02-16 14:15 UTC (permalink / raw)
To: Jakub Narebski; +Cc: git, gitster
In-Reply-To: <m3d39esxrg.fsf@localhost.localdomain>
Jakub Narebski <jnareb@gmail.com> writes:
> Nb. I think it would be good to put detecting PDF viewer in its own
> function, don't you?
I normally like functions, but for such a linear and small piece of
code, I think it adds more confusion that clarity to write
do_fo () {
actual stuff
}
do_foo
than the actual stuff alone.
--
Matthieu Moy
http://www-verimag.imag.fr/~moy/
^ permalink raw reply
* [PATCH v4] git-latexdiff: new command in contrib, to use latexdiff and Git
From: Matthieu Moy @ 2012-02-16 14:08 UTC (permalink / raw)
To: git, gitster; +Cc: Matthieu Moy
In-Reply-To: <m3d39esxrg.fsf@localhost.localdomain>
git-latexdiff is a wrapper around latexdiff
(http://www.ctan.org/pkg/latexdiff) that allows using it to diff two
revisions of a LaTeX file.
git-latexdiff is made to work on documents split accross multiple .tex
files (plus possibly figures and other non-diffable files), hence could
not be implemented as a per-file diff driver.
Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
Sorry, I forgot to commit before sending v3, so it was obviously wrong.
This one should contain what I promised in v2, i.e:
- Try 'open' on MacOS to view PDF file.
- Shell style issues (thanks to Jakub)
Jakub, I also forgot to send this before the patch (I got interrupted,
and it seems my mental context-switch implementation loses data ;-) ):
> Why we autodetect PDF viewer unconditionally?
We don't really do that: the test is inside a test on $PDFVIEWER, so
it's essentially the same. I just wrote it this way because I thought
"break" wasn't POSIX, but it is actually, so I'm taking your version.
contrib/latex/Makefile | 22 ++++
contrib/latex/README | 12 ++
contrib/latex/git-latexdiff | 256 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 290 insertions(+), 0 deletions(-)
create mode 100644 contrib/latex/Makefile
create mode 100644 contrib/latex/README
create mode 100755 contrib/latex/git-latexdiff
diff --git a/contrib/latex/Makefile b/contrib/latex/Makefile
new file mode 100644
index 0000000..4617906
--- /dev/null
+++ b/contrib/latex/Makefile
@@ -0,0 +1,22 @@
+-include ../../config.mak
+-include ../../config.mak.autogen
+
+ifndef SHELL_PATH
+ SHELL_PATH = /bin/sh
+endif
+
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
+
+SCRIPT=git-latexdiff
+
+.PHONY: install help
+help:
+ @echo 'This is the help target of the Makefile. Current configuration:'
+ @echo ' gitexecdir = $(gitexecdir_SQ)'
+ @echo ' SHELL_PATH = $(SHELL_PATH_SQ)'
+ @echo 'Run "$(MAKE) install" to install $(SCRIPT) in gitexecdir.'
+
+install:
+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' $(SCRIPT) > '$(gitexecdir_SQ)/$(SCRIPT)'
+ chmod 755 '$(gitexecdir)/$(SCRIPT)'
diff --git a/contrib/latex/README b/contrib/latex/README
new file mode 100644
index 0000000..2d7fdd6
--- /dev/null
+++ b/contrib/latex/README
@@ -0,0 +1,12 @@
+git-latexdiff is a wrapper around latexdiff
+(http://www.ctan.org/pkg/latexdiff) that allows using it to diff two
+revisions of a LaTeX file.
+
+The script internally checks out the full tree for the specified
+revisions, and calls latexdiff with the --flatten option, hence this
+works if the document is split into multiple .tex files.
+
+Try "git latexdiff -h" for more information.
+
+To install, either drop git-latexdiff in your $PATH, or run "make
+install".
diff --git a/contrib/latex/git-latexdiff b/contrib/latex/git-latexdiff
new file mode 100755
index 0000000..19b9783
--- /dev/null
+++ b/contrib/latex/git-latexdiff
@@ -0,0 +1,256 @@
+#! /bin/sh
+
+# Author: Matthieu Moy <Matthieu.Moy@imag.fr> (2012)
+
+# Missing features (patches welcome ;-) :
+# - diff the index or the current worktree
+# - checkout only a subdirectory of the repo
+# - hardlink temporary checkouts as much as possible
+
+usage () {
+ cat << EOF
+Usage: $(basename $0) [options] OLD [NEW]
+Call latexdiff on two Git revisions of a file.
+
+OLD and NEW are Git revision identifiers. NEW defaults to HEAD.
+
+Options:
+ --help this help message
+ --main <file.tex> name of the main LaTeX file
+ --no-view don't display the resulting PDF file
+ --view view the resulting PDF file
+ (default if -o is not used)
+ --pdf-viewer <cmd> use <cmd> to view the PDF file (default: \$PDFVIEWER)
+ --no-cleanup don't cleanup temp dir after running
+ -o <file>, --output <file>
+ copy resulting PDF into <file>
+ (usually ending with .pdf)
+EOF
+}
+
+die () {
+ echo "fatal: $@"
+ exit 1
+}
+
+verbose () {
+ if test "$verbose" = 1 ; then
+ printf "%s ..." "$@"
+ fi
+}
+
+verbose_progress () {
+ if test "$verbose" = 1 ; then
+ printf "." "$@"
+ fi
+}
+
+verbose_done () {
+ if test "$verbose" = 1 ; then
+ echo " ${1:-done}."
+ fi
+}
+
+old=
+new=
+main=
+view=maybe
+cleanup=1
+verbose=0
+output=
+initial_dir=$PWD
+
+while test $# -ne 0; do
+ case "$1" in
+ "--help"|"-h")
+ usage
+ exit 0
+ ;;
+ "--main")
+ test $# -gt 1 && shift || die "missing argument for $1"
+ main=$1
+ ;;
+ "--no-view")
+ view=0
+ ;;
+ "--view")
+ view=1
+ ;;
+ "--pdf-viewer")
+ test $# -gt 1 && shift || die "missing argument for $1"
+ PDFVIEWER="$1"
+ ;;
+ "--no-cleanup")
+ cleanup=0
+ ;;
+ "-o"|"--output")
+ test $# -gt 1 && shift || die "missing argument for $1"
+ output=$1
+ ;;
+ "--verbose"|"-v")
+ verbose=1
+ ;;
+ *)
+ if test -z "$1" ; then
+ echo "Empty string not allowed as argument"
+ usage
+ exit 1
+ elif test -z "$old" ; then
+ old=$1
+ elif test -z "$new" ; then
+ new=$1
+ else
+ echo "Bad argument $1"
+ usage
+ exit 1
+ fi
+ ;;
+ esac
+ shift
+done
+
+if test -z "$new" ; then
+ new=HEAD
+fi
+
+if test -z "$old" ; then
+ echo "fatal: Please, provide at least one revision to diff with."
+ usage
+ exit 1
+fi
+
+if test -z "$PDFVIEWER" ; then
+ verbose "Auto-detecting PDF viewer"
+ candidates="xdg-open evince okular xpdf acroread"
+ if test "$(uname)" = Darwin ; then
+ # open exists on GNU/Linux, but does not open PDFs
+ candidates="open $candidates"
+ fi
+
+ for command in $candidates; do
+ if command -v "$command" >/dev/null 2>&1; then
+ PDFVIEWER="$command"
+ break
+ else
+ verbose_progress
+ fi
+ done
+ verbose_done "$PDFVIEWER"
+fi
+
+case "$view" in
+ maybe|1)
+ if test -z "$PDFVIEWER" ; then
+ echo "warning: could not find a PDF viewer on your system."
+ echo "warning: Please set \$PDFVIEWER or use --pdf-viewer CMD."
+ PDFVIEWER=false
+ fi
+ ;;
+esac
+
+if test -z "$main" ; then
+ printf "%s" "No --main provided, trying to guess ... "
+ main=$(git grep -l '^[ \t]*\\documentclass')
+ # May return multiple results, but if so the result won't be a file.
+ if test -r "$main" ; then
+ echo "Using $main as the main file."
+ else
+ if test -z "$main" ; then
+ echo "No candidate for main file."
+ else
+ echo "Multiple candidates for main file:"
+ printf "%s\n" "$main" | sed 's/^/\t/'
+ fi
+ die "Please, provide a main file with --main FILE.tex."
+ fi
+fi
+
+if test ! -r "$main" ; then
+ die "Cannot read $main."
+fi
+
+verbose "Creating temporary directories"
+
+git_prefix=$(git rev-parse --show-prefix)
+cd "$(git rev-parse --show-cdup)" || die "Can't cd back to repository root"
+git_dir="$(git rev-parse --git-dir)" || die "Not a git repository?"
+git_dir=$(cd "$git_dir"; pwd)
+
+main=$git_prefix/$main
+
+tmpdir=$initial_dir/git-latexdiff.$$
+mkdir "$tmpdir" || die "Cannot create temporary directory."
+
+cd "$tmpdir" || die "Cannot cd to $tmpdir"
+
+mkdir old new diff || die "Cannot create old, new and diff directories."
+
+verbose_done
+verbose "Checking out old and new version"
+
+cd old || die "Cannot cd to old/"
+git --git-dir="$git_dir" --work-tree=. checkout "$old" -- . || die "checkout failed for old/"
+verbose_progress
+cd ../new || die "Cannot cd to new/"
+git --git-dir="$git_dir" --work-tree=. checkout "$new" -- . || die "checkout failed for new/"
+verbose_progress
+cd ..
+
+verbose_done
+verbose "Running latexdiff --flatten old/$main new/$main > $main"
+
+latexdiff --flatten old/"$main" new/"$main" > diff.tex || die "latexdiff failed"
+
+mv -f diff.tex new/"$main"
+
+verbose_done
+
+mainbase=$(basename "$main" .tex)
+maindir=$(dirname "$main")
+
+verbose "Compiling result"
+
+compile_error=0
+cd new/"$maindir" || die "Can't cd to new/$maindir"
+if test -f Makefile ; then
+ make || compile_error=1
+else
+ pdflatex --interaction errorstopmode "$mainbase" || compile_error=1
+fi
+
+verbose_done
+
+pdffile="$mainbase".pdf
+if test ! -r "$pdffile" ; then
+ echo "No PDF file generated."
+ compile_error=1
+fi
+
+if test ! -s "$pdffile" ; then
+ echo "PDF file generated is empty."
+ compile_error=1
+fi
+
+if test "$compile_error" = "1" ; then
+ echo "Error during compilation. Please examine and cleanup if needed:"
+ echo "Directory: $tmpdir/new/$maindir/"
+ echo " File: $mainbase.tex"
+ # Don't clean up to let the user diagnose.
+ exit 1
+fi
+
+if test -n "$output" ; then
+ abs_pdffile="$PWD/$pdffile"
+ (cd "$initial_dir" && cp "$abs_pdffile" "$output")
+ echo "Output written on $output"
+fi
+
+if test "$view" = 1 || test "$view" = maybe && test -z "$output" ; then
+ "$PDFVIEWER" "$pdffile"
+fi
+
+if test "$cleanup" = 1 ; then
+ verbose "Cleaning-up result"
+ rm -fr "$tmpdir"
+ verbose_done
+fi
--
1.7.9.111.gf3fb0.dirty
^ permalink raw reply related
* Re: git status: small difference between stating whole repository and small subdirectory
From: Thomas Rast @ 2012-02-16 14:05 UTC (permalink / raw)
To: Piotr Krukowiecki; +Cc: Jeff King, Git Mailing List, Nguyen Thai Ngoc Duy
In-Reply-To: <CAA01Cso5y23UMguEe0vwOc6kR3-DjuC8-LTMDsMeeOKU4rVGvg@mail.gmail.com>
Piotr Krukowiecki <piotr.krukowiecki@gmail.com> writes:
> On Wed, Feb 15, 2012 at 8:03 PM, Jeff King <peff@peff.net> wrote:
>> On Wed, Feb 15, 2012 at 09:57:29AM +0100, Piotr Krukowiecki wrote:
>>>
>> I notice that you're still I/O bound even after the repack:
>>
>>> $ time git status -- .
>>> real 0m2.503s
>>> user 0m0.160s
>>> sys 0m0.096s
>>>
>>> $ time git status
>>> real 0m9.663s
>>> user 0m0.232s
>>> sys 0m0.556s
>>
>> Did you drop caches here, too?
>
> Yes I did - with cache the status takes something like 0.1-0.3s on whole repo.
So umm, I'm not sure that leaves anything to be improved.
I looked at some strace dumps, and limiting the status to a subdirectory
(in my case, '-- t' in git.git) does omit the lstat()s on uninteresting
parts of the index-listed files, as well as the getdents() (i.e.,
readdir()) for parts of the tree that are not interesting.
BTW, some other parts of git-status's display may be responsible for the
amount of data it pulls from disk. In particular, the "Your branch is
ahead" display requires computing the merge-base between HEAD and
@{upstream}. If your @{upstream} is way ahead/behind, or points at a
disjoint chunk of history, this may mean essentially pulling all of the
involved history from disk. If my memory of pack organization serves
right, the commit objects involved would essentially be spread across
the whole pack (corresponding to "time") and thus this operation would
more or less load the entire pack from disk.
--
Thomas Rast
trast@{inf,student}.ethz.ch
^ permalink raw reply
* Re: [PATCH v3] git-latexdiff: new command in contrib, to use latexdiff and Git
From: Jakub Narebski @ 2012-02-16 13:40 UTC (permalink / raw)
To: Matthieu Moy; +Cc: git, gitster
In-Reply-To: <1329395775-18294-1-git-send-email-Matthieu.Moy@imag.fr>
Matthieu Moy <Matthieu.Moy@imag.fr> writes:
> Changes since v2:
>
[...]
> - Shell style issues (thanks to Jakub)
[...]
> +verbose "Auto-detecting PDF viewer"
> +candidates="xdg-open evince okular xpdf acroread"
> +if [ "$(uname)" = Darwin ]; then
> + # open exists on GNU/Linux, but does not open PDFs
> + candidates="open $candidates"
> +fi
> +
> +for command in $candidates; do
> + if [ "$PDFVIEWER" = "" ]; then
> + if command -v "$command" >/dev/null 2>&1; then
> + PDFVIEWER="$command"
> + else
> + verbose_progress
> + fi
> + fi
> +done
> +verbose_done "$PDFVIEWER"
Eh? I don't see shell style issues fixed (loop inside conditional
instead of vice-versa, "test ..." instead of "[ ... ]").
Nb. I think it would be good to put detecting PDF viewer in its own
function, don't you?
--
Jakub Narebski
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox