Git development
 help / color / mirror / Atom feed
* Re: [RFH] shifting xdiff hunks?
From: Jakub Narebski @ 2006-04-13  7:44 UTC (permalink / raw)
  To: git
In-Reply-To: <7vmzeqyolw.fsf@assigned-by-dhcp.cox.net>

Junio C. Hamano wrote:
 
> Now, from correctness point of view, this is not a problem at
> all, but I am wondering if xdiff can help to always shift the
> hunk down or up to consistently produce one way or another
> (personally I feel the former is easier to read).

This would also help with adding new functions, as sometimes diff begins
with the closing brace of the preceding function, instead of ending with
closing brace of the added function.

Just my own 0.2 eurocents.

-- 
Jakub Narebski
Warsaw, Poland

^ permalink raw reply

* Test fails on ubuntu breezy
From: Aneesh Kumar @ 2006-04-13  7:53 UTC (permalink / raw)
  To: Git Mailing List

kvaneesh@home:~/git-work/git.build/t$ ./t3600-rm.sh
Committing initial tree e5c556e46aae6124ff4a2a466c95004e92d9a2e4
*   ok 1: Pre-check that foo exists and is in index before git-rm foo
*   ok 2: Test that git-rm foo succeeds
*   ok 3: Post-check that foo exists but is not in index after git-rm foo
*   ok 4: Pre-check that bar exists and is in index before "git-rm -f bar"
*   ok 5: Test that "git-rm -f bar" succeeds
*   ok 6: Post-check that bar does not exist and is not in index after
"git-rm -f bar"
*   ok 7: Test that "git-rm -- -q" succeeds (remove a file that looks
like an option)
*   ok 8: Test that "git-rm -f" succeeds with embedded space, tab, or
newline characters.
* FAIL 9: Test that "git-rm -f" fails if its rm fails
        git-rm -f baz
*   ok 10: When the rm in "git-rm -f" fails, it should not remove the
file from the index
* failed 1 among 10 test(s)
kvaneesh@home:~/git-work/git.build/t$

-aneesh

^ permalink raw reply

* Re: [PATCH] diff-options: add --stat
From: Johannes Schindelin @ 2006-04-13  8:29 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vek02ynif.fsf@assigned-by-dhcp.cox.net>

Hi,

On Wed, 12 Apr 2006, Junio C Hamano wrote:

> Junio C Hamano <junkio@cox.net> writes:
> 
> > I wonder if you can also make this an independent option that
> > prepends diffstat in front of the patch, just like the way the
> > new flag --patch-with-raw flag prepends raw output in front of
> > the patch.
> 
> Clarification.
> 
> Traditionally, we had diff-raw and diff-patch formats.
> We can think of --name-status and --name-only variants of
> diff-raw (just like different --abbrev settings give different
> visuals for diff-raw).  Until very recently, these were either-or
> output formats, but for Cogito we added an option to show both.
> 
> We could reorganize the output format options to:
> 
> 	- diff-raw and its name variants
> 	- diff-stat
>         - diff-patch
> 
> and have (internally) three bools to specify which ones to
> output, in the above order.  The recent --patch-with-raw would
> flip bit #0 (show raw) and bit #2 (show patch) on.  It is very
> likely that diff-stat followed by diff-patch would be a popular
> format (that is what git-format-patch does), and it also is
> conceivable that diff-raw with diff-stat but without diff-patch
> might turn out to be useful for some people.

That sounds plausible. Note that if diff-stat and diff-patch are turned 
on, the patch generating code will be called twice. I do not think it is 
sensible (or robust, for that matter) to cache the patch for this case.

> Also, I forgot to mention, but would it be useful to have a
> diffstat to show --cc?  It is unclear, without much thinking,
> what the numbers would mean, though...

I thought long and hard about that. But how would you display it? You can 
have lines starting with "+ ", " +", "--", etc. The only half-way 
reasonable approach I found was to display the diffstat against each 
parent individually. Since it would be a bit involved to implement that, I 
wanted to think about it a bit longer, if it really makes sense.

Ciao,
Dscho

^ permalink raw reply

* Re: Test fails on ubuntu breezy
From: Peter Eriksen @ 2006-04-13 10:20 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <cc723f590604130053k6896c0cfkd8ea648e91d50d0e@mail.gmail.com>

On Thu, Apr 13, 2006 at 01:23:07PM +0530, Aneesh Kumar wrote:
> kvaneesh@home:~/git-work/git.build/t$ ./t3600-rm.sh
> Committing initial tree e5c556e46aae6124ff4a2a466c95004e92d9a2e4
> *   ok 1: Pre-check that foo exists and is in index before git-rm foo
> *   ok 2: Test that git-rm foo succeeds
> *   ok 3: Post-check that foo exists but is not in index after git-rm foo
> *   ok 4: Pre-check that bar exists and is in index before "git-rm -f bar"
> *   ok 5: Test that "git-rm -f bar" succeeds
> *   ok 6: Post-check that bar does not exist and is not in index after
> "git-rm -f bar"
> *   ok 7: Test that "git-rm -- -q" succeeds (remove a file that looks
> like an option)
> *   ok 8: Test that "git-rm -f" succeeds with embedded space, tab, or
> newline characters.
> * FAIL 9: Test that "git-rm -f" fails if its rm fails
>         git-rm -f baz
> *   ok 10: When the rm in "git-rm -f" fails, it should not remove the
> file from the index
> * failed 1 among 10 test(s)
> kvaneesh@home:~/git-work/git.build/t$

On solaris 10 too, only test 5 also fails.

Peter

^ permalink raw reply

* Re: Test fails on ubuntu breezy
From: linux @ 2006-04-13 11:54 UTC (permalink / raw)
  To: git

I've recently encountered the same problem with t/t3600-rm.sh step 9,
but I put it down to compiling as root.

Basically, the chmod of the directory didn't stop the delete from
happening, since I had umask 002 and it was g+w.

Anyway, that test is fragile.

^ permalink raw reply

* Re: [PATCH] Implement limited context matching in git-apply.
From: Eric W. Biederman @ 2006-04-13 12:02 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Junio C Hamano, git
In-Reply-To: <Pine.LNX.4.64.0604111100510.10745@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> On Mon, 10 Apr 2006, Eric W. Biederman wrote:
>> 
>> So at a quick inspection it looks to me like:
>> About .059s to perform to check for missing files.
>> About .019s to write the new tree.
>> About .155s in start up overhead, read_cache, and sanity checks.
>> 
>> So at a first glance it looks like librification to
>> allow the redundant work to be skipped, is where
>> the big speed win on my machine would be.
>
> That sounded wrong to me, so I did a stupid patch to datestamp the 
> different phases of git-write-tree, and here's what it says for me:
>
>      0.000479 setup_git_directory
>      0.008333 read_cache
>      0.000813 ce_stage check
>      0.001838 tree validity check
>      0.037233 write_tree itself
>
> 	real    0m0.051s
> 	user    0m0.044s
> 	sys     0m0.008s
>
> all times are in seconds. 

Ok.  This is interesting and probably reveals what is different
about my setup.  For you user+sys = real.  For me there was
a significant gap.  So it looks like for some reason I was not
succeeding in keeping .git/index hot in the page cache.

When you are I/O bound it does make sense for read_cache
to be the dominate time.  I just need to track what is up
with my machine that makes me I/O bound.  Having too little
ram is an obvious candidate but it is too simple.  Currently
out of 512M I only have 21M in the page cache which sounds
really low.  Something for me to look at.

> Which would imply pretty major surgery - you'd have to add the tree entry 
> information to the index file, and make sure they got invalidated properly 
> (all the way to the root) whenever adding/deleting/updating a path in the 
> index file.
>
> Quite frankly, I don't think it's really worth it.

For the current size of the kernel tree I agree.

It is a potential scaling limitation and if someone starts
tracking really big tress with git it may be worth revisiting.

> Yes, it would speed up applying of huge patch-sets, but it's not like 
> we're really slow at that even now, and I suspect you'd be better off 
> trying to either live with it, or trying to see if you could change your 
> workflow. There clearly _are_ tools that are better at handling pure 
> patches, with quilt being the obvious example.

Probably.  For my workflow not having to switch tool chains is
the biggest win.  Which is part of what the -C is about.


> I routinely apply 100-200 patches in a go, and that's fast enough to not 
> even be an issue. Yes, I have reasonably fast hardware, but we're likely 
> talking thousands of patches in a series for it to be _really_ painful 
> even on pretty basic developer hardware. Even a slow machine should do a 
> few hundred patches in a couple of minutes.
>
> Maybe enough time to get a cup of coffee, but no more than it would take 
> to compile the project.

Agreed.  I did the analysis so I could understand what was going on.
If the analysis revealed low hanging fruit I would have plucked it.

Eric

^ permalink raw reply

* Re: Common option parsing..
From: Linus Torvalds @ 2006-04-13 14:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vmzeq12zd.fsf@assigned-by-dhcp.cox.net>



On Wed, 12 Apr 2006, Junio C Hamano wrote:
> 
> However, I am not sure about the two-revs case.  I suspect the
> incoming items are sorted in the revs->commits list

Not by the argument parsing. That happens by "rev_parse_setup()" when we 
start to do the revision walking. 

So after setup_revisions() it's all good (although I think the list may be 
in "reverse order", I didn't check).

		Linus

^ permalink raw reply

* Use less memory in "git log"
From: Linus Torvalds @ 2006-04-13 17:01 UTC (permalink / raw)
  To: Junio C Hamano, Git Mailing List


This trivially avoids keeping the commit message data around after we 
don't need it any more, avoiding a continually growing "git log" memory 
footprint.

It's not a huge deal, but it's somewhat noticeable. For the current kernel 
tree, doing a full "git log" I got

 - before: /usr/bin/time git log > /dev/null 
	0.81user 0.02system 0:00.84elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
	0inputs+0outputs (0major+8851minor)pagefaults 0swaps

 - after: /usr/bin/time git log > /dev/null 
	0.79user 0.03system 0:00.83elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
	0inputs+0outputs (0major+5039minor)pagefaults 0swaps

ie the touched pages dropped from 8851 to 5039. For the historic kernel 
archive, the numbers are 18357->11037 minor page faults.

We could/should in theory free the commits themselves, but that's really a 
lot harder, since during revision traversal we may hit the same commit 
twice through different children having it as a parent, even after we've 
shown it once (when that happens, we'll silently ignore it next time, but 
we still need the "struct commit" to know).

And as the commit message data is clearly the biggest part of the commit, 
this is the really easy 60% solution.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---
diff --git a/git.c b/git.c
index d6f17db..78ed403 100644
--- a/git.c
+++ b/git.c
@@ -391,6 +391,8 @@ static int cmd_log(int argc, const char 
 		if (do_diff)
 			log_tree_commit(&opt, commit);
 		shown = 1;
+		free(commit->buffer);
+		commit->buffer = NULL;
 	}
 	free(buf);
 	return 0;

^ permalink raw reply related

* Re: [PATCH] diff-options: add --stat
From: Marco Costalba @ 2006-04-13 17:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git
In-Reply-To: <7vek02ynif.fsf@assigned-by-dhcp.cox.net>

On 4/13/06, Junio C Hamano <junkio@cox.net> wrote:
> Junio C Hamano <junkio@cox.net> writes:
>
> It is very
> likely that diff-stat followed by diff-patch would be a popular
> format (that is what git-format-patch does),

Yes, it is very likely the new output format (stat+patch) will be the
default in qgit diff window viewer ;-)

Marco

^ permalink raw reply

* Re: [PATCH] diff-options: add --stat
From: Junio C Hamano @ 2006-04-13 18:26 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git
In-Reply-To: <Pine.LNX.4.63.0604130301240.28688@wbgn013.biozentrum.uni-wuerzburg.de>

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Now you can say "git diff --stat" (to get an idea how many changes are
> uncommitted), or "git log --stat" (to get an idea how many changes were
> committed).

I like what this tries to do (as I already said), but there are
issues with the way it does it; here are some comments.

> +static int fn_diffstat(void *priv, mmbuffer_t *mb, int nbuf)
> +{
> +...
> +	for (i = 0; i < nbuf; i++)
> +		if (mb[i].ptr[0] == '+')
> +			x->added++;
> +		else if (mb[i].ptr[0] == '-')
> +			x->deleted++;
> +	return 0;
> +}

This is broken if you have a hunk that adds, deletes or
leaves a line that happens to begin with a plus or a minus.

A demonstration.

        : siamese; ./git-diff-files -p
        diff --git a/Makefile b/Makefile
        index 1130af4..389143e 100644
        --- a/Makefile
        +++ b/Makefile
        @@ -1,3 +1,4 @@
        +-this new line begins with a minus
         # The default target of this Makefile is...
         all:
         
        : siamese; ./git-diff-files --stat
        ---
         Makefile |    2 +-
         1 files changed, 1 insertions(+), 1 deletions(-)

For an added line, xdl_emit_diffrec(rec, size, " ", 1, ecb) is
called, which gives mb[0].ptr = " " and mb[1].ptr = <the
contents of the line>; fn_diffstat() is called with (nbuf == 2).
You are counting the addition mark '+', but you are counting the
minus at the beginning of '-this new line...'.

A fragile workaround would be to make fn_diffstat() aware of
what the caller does and look only at mb[0], with a note to the
poor soul who needs to debug future breakage if xdiff side ever
changes.

A less efficient but more futureproof alternative is to use
xdiff-interface.c::xdiff_outf() to make sure your callback is
fed one line at a time (an example of how this can be done is
found in combine-diff.c::combine_diff()).

For the original "emit everything to stdout" code Linus did,
this was not a problem, because that usage does not care about
the record boundary.  For this "counting '+' and '-' at the
beginning of each hunk data", you do.

> @@ -221,43 +367,49 @@ static void builtin_diff(const char *nam
>  	b_two = quote_two("b/", name_b);
>  	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
>  	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
>...
> +	if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
> +		if (diffstat)
> +			diffstat_binary(diffstat, lbl[0]);
> +		else
> +			printf("Binary files %s and %s differ\n",
> +					lbl[0], lbl[1]);
> +	} else {
>...
> @@ -690,16 +843,19 @@ static void run_diff_cmd(const char *pgm
>  			 struct diff_filespec *one,
>  			 struct diff_filespec *two,
>  			 const char *xfrm_msg,
> -			 int complete_rewrite)
> +			 int complete_rewrite,
> +			 struct diffstat_t *diffstat)
>  {
>  	if (pgm) {
> +		if (diffstat)
> +			die ("Cannot use diffstat with external diff");
>  		run_external_diff(pgm, name, other, one, two, xfrm_msg,
>  				  complete_rewrite);
>  		return;
>  	}

Instead of driving diffstat code from run_diff(),
run_diff_cmd(), and builtin_diff(), I think it would be much
cleaner to define diff_flush_stat() as a sibling to
diff_flush_raw() and diff_flush_patch(), and bypass the
run_diff() chain.  There will be some stuff you can reuse from
builtin_diff() (mostly how it interfaces with xdiff library), so
split that out as a separate function, and call it from both
builtin_diff() and diff_flush_stat().  Then you do not have to
say "Cannot use diffstat with external diff"; even when you
usually use your wdiff based external diff, you would want an
option to get diffstat, no?  This is even more true if we are to
have three bools to toggle which format to enable like I said in
my previous message with --with-raw --with-stat --with-patch
options.

^ permalink raw reply

* Re: [PATCH] diff-options: add --stat
From: Linus Torvalds @ 2006-04-13 18:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Schindelin, git
In-Reply-To: <7v64ld2uyv.fsf@assigned-by-dhcp.cox.net>



On Thu, 13 Apr 2006, Junio C Hamano wrote:
> 
> I like what this tries to do (as I already said), but there are
> issues with the way it does it; here are some comments.
> 
> > +static int fn_diffstat(void *priv, mmbuffer_t *mb, int nbuf)
> > +{
> > +...
> > +	for (i = 0; i < nbuf; i++)
> > +		if (mb[i].ptr[0] == '+')
> > +			x->added++;
> > +		else if (mb[i].ptr[0] == '-')
> > +			x->deleted++;
> > +	return 0;
> > +}
> 
> This is broken if you have a hunk that adds, deletes or
> leaves a line that happens to begin with a plus or a minus.

I think you can just depend on it being in mb[0].ptr[0], no?

xdiff always gives full lines at a time, afaik, and mb[0] always ends up 
being available.

		Linus

^ permalink raw reply

* Re: [PATCH] diff-options: add --stat
From: Junio C Hamano @ 2006-04-13 18:55 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0604131138080.14565@g5.osdl.org>

Linus Torvalds <torvalds@osdl.org> writes:

> I think you can just depend on it being in mb[0].ptr[0], no?

Yes that happens to be the case _now_.  I just did not want to
worry about future breakage, in case if Davide ever wants to
change how mb[] is prepared for whatever reason.

^ permalink raw reply

* Re: Test fails on ubuntu breezy
From: Junio C Hamano @ 2006-04-13 18:56 UTC (permalink / raw)
  To: linux, Peter Eriksen, Aneesh Kumar; +Cc: git, Carl Worth
In-Reply-To: <20060413115447.11819.qmail@science.horizon.com>

linux@horizon.com writes:

> I've recently encountered the same problem with t/t3600-rm.sh step 9,
> but I put it down to compiling as root.
>
> Basically, the chmod of the directory didn't stop the delete from
> happening, since I had umask 002 and it was g+w.
>
> Anyway, that test is fragile.

Indeed.  I am not sure about test #5, but here is my stab at
fixing test #9.

-- >8 --
[PATCH] t3600-rm: skip failed-remove test when we cannot make an unremovable file

When running t3600-rm test under fakeroot (or as root), we
cannot make a file unremovable with "chmod a-w .".  Detect this
case early and skip that test.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index d1947e1..acaa4d6 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -23,6 +23,16 @@ else
     test_tabs=n
 fi
 
+# Later we will try removing an unremovable path to make sure
+# git-rm barfs, but if the test is run as root that cannot be
+# arranged.
+: >test-file
+chmod a-w .
+rm -f test-file
+test -f test-file && test_failed_remove=y
+chmod 775 .
+rm -f test-file
+
 test_expect_success \
     'Pre-check that foo exists and is in index before git-rm foo' \
     '[ -f foo ] && git-ls-files --error-unmatch foo'
@@ -56,12 +66,14 @@ test "$test_tabs" = y && test_expect_suc
     "git-rm -f 'space embedded' 'tab	embedded' 'newline
 embedded'"
 
-if test "$test_tabs" = y; then
-chmod u-w .
+if test "$test_failed_remove" = y; then
+chmod a-w .
 test_expect_failure \
     'Test that "git-rm -f" fails if its rm fails' \
     'git-rm -f baz'
-chmod u+w .
+chmod 775 .
+else
+    test_expect_success 'skipping removal failure (perhaps running as root?)' :
 fi
 
 test_expect_success \

^ permalink raw reply related

* Re: [PATCH] diff-options: add --stat
From: Linus Torvalds @ 2006-04-13 19:18 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vwtdt1f10.fsf@assigned-by-dhcp.cox.net>



On Thu, 13 Apr 2006, Junio C Hamano wrote:
> 
> Yes that happens to be the case _now_.  I just did not want to
> worry about future breakage, in case if Davide ever wants to
> change how mb[] is prepared for whatever reason.

I'd be worried if we depended on an external version of xdiff, but as it 
is, we'd see all changes to our local xdiff implementation, so...

It's not like either of the statements
 - we always get whole lines
 - the first memory block is always non-empty
is really very controversial ;)

		Linus

^ permalink raw reply

* Re: [PATCH] diff-options: add --stat
From: Johannes Schindelin @ 2006-04-13 20:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7v64ld2uyv.fsf@assigned-by-dhcp.cox.net>

Hi,

On Thu, 13 Apr 2006, Junio C Hamano wrote:

> For an added line, xdl_emit_diffrec(rec, size, " ", 1, ecb) is
> called, which gives mb[0].ptr = " " and mb[1].ptr = <the
> contents of the line>; fn_diffstat() is called with (nbuf == 2).

Silly me. I did not check that code, but assumed that mb Just Contains 
Whole Lines...

> Instead of driving diffstat code from run_diff(),
> run_diff_cmd(), and builtin_diff(), I think it would be much
> cleaner to define diff_flush_stat() as a sibling to
> diff_flush_raw() and diff_flush_patch(), and bypass the
> run_diff() chain.

I guess you're right. It is also more work :-(

There is another bug: if a file is created, you see "ev/null" as filename. 
Ugly.

I'll try to fix it.

Ciao,
Dscho

^ permalink raw reply

* [PATCH 0/4] Add a testsuite to stgit (take 2)
From: Yann Dirson @ 2006-04-13 21:38 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git

This is an update of the previous patch series, including minor improvements
to the way the test engine is adapted to stgit, as well as a new testsuite
demonstrating robustness issues on series creation, and proposed fixes for all
those bugs.  And hopefully more standard patches.

-- 
Yann Dirson    <ydirson@altern.org> |
Debian-related: <dirson@debian.org> |   Support Debian GNU/Linux:
                                    |  Freedom, Power, Stability, Gratis
     http://ydirson.free.fr/        | Check <http://www.debian.org/>

^ permalink raw reply

* [PATCH 1/4] Add a testsuite framework copied from git-core
From: Yann Dirson @ 2006-04-13 21:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20060413213819.8806.53300.stgit@gandelf.nowhere.earth>

From: Yann Dirson <ydirson@altern.org>

See git's t/README for details on how to use this framework.

There is no integration yet in the toplevel Makefile, I'll let
python masters take care of this.  Use "make -C t" to run the
tests for now.

A patch-naming policy should be defined for stgit, since the
git one does not apply.
---

 TODO             |    2 -
 t/Makefile       |   25 ++++++
 t/t0000-dummy.sh |   19 +++++
 t/test-lib.sh    |  208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 253 insertions(+), 1 deletions(-)

diff --git a/TODO b/TODO
index e5affe0..d97ffd1 100644
--- a/TODO
+++ b/TODO
@@ -6,7 +6,7 @@ The TODO list until 1.0:
 - debian package support
 - man page
 - code execution allowed from templates
-- regression tests
+- more regression tests
 - release 1.0
 
 
diff --git a/t/Makefile b/t/Makefile
new file mode 100644
index 0000000..d5d7b6f
--- /dev/null
+++ b/t/Makefile
@@ -0,0 +1,25 @@
+# Run tests
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+#GIT_TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+all: $(T) clean
+
+$(T):
+	@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean:
+	rm -fr trash
+
+.PHONY: $(T) clean
+.NOPARALLEL:
+
diff --git a/t/t0000-dummy.sh b/t/t0000-dummy.sh
new file mode 100755
index 0000000..8dc25d3
--- /dev/null
+++ b/t/t0000-dummy.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson
+#
+
+test_description='Dummy test.
+
+Only to test the testing environment.
+'
+
+. ./test-lib.sh
+
+test_stg_init
+
+test_expect_success \
+    'check stgit can be run' \
+    'stg version'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
new file mode 100755
index 0000000..2580bcc
--- /dev/null
+++ b/t/test-lib.sh
@@ -0,0 +1,208 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2006 Yann Dirson
+#
+
+# For repeatability, reset the environment to known value.
+LANG=C
+LC_ALL=C
+PAGER=cat
+TZ=UTC
+export LANG LC_ALL PAGER TZ
+unset AUTHOR_DATE
+unset AUTHOR_EMAIL
+unset AUTHOR_NAME
+unset COMMIT_AUTHOR_EMAIL
+unset COMMIT_AUTHOR_NAME
+unset GIT_ALTERNATE_OBJECT_DIRECTORIES
+unset GIT_AUTHOR_DATE
+GIT_AUTHOR_EMAIL=author@example.com
+GIT_AUTHOR_NAME='A U Thor'
+unset GIT_COMMITTER_DATE
+GIT_COMMITTER_EMAIL=committer@example.com
+GIT_COMMITTER_NAME='C O Mitter'
+unset GIT_DIFF_OPTS
+unset GIT_DIR
+unset GIT_EXTERNAL_DIFF
+unset GIT_INDEX_FILE
+unset GIT_OBJECT_DIRECTORY
+unset SHA1_FILE_DIRECTORIES
+unset SHA1_FILE_DIRECTORY
+export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+
+# Each test should start with something like this, after copyright notices:
+#
+# test_description='Description of this test...
+# This test checks if command xyzzy does the right thing...
+# '
+# . ./test-lib.sh
+
+error () {
+	echo "* error: $*"
+	trap - exit
+	exit 1
+}
+
+say () {
+	echo "* $*"
+}
+
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+while test "$#" -ne 0
+do
+	case "$1" in
+	-d|--d|--de|--deb|--debu|--debug)
+		debug=t; shift ;;
+	-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
+		immediate=t; shift ;;
+	-h|--h|--he|--hel|--help)
+		echo "$test_description"
+		exit 0 ;;
+	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+		verbose=t; shift ;;
+	*)
+		break ;;
+	esac
+done
+
+exec 5>&1
+if test "$verbose" = "t"
+then
+	exec 4>&2 3>&1
+else
+	exec 4>/dev/null 3>/dev/null
+fi
+
+test_failure=0
+test_count=0
+
+trap 'echo >&5 "FATAL: Unexpected exit with code $?"; exit 1' exit
+
+
+# You are not expected to call test_ok_ and test_failure_ directly, use
+# the text_expect_* functions instead.
+
+test_ok_ () {
+	test_count=$(expr "$test_count" + 1)
+	say "  ok $test_count: $@"
+}
+
+test_failure_ () {
+	test_count=$(expr "$test_count" + 1)
+	test_failure=$(expr "$test_failure" + 1);
+	say "FAIL $test_count: $1"
+	shift
+	echo "$@" | sed -e 's/^/	/'
+	test "$immediate" = "" || { trap - exit; exit 1; }
+}
+
+
+test_debug () {
+	test "$debug" = "" || eval "$1"
+}
+
+test_run_ () {
+	eval >&3 2>&4 "$1"
+	eval_ret="$?"
+	return 0
+}
+
+test_expect_failure () {
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 parameters to test-expect-failure"
+	say >&3 "expecting failure: $2"
+	test_run_ "$2"
+	if [ "$?" = 0 -a "$eval_ret" != 0 ]
+	then
+		test_ok_ "$1"
+	else
+		test_failure_ "$@"
+	fi
+}
+
+test_expect_success () {
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 parameters to test-expect-success"
+	say >&3 "expecting success: $2"
+	test_run_ "$2"
+	if [ "$?" = 0 -a "$eval_ret" = 0 ]
+	then
+		test_ok_ "$1"
+	else
+		test_failure_ "$@"
+	fi
+}
+
+test_expect_code () {
+	test "$#" = 3 ||
+	error "bug in the test script: not 3 parameters to test-expect-code"
+	say >&3 "expecting exit code $1: $3"
+	test_run_ "$3"
+	if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+	then
+		test_ok_ "$2"
+	else
+		test_failure_ "$@"
+	fi
+}
+
+# Most tests can use the created repository, but some amy 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"
+	owd=`pwd`
+	repo="$1"
+	mkdir "$repo"
+	cd "$repo" || error "Cannot setup test environment"
+	git-init-db 2>/dev/null ||
+	error "cannot run git-init-db -- have you installed git-core?"
+	mv .git/hooks .git/hooks-disabled
+	cd "$owd"
+}
+
+test_stg_init () {
+	echo "empty start" |
+	git-commit-tree `git-write-tree` >.git/refs/heads/master 2>/dev/null ||
+	error "cannot run git-commit -- is your git-core funtionning?"
+	stg init ||
+	error "cannot run stg init -- have you built things yet?"
+}
+
+test_done () {
+	trap - exit
+	case "$test_failure" in
+	0)
+		# We could:
+		# cd .. && rm -fr trash
+		# but that means we forbid any tests that use their own
+		# subdirectory from calling test_done without coming back
+		# to where they started from.
+		# The Makefile provided will clean this test area so
+		# we will leave things as they are.
+
+		say "passed all $test_count test(s)"
+		exit 0 ;;
+
+	*)
+		say "failed $test_failure among $test_count test(s)"
+		exit 1 ;;
+
+	esac
+}
+
+# Test the binaries we have just built.  The tests are kept in
+# t/ subdirectory and are run in trash subdirectory.
+PATH=$(pwd)/..:$PATH
+export PATH
+
+
+# Test repository
+test=trash
+rm -fr "$test"
+test_create_repo $test
+cd "$test"

^ permalink raw reply related

* [PATCH 2/4] Add list of bugs to TODO
From: Yann Dirson @ 2006-04-13 21:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20060413213819.8806.53300.stgit@gandelf.nowhere.earth>

From: Yann Dirson <ydirson@altern.org>

Since there is no formal place to register bugs, other than the git ml,
I have added a couple of them, even mentionned on the ml, but which are
still around.
---

 TODO |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/TODO b/TODO
index d97ffd1..7dd099c 100644
--- a/TODO
+++ b/TODO
@@ -17,3 +17,12 @@ The future, when time allows or if someo
   synchronising with other patches (diff format or in other
   repositories)
 - write bash-completion script for the StGIT commands
+- support for branches with / in names
+  (ml: "Handle branch names with slashes")
+- "pull" argument should default to a sane value, "origin" is wrong in
+  many cases
+
+Bugs:
+
+- the following commands break in subdirs:
+  - refresh (ml: "Running StGIT in subdirectories")

^ permalink raw reply related

* [PATCH 4/4] Add a couple of safety checks to series creation
From: Yann Dirson @ 2006-04-13 21:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20060413213819.8806.53300.stgit@gandelf.nowhere.earth>

From: Yann Dirson <ydirson@altern.org>

Check first whether the operation can complete, instead of
bombing out halfway.
---

 stgit/commands/branch.py |    5 +++
 stgit/stack.py           |    7 ++++-
 t/t1000-branch-create.sh |   66 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/stgit/commands/branch.py b/stgit/commands/branch.py
index c4b5945..c95e529 100644
--- a/stgit/commands/branch.py
+++ b/stgit/commands/branch.py
@@ -122,12 +122,15 @@ def func(parser, options, args):
         check_conflicts()
         check_head_top_equal()
 
+        if git.branch_exists(args[0]):
+            raise CmdException, 'Branch "%s" already exists' % args[0]
+        
         tree_id = None
         if len(args) == 2:
             tree_id = git_id(args[1])
 
-        git.create_branch(args[0], tree_id)
         stack.Series(args[0]).init()
+        git.create_branch(args[0], tree_id)
 
         print 'Branch "%s" created.' % args[0]
         return
diff --git a/stgit/stack.py b/stgit/stack.py
index 92407e7..236e67f 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -431,8 +431,13 @@ class Series:
         """
         bases_dir = os.path.join(self.__base_dir, 'refs', 'bases')
 
-        if self.is_initialised():
+        if os.path.exists(self.__patch_dir):
             raise StackException, self.__patch_dir + ' already exists'
+        if os.path.exists(self.__refs_dir):
+            raise StackException, self.__refs_dir + ' already exists'
+        if os.path.exists(self.__base_file):
+            raise StackException, self.__base_file + ' already exists'
+
         os.makedirs(self.__patch_dir)
 
         if not os.path.isdir(bases_dir):
diff --git a/t/t1000-branch-create.sh b/t/t1000-branch-create.sh
new file mode 100755
index 0000000..bee0b1c
--- /dev/null
+++ b/t/t1000-branch-create.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson
+#
+
+test_description='Branch operations.
+
+Exercises the "stg branch" commands.
+'
+
+. ./test-lib.sh
+
+test_stg_init
+
+test_expect_failure \
+    'Try to create an stgit branch with a spurious refs/patches/ entry' \
+    'find .git -name foo | xargs rm -rf &&
+     touch .git/refs/patches/foo &&
+     stg branch -c foo
+'
+
+test_expect_success \
+    'Check no part of the branch was created' \
+    'test "`find .git -name foo | tee /dev/stderr`" = ".git/refs/patches/foo"
+'
+
+
+test_expect_failure \
+    'Try to create an stgit branch with a spurious patches/ entry' \
+    'find .git -name foo | xargs rm -rf &&
+     touch .git/patches/foo &&
+     stg branch -c foo
+'
+
+test_expect_success \
+    'Check no part of the branch was created' \
+    'test "`find .git -name foo | tee /dev/stderr`" = ".git/patches/foo"
+'
+
+
+test_expect_failure \
+    'Try to create an stgit branch with a spurious refs/bases/ entry' \
+    'find .git -name foo | xargs rm -rf &&
+     touch .git/refs/bases/foo &&
+     stg branch -c foo
+'
+
+test_expect_success \
+    'Check no part of the branch was created' \
+    'test "`find .git -name foo | tee /dev/stderr`" = ".git/refs/bases/foo"
+'
+
+
+# test_expect_failure \
+#     'Try to create an stgit branch with a spurious refs/heads/ entry' \
+#     'find .git -name foo | xargs rm -rf &&
+#      touch .git/refs/heads/foo &&
+#      stg branch -c foo
+# '
+
+# test_expect_success \
+#     'Check no part of the branch was created' \
+#     'test "`find .git -name foo | tee /dev/stderr`" = ".git/refs/heads/foo"
+# '
+
+test_done

^ permalink raw reply related

* [PATCH 3/4] Correctly handle refs/patches on series rename
From: Yann Dirson @ 2006-04-13 21:44 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <20060413213819.8806.53300.stgit@gandelf.nowhere.earth>

From: Yann Dirson <ydirson@altern.org>

When renaming a series, the refs/patches dir was not moved, and
by chance a new one was created by the repository-upgrade code, but
that left the old one behind as cruft.

Also added a regression test to assert that nothing by the old name
is left behind.
---

 stgit/stack.py           |    2 ++
 t/t1001-branch-rename.sh |   33 +++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/stgit/stack.py b/stgit/stack.py
index f4d7490..92407e7 100644
--- a/stgit/stack.py
+++ b/stgit/stack.py
@@ -497,6 +497,8 @@ class Series:
             os.rename(self.__series_dir, to_stack.__series_dir)
         if os.path.exists(self.__base_file):
             os.rename(self.__base_file, to_stack.__base_file)
+        if os.path.exists(self.__refs_dir):
+            os.rename(self.__refs_dir, to_stack.__refs_dir)
 
         self.__init__(to_name)
 
diff --git a/t/t1001-branch-rename.sh b/t/t1001-branch-rename.sh
new file mode 100755
index 0000000..65a5280
--- /dev/null
+++ b/t/t1001-branch-rename.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson
+#
+
+test_description='Branch operations.
+
+Exercises the "stg branch" commands.
+'
+
+. ./test-lib.sh
+
+test_stg_init
+
+test_expect_success \
+    'Create an stgit branch from scratch' \
+    'stg branch -c foo &&
+     stg new p1 -m "p1"
+'
+
+test_expect_failure \
+    'Rename the current stgit branch' \
+    'stg branch -r foo bar
+'
+
+test_expect_success \
+    'Rename an stgit branch' \
+    'stg branch -c buz &&
+     stg branch -r foo bar &&
+     test -z `find .git -name foo | tee /dev/stderr`
+'
+
+test_done

^ permalink raw reply related

* Re: [RFH] shifting xdiff hunks?
From: Davide Libenzi @ 2006-04-13 21:55 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0604122348010.7104@alien.or.mcafeemobile.com>

[-- Attachment #1: Type: TEXT/PLAIN, Size: 419 bytes --]

On Wed, 12 Apr 2006, Davide Libenzi wrote:

> Yes, this is what GNU diff does. It's a post-process of the edit script. Not 
> a problem at all. Till this weekend (included) I'm pretty booked, but I'll do 
> that in the following days.

Dang, that was a short weekend. I found a lunch-time hour for this. Would 
you try to see if this libxdiff-based diff merges on your tree?
See also how it looks for you.



- Davide


[-- Attachment #2: Type: TEXT/plain, Size: 4294 bytes --]

--- a/xdiffi.c
+++ b/xdiffi.c
@@ -45,6 +45,8 @@
 		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
 		      xdalgoenv_t *xenv);
 static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
+static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo);
+
 
 
 
@@ -394,6 +396,110 @@
 }
 
 
+static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo) {
+	long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
+	char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+	xrecord_t **recs = xdf->recs;
+
+	/*
+	 * This is the same of what GNU diff does. Move back and forward
+	 * change groups for a consistent and pretty diff output. This also
+	 * helps in finding joineable change groups and reduce the diff size.
+	 */
+	for (ix = ixo = 0;;) {
+		/*
+		 * Find the first changed line in the to-be-compacted file.
+		 * We need to keep track of both indexes, so if we find a
+		 * changed lines group on the other file, while scanning the
+		 * to-be-compacted file, we need to skip it properly. Note
+		 * that loops that are testing for changed lines on rchg* do
+		 * not need index bounding since the array is prepared with
+		 * a zero at position -1 and N.
+		 */
+		for (; ix < nrec && !rchg[ix]; ix++)
+			while (rchgo[ixo++]);
+		if (ix == nrec)
+			break;
+
+		/*
+		 * Record the start of a changed-group in the to-be-compacted file
+		 * and find the end of it, on both to-be-compacted and other file
+		 * indexes (ix and ixo).
+		 */
+		ixs = ix;
+		for (ix++; rchg[ix]; ix++);
+		for (; rchgo[ixo]; ixo++);
+
+		do {
+			grpsiz = ix - ixs;
+
+			/*
+			 * If the line before the current change group, is equal to
+			 * the last line of the current change group, shift backward
+			 * the group.
+			 */
+			while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
+			       XDL_RECMATCH(recs[ixs - 1], recs[ix - 1])) {
+				rchg[--ixs] = 1;
+				rchg[--ix] = 0;
+
+				/*
+				 * This change might have joined two change groups,
+				 * so we try to take this scenario in account by moving
+				 * the start index accordingly (and so the other-file
+				 * end-of-group index).
+				 */
+				for (; rchg[ixs - 1]; ixs--);
+				while (rchgo[--ixo]);
+			}
+
+			/*
+			 * Record the end-of-group position in case we are matched
+			 * with a group of changes in the other file (that is, the
+			 * change record before the enf-of-group index in the other
+			 * file is set).
+			 */
+			ixref = rchgo[ixo - 1] ? ix: nrec;
+
+			/*
+			 * If the first line of the current change group, is equal to
+			 * the line next of the current change group, shift forward
+			 * the group.
+			 */
+			while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
+			       XDL_RECMATCH(recs[ixs], recs[ix])) {
+				rchg[ixs++] = 0;
+				rchg[ix++] = 1;
+
+				/*
+				 * This change might have joined two change groups,
+				 * so we try to take this scenario in account by moving
+				 * the start index accordingly (and so the other-file
+				 * end-of-group index). Keep tracking the reference
+				 * index in case we are shifting together with a
+				 * corresponding group of changes in the other file.
+				 */
+				for (; rchg[ix]; ix++);
+				while (rchgo[++ixo])
+					ixref = ix;
+			}
+		} while (grpsiz != ix - ixs);
+
+		/*
+		 * Try to move back the possibly merged group of changes, to match
+		 * the recorded postion in the other file.
+		 */
+		while (ixref < ix) {
+			rchg[--ixs] = 1;
+			rchg[--ix] = 0;
+			while (rchgo[--ixo]);
+		}
+	}
+
+	return 0;
+}
+
+
 int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
 	xdchange_t *cscr = NULL, *xch;
 	char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
@@ -439,13 +545,13 @@
 
 		return -1;
 	}
-
-	if (xdl_build_script(&xe, &xscr) < 0) {
+	if (xdl_change_compact(&xe.xdf1, &xe.xdf2) < 0 ||
+	    xdl_change_compact(&xe.xdf2, &xe.xdf1) < 0 ||
+	    xdl_build_script(&xe, &xscr) < 0) {
 
 		xdl_free_env(&xe);
 		return -1;
 	}
-
 	if (xscr) {
 		if (xdl_emit_diff(&xe, xscr, ecb, xecfg) < 0) {
 
@@ -453,10 +559,8 @@
 			xdl_free_env(&xe);
 			return -1;
 		}
-
 		xdl_free_script(xscr);
 	}
-
 	xdl_free_env(&xe);
 
 	return 0;




^ permalink raw reply

* [PATCH] Shell utilities: Guard against expr' magic tokens.
From: Mark Wooding @ 2006-04-13 22:01 UTC (permalink / raw)
  To: git

From: Mark Wooding <mdw@distorted.org.uk>

Some words, e.g., `match', are special to expr(1), and cause strange
parsing effects.  Track down all uses of expr and mangle the arguments
so that this isn't a problem.

Signed-off-by: Mark Wooding <mdw@distorted.org.uk>
---
Amusing one, this.  I hacked on one of my projects, messing with a
simple glob matching function.  Being uncreative, I called my topic
branch `match'.  When I was ready, I switched back to my master branch
and said

  $ git pull . match
  Already up-to-date.

Oh.  I checked.  Nope, not up-to-date.  I tried 

  $ git merge fast HEAD match, and that

and that did the right thing.  But I was puzzled.  I fired up the
git-bisect machinery and tried to find a good version to no avail.  And
then, comparing `sh -x' traces of git-fetch, I noticed what had gone
wrong.

There's a line in git-parse-remote.sh, in canon_refs_list_for_fetch,
which says

  expr "$ref" : '.*:' >/dev/null || ref="${ref}:"

In my case, $ref is `match', so this expands to

  expr match : '.*:' >...

Unfortunately, GNU expr has a magic keyword `match'.  So what this does
is compare `:' to the regexp `.*:', which /succeeds/, even though POSIX
expr without the `match' keyword would do the right thing and fail.  So
$ref never has a `:' appended, which makes the later parsing fail, and
all sorts of strange things happen.

This patch puts magical extra characters in expr regexp calls
throughout the shell bits of GIT, to robustify them against this kind of
crapness.

There's a small chance I got something wrong while making this fix.  I
was fairly careful, though, and ran the test suite without any
problems.  I also checked Cogito, though that has no truck with expr.

---

 git-cherry.sh         |    2 +-
 git-clone.sh          |    6 +++---
 git-commit.sh         |    4 ++--
 git-fetch.sh          |   18 +++++++++---------
 git-format-patch.sh   |    4 ++--
 git-merge-one-file.sh |    2 +-
 git-parse-remote.sh   |   20 ++++++++++----------
 git-rebase.sh         |    2 +-
 git-tag.sh            |    2 +-
 9 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/git-cherry.sh b/git-cherry.sh
index 1a62320..f0e8831 100755
--- a/git-cherry.sh
+++ b/git-cherry.sh
@@ -20,7 +20,7 @@ case "$1" in -v) verbose=t; shift ;; esa
 
 case "$#,$1" in
 1,*..*)
-    upstream=$(expr "$1" : '\(.*\)\.\.') ours=$(expr "$1" : '.*\.\.\(.*\)$')
+    upstream=$(expr "z$1" : 'z\(.*\)\.\.') ours=$(expr "z$1" : '.*\.\.\(.*\)$')
     set x "$upstream" "$ours"
     shift ;;
 esac
diff --git a/git-clone.sh b/git-clone.sh
index c013e48..0805168 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -38,12 +38,12 @@ Perhaps git-update-server-info needs to 
 	}
 	while read sha1 refname
 	do
-		name=`expr "$refname" : 'refs/\(.*\)'` &&
+		name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
 		case "$name" in
 		*^*)	continue;;
 		esac
 		if test -n "$use_separate_remote" &&
-		   branch_name=`expr "$name" : 'heads/\(.*\)'`
+		   branch_name=`expr "z$name" : 'zheads/\(.*\)'`
 		then
 			tname="remotes/$origin/$branch_name"
 		else
@@ -346,7 +346,7 @@ then
 		# new style repository with a symref HEAD).
 		# Ideally we should skip the guesswork but for now
 		# opt for minimum change.
-		head_sha1=`expr "$head_sha1" : 'ref: refs/heads/\(.*\)'`
+		head_sha1=`expr "z$head_sha1" : 'zref: refs/heads/\(.*\)'`
 		head_sha1=`cat "$GIT_DIR/$remote_top/$head_sha1"`
 		;;
 	esac
diff --git a/git-commit.sh b/git-commit.sh
index bd3dc71..01c73bd 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -549,8 +549,8 @@ fi >>"$GIT_DIR"/COMMIT_EDITMSG
 # Author
 if test '' != "$force_author"
 then
-	GIT_AUTHOR_NAME=`expr "$force_author" : '\(.*[^ ]\) *<.*'` &&
-	GIT_AUTHOR_EMAIL=`expr "$force_author" : '.*\(<.*\)'` &&
+	GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
+	GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
 	test '' != "$GIT_AUTHOR_NAME" &&
 	test '' != "$GIT_AUTHOR_EMAIL" ||
 	die "malformatted --author parameter"
diff --git a/git-fetch.sh b/git-fetch.sh
index 954901d..711650f 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -112,7 +112,7 @@ append_fetch_head () {
     *)
 	note_="$remote_name of " ;;
     esac
-    remote_1_=$(expr "$remote_" : '\(.*\)\.git/*$') &&
+    remote_1_=$(expr "z$remote_" : 'z\(.*\)\.git/*$') &&
 	remote_="$remote_1_"
     note_="$note_$remote_"
 
@@ -245,22 +245,22 @@ fetch_main () {
 
       # These are relative path from $GIT_DIR, typically starting at refs/
       # but may be HEAD
-      if expr "$ref" : '\.' >/dev/null
+      if expr "z$ref" : 'z\.' >/dev/null
       then
 	  not_for_merge=t
-	  ref=$(expr "$ref" : '\.\(.*\)')
+	  ref=$(expr "z$ref" : 'z\.\(.*\)')
       else
 	  not_for_merge=
       fi
-      if expr "$ref" : '\+' >/dev/null
+      if expr "z$ref" : 'z\+' >/dev/null
       then
 	  single_force=t
-	  ref=$(expr "$ref" : '\+\(.*\)')
+	  ref=$(expr "z$ref" : 'z\+\(.*\)')
       else
 	  single_force=
       fi
-      remote_name=$(expr "$ref" : '\([^:]*\):')
-      local_name=$(expr "$ref" : '[^:]*:\(.*\)')
+      remote_name=$(expr "z$ref" : 'z\([^:]*\):')
+      local_name=$(expr "z$ref" : 'z[^:]*:\(.*\)')
 
       rref="$rref$LF$remote_name"
 
@@ -276,7 +276,7 @@ fetch_main () {
 	      print "$u";
 	  ' "$remote_name")
 	  head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
-	  expr "$head" : "$_x40\$" >/dev/null ||
+	  expr "z$head" : "z$_x40\$" >/dev/null ||
 		  die "Failed to fetch $remote_name from $remote"
 	  echo >&2 Fetching "$remote_name from $remote" using http
 	  git-http-fetch -v -a "$head" "$remote/" || exit
@@ -362,7 +362,7 @@ fetch_main () {
 		  break ;;
 	      esac
 	  done
-	  local_name=$(expr "$found" : '[^:]*:\(.*\)')
+	  local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
 	  append_fetch_head "$sha1" "$remote" \
 		  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
       done
diff --git a/git-format-patch.sh b/git-format-patch.sh
index 2ebf7e8..c7133bc 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -126,8 +126,8 @@ for revpair
 do
 	case "$revpair" in
 	?*..?*)
-		rev1=`expr "$revpair" : '\(.*\)\.\.'`
-		rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
+		rev1=`expr "z$revpair" : 'z\(.*\)\.\.'`
+		rev2=`expr "z$revpair" : 'z.*\.\.\(.*\)'`
 		;;
 	*)
 		rev1="$revpair^"
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index 5349a1c..5619409 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -26,7 +26,7 @@ #
 	fi
 	if test -f "$4"; then
 		rm -f -- "$4" &&
-		rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null || :
+		rmdir -p "$(expr "z$4" : 'z\(.*\)/')" 2>/dev/null || :
 	fi &&
 		exec git-update-index --remove -- "$4"
 	;;
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 63f2281..65c66d5 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -8,8 +8,8 @@ get_data_source () {
 	case "$1" in
 	*/*)
 		# Not so fast.	This could be the partial URL shorthand...
-		token=$(expr "$1" : '\([^/]*\)/')
-		remainder=$(expr "$1" : '[^/]*/\(.*\)')
+		token=$(expr "z$1" : 'z\([^/]*\)/')
+		remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
 		if test -f "$GIT_DIR/branches/$token"
 		then
 			echo branches-partial
@@ -43,8 +43,8 @@ get_remote_url () {
 	branches)
 		sed -e 's/#.*//' "$GIT_DIR/branches/$1" ;;
 	branches-partial)
-		token=$(expr "$1" : '\([^/]*\)/')
-		remainder=$(expr "$1" : '[^/]*/\(.*\)')
+		token=$(expr "z$1" : 'z\([^/]*\)/')
+		remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
 		url=$(sed -e 's/#.*//' "$GIT_DIR/branches/$token")
 		echo "$url/$remainder"
 		;;
@@ -77,13 +77,13 @@ canon_refs_list_for_fetch () {
 		force=
 		case "$ref" in
 		+*)
-			ref=$(expr "$ref" : '\+\(.*\)')
+			ref=$(expr "z$ref" : 'z\+\(.*\)')
 			force=+
 			;;
 		esac
-		expr "$ref" : '.*:' >/dev/null || ref="${ref}:"
-		remote=$(expr "$ref" : '\([^:]*\):')
-		local=$(expr "$ref" : '[^:]*:\(.*\)')
+		expr "z$ref" : 'z.*:' >/dev/null || ref="${ref}:"
+		remote=$(expr "z$ref" : 'z\([^:]*\):')
+		local=$(expr "z$ref" : 'z[^:]*:\(.*\)')
 		case "$remote" in
 		'') remote=HEAD ;;
 		refs/heads/* | refs/tags/* | refs/remotes/*) ;;
@@ -97,7 +97,7 @@ canon_refs_list_for_fetch () {
 		*) local="refs/heads/$local" ;;
 		esac
 
-		if local_ref_name=$(expr "$local" : 'refs/\(.*\)')
+		if local_ref_name=$(expr "z$local" : 'zrefs/\(.*\)')
 		then
 		   git-check-ref-format "$local_ref_name" ||
 		   die "* refusing to create funny ref '$local_ref_name' locally"
@@ -171,7 +171,7 @@ get_remote_refs_for_fetch () {
 
 resolve_alternates () {
 	# original URL (xxx.git)
-	top_=`expr "$1" : '\([^:]*:/*[^/]*\)/'`
+	top_=`expr "z$1" : 'z\([^:]*:/*[^/]*\)/'`
 	while read path
 	do
 		case "$path" in
diff --git a/git-rebase.sh b/git-rebase.sh
index 5956f06..86dfe9c 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -94,7 +94,7 @@ case "$#" in
 	;;
 *)
 	branch_name=`git symbolic-ref HEAD` || die "No current branch"
-	branch_name=`expr "$branch_name" : 'refs/heads/\(.*\)'`
+	branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
 	;;
 esac
 branch=$(git-rev-parse --verify "${branch_name}^0") || exit
diff --git a/git-tag.sh b/git-tag.sh
index 76e51ed..dc6aa95 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -75,7 +75,7 @@ git-check-ref-format "tags/$name" ||
 object=$(git-rev-parse --verify --default HEAD "$@") || exit 1
 type=$(git-cat-file -t $object) || exit 1
 tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
-: ${username:=$(expr "$tagger" : '\(.*>\)')}
+: ${username:=$(expr "z$tagger" : 'z\(.*>\)')}
 
 trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0
 



-- [mdw]

^ permalink raw reply related

* [PATCH] diff-options: add --stat (take 2)
From: Johannes Schindelin @ 2006-04-13 22:15 UTC (permalink / raw)
  To: git, junkio


Now, you can say "git diff --stat" (to get an idea how many changes are
uncommitted), or "git log --stat".
    
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>

---

	Thanks to Junio's comments, this looks much better now; I am
	reasonably happy about it (it even lost some pounds: 31 lines).

	I still did not find a way to share code with git-apply's diffstat
	code, though. But then, it is only one function (show_stats).

 Documentation/diff-options.txt |    3 +
 diff.c                         |  220 +++++++++++++++++++++++++++++++++++++++-
 diff.h                         |    2 
 git-diff.sh                    |    6 +
 4 files changed, 225 insertions(+), 6 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 338014c..447e522 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -7,6 +7,9 @@
 --patch-with-raw::
 	Generate patch but keep also the default raw diff output.
 
+--stat::
+	Generate a diffstat instead of a patch.
+
 -z::
 	\0 line termination on output
 
diff --git a/diff.c b/diff.c
index a14e664..ad8478b 100644
--- a/diff.c
+++ b/diff.c
@@ -8,7 +8,7 @@ #include "cache.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
-#include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
 
 static int use_size_cache;
 
@@ -195,6 +195,137 @@ static int fn_out(void *priv, mmbuffer_t
 	return 0;
 }
 
+struct diffstat_t {
+	struct xdiff_emit_state xm;
+
+	int nr;
+	int alloc;
+	struct diffstat_file {
+		char *name;
+		unsigned int added, deleted;
+	} **files;
+};
+
+static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
+		const char *name)
+{
+	struct diffstat_file *x;
+	x = xcalloc(sizeof (*x), 1);
+	if (diffstat->nr == diffstat->alloc) {
+		diffstat->alloc = alloc_nr(diffstat->alloc);
+		diffstat->files = xrealloc(diffstat->files,
+				diffstat->alloc * sizeof(x));
+	}
+	diffstat->files[diffstat->nr++] = x;
+	x->name = strdup(name);
+	return x;
+}
+
+static void diffstat_consume(void *priv, char *line, unsigned long len)
+{
+	struct diffstat_t *diffstat = priv;
+	struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
+
+	if (line[0] == '+')
+		x->added++;
+	else if (line[0] == '-')
+		x->deleted++;
+}
+
+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct diffstat_t* data)
+{
+	char *prefix = "";
+	int i, len, add, del, total, adds = 0, dels = 0;
+	int max, max_change = 0, max_len = 0;
+	int total_files = data->nr;
+
+	if (data->nr == 0)
+		return;
+
+	printf("---\n");
+
+	for (i = 0; i < data->nr; i++) {
+		struct diffstat_file *file = data->files[i];
+		
+		if (max_change < file->added + file->deleted)
+			max_change = file->added + file->deleted;
+		len = strlen(file->name);
+		if (max_len < len)
+			max_len = len;
+	}
+
+	for (i = 0; i < data->nr; i++) {
+		char *name = data->files[i]->name;
+		int added = data->files[i]->added;
+		int deleted = data->files[i]->deleted;
+
+		if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+			char *qname = xmalloc(len + 1);
+			quote_c_style(name, qname, NULL, 0);
+			free(name);
+			name = qname;
+		}
+
+		/*
+		 * "scale" the filename
+		 */
+		len = strlen(name);
+		max = max_len;
+		if (max > 50)
+			max = 50;
+		if (len > max) {
+			char *slash;
+			prefix = "...";
+			max -= 3;
+			name += len - max;
+			slash = strchr(name, '/');
+			if (slash)
+				name = slash;
+		}
+		len = max;
+
+		/*
+		 * scale the add/delete
+		 */
+		max = max_change;
+		if (max + len > 70)
+			max = 70 - len;
+
+		if (added < 0) {
+			/* binary file */
+			printf(" %s%-*s |  Bin\n", prefix, len, name);
+			continue;
+		} else if (added + deleted == 0) {
+			total_files--;
+			continue;
+		}
+
+		add = added;
+		del = deleted;
+		total = add + del;
+		adds += add;
+		dels += del;
+
+		if (max_change > 0) {
+			total = (total * max + max_change / 2) / max_change;
+			add = (add * max + max_change / 2) / max_change;
+			del = total - add;
+		}
+		/* TODO: binary */
+		printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+				len, name, added + deleted,
+				add, pluses, del, minuses);
+		free(name);
+		free(data->files[i]);
+	}
+	free(data->files);
+	printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+			total_files, adds, dels);
+}
+
 #define FIRST_FEW_BYTES 8000
 static int mmfile_is_binary(mmfile_t *mf)
 {
@@ -285,7 +416,36 @@ static void builtin_diff(const char *nam
 	free(b_two);
 	return;
 }
+
+static void builtin_diffstat(const char *name_a, const char *name_b,
+		struct diff_filespec *one, struct diff_filespec *two,
+		struct diffstat_t *diffstat)
+{
+	mmfile_t mf1, mf2;
+	struct diffstat_file *data;
 
+	data = diffstat_add(diffstat, name_a ? name_a : name_b);
+
+	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+		die("unable to read files to diff");
+
+	if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+		data->added = -1;
+	else {
+		/* Crazy xdl interfaces.. */
+		xpparam_t xpp;
+		xdemitconf_t xecfg;
+		xdemitcb_t ecb;
+
+		xpp.flags = XDF_NEED_MINIMAL;
+		xecfg.ctxlen = 3;
+		xecfg.flags = XDL_EMIT_FUNCNAMES;
+		ecb.outf = xdiff_outf;
+		ecb.priv = diffstat;
+		xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+	}
+}
+
 struct diff_filespec *alloc_filespec(const char *path)
 {
 	int namelen = strlen(path);
@@ -818,7 +978,28 @@ static void run_diff(struct diff_filepai
 	free(name_munged);
 	free(other_munged);
 }
+
+static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
+		struct diffstat_t *diffstat)
+{
+	const char *name;
+	const char *other;
 
+	if (DIFF_PAIR_UNMERGED(p)) {
+		/* unmerged */
+		builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);
+		return;
+	}
+
+	name = p->one->path;
+	other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+
+	diff_fill_sha1_info(p->one);
+	diff_fill_sha1_info(p->two);
+
+	builtin_diffstat(name, other, p->one, p->two, diffstat);
+}
+
 void diff_setup(struct diff_options *options)
 {
 	memset(options, 0, sizeof(*options));
@@ -866,6 +1047,8 @@ int diff_opt_parse(struct diff_options *
 		options->output_format = DIFF_FORMAT_PATCH;
 		options->with_raw = 1;
 	}
+	else if (!strcmp(arg, "--stat"))
+		options->output_format = DIFF_FORMAT_DIFFSTAT;
 	else if (!strcmp(arg, "-z"))
 		options->line_termination = 0;
 	else if (!strncmp(arg, "-l", 2))
@@ -1163,6 +1346,19 @@ static void diff_flush_patch(struct diff
 		return; /* no tree diffs in patch format */ 
 
 	run_diff(p, o);
+}
+
+static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
+		struct diffstat_t *diffstat)
+{
+	if (diff_unmodified_pair(p))
+		return;
+
+	if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+	    (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+		return; /* no tree diffs in patch format */ 
+
+	run_diffstat(p, o, diffstat);
 }
 
 int diff_queue_is_empty(void)
@@ -1276,7 +1472,8 @@ static void diff_resolve_rename_copy(voi
 
 static void flush_one_pair(struct diff_filepair *p,
 			   int diff_output_format,
-			   struct diff_options *options)
+			   struct diff_options *options,
+			   struct diffstat_t *diffstat)
 {
 	int inter_name_termination = '\t';
 	int line_termination = options->line_termination;
@@ -1291,6 +1488,9 @@ static void flush_one_pair(struct diff_f
 		break;
 	default:
 		switch (diff_output_format) {
+		case DIFF_FORMAT_DIFFSTAT:
+			diff_flush_stat(p, options, diffstat);
+			break;
 		case DIFF_FORMAT_PATCH:
 			diff_flush_patch(p, options);
 			break;
@@ -1316,19 +1516,31 @@ void diff_flush(struct diff_options *opt
 	struct diff_queue_struct *q = &diff_queued_diff;
 	int i;
 	int diff_output_format = options->output_format;
+	struct diffstat_t *diffstat = NULL;
 
+	if (diff_output_format == DIFF_FORMAT_DIFFSTAT) {
+		diffstat = xcalloc(sizeof (struct diffstat_t), 1);
+		diffstat->xm.consume = diffstat_consume;
+	}
+
 	if (options->with_raw) {
 		for (i = 0; i < q->nr; i++) {
 			struct diff_filepair *p = q->queue[i];
-			flush_one_pair(p, DIFF_FORMAT_RAW, options);
+			flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
 		}
 		putchar(options->line_termination);
 	}
 	for (i = 0; i < q->nr; i++) {
 		struct diff_filepair *p = q->queue[i];
-		flush_one_pair(p, diff_output_format, options);
+		flush_one_pair(p, diff_output_format, options, diffstat);
 		diff_free_filepair(p);
 	}
+
+	if (diffstat) {
+		show_stats(diffstat);
+		free(diffstat);
+	}
+
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
diff --git a/diff.h b/diff.h
index 236095f..2f8aff2 100644
--- a/diff.h
+++ b/diff.h
@@ -119,6 +119,7 @@ #define COMMON_DIFF_OPTIONS_HELP \
 "  -u            synonym for -p.\n" \
 "  --patch-with-raw\n" \
 "                output both a patch and the diff-raw format.\n" \
+"  --stat        show diffstat instead of patch.\n" \
 "  --name-only   show only names of changed files.\n" \
 "  --name-status show names and status of changed files.\n" \
 "  --full-index  show full object name on index lines.\n" \
@@ -142,6 +143,7 @@ #define DIFF_FORMAT_PATCH	2
 #define DIFF_FORMAT_NO_OUTPUT	3
 #define DIFF_FORMAT_NAME	4
 #define DIFF_FORMAT_NAME_STATUS	5
+#define DIFF_FORMAT_DIFFSTAT	6
 
 extern void diff_flush(struct diff_options*);
 
diff --git a/git-diff.sh b/git-diff.sh
index dc0dd31..0fe6770 100755
--- a/git-diff.sh
+++ b/git-diff.sh
@@ -30,9 +30,11 @@ case " $flags " in
 	cc_or_p=--cc ;;
 esac
 
-# If we do not have --name-status, --name-only, -r, or -c default to --cc.
+# If we do not have --name-status, --name-only, -r, -c or --stat,
+# default to --cc.
 case " $flags " in
-*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* )
+*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \
+*" '--stat' "*)
 	;;
 *)
 	flags="$flags'$cc_or_p' " ;;

^ permalink raw reply related

* Re: [PATCH] diff-options: add --stat (take 2)
From: Johannes Schindelin @ 2006-04-13 23:09 UTC (permalink / raw)
  To: git, junkio
In-Reply-To: <Pine.LNX.4.63.0604140012560.10924@wbgn013.biozentrum.uni-wuerzburg.de>

... and a fix for an invalid free():

---

 diff.c |   10 +++++-----
 1 files changed, 5 insertions(+), 5 deletions(-)

14d8e3c7cda1e2aaff62375fe34db2458d302173
diff --git a/diff.c b/diff.c
index ad8478b..2968153 100644
--- a/diff.c
+++ b/diff.c
@@ -266,7 +266,7 @@ static void show_stats(struct diffstat_t
 			char *qname = xmalloc(len + 1);
 			quote_c_style(name, qname, NULL, 0);
 			free(name);
-			name = qname;
+			data->files[i]->name = name = qname;
 		}
 
 		/*
@@ -297,10 +297,10 @@ static void show_stats(struct diffstat_t
 		if (added < 0) {
 			/* binary file */
 			printf(" %s%-*s |  Bin\n", prefix, len, name);
-			continue;
+			goto free_diffstat_file;
 		} else if (added + deleted == 0) {
 			total_files--;
-			continue;
+			goto free_diffstat_file;
 		}
 
 		add = added;
@@ -314,11 +314,11 @@ static void show_stats(struct diffstat_t
 			add = (add * max + max_change / 2) / max_change;
 			del = total - add;
 		}
-		/* TODO: binary */
 		printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
 				len, name, added + deleted,
 				add, pluses, del, minuses);
-		free(name);
+	free_diffstat_file:
+		free(data->files[i]->name);
 		free(data->files[i]);
 	}
 	free(data->files);
-- 
1.3.0.rc3.g9813

^ permalink raw reply related

* Re: [RFH] shifting xdiff hunks?
From: Junio C Hamano @ 2006-04-13 23:31 UTC (permalink / raw)
  To: Davide Libenzi; +Cc: git
In-Reply-To: <Pine.LNX.4.64.0604131452250.10564@alien.or.mcafeemobile.com>

Davide Libenzi <davidel@xmailserver.org> writes:

> On Wed, 12 Apr 2006, Davide Libenzi wrote:
>
>> Yes, this is what GNU diff does. It's a post-process of the edit
>> script. Not a problem at all. Till this weekend (included) I'm
>> pretty booked, but I'll do that in the following days.
>
> Dang, that was a short weekend. I found a lunch-time hour for
> this. Would you try to see if this libxdiff-based diff merges on your
> tree?
> See also how it looks for you.

Very impressed, and pleased with the result.  I've only taken a
cursory look, but with a very limited number of tests, it looks
much better.  Thanks.

For the sake of full disclosure, the reason I wanted consistency
was not for the diff output I quoted earlier, but to help making
the combined patch output cleaner.  It does reduce false match
from the infamous 12-way Octopus by Len Brown:

	git diff-tree --cc 9fdb62af92c741addbea15545f214a6e89460865

^ permalink raw reply


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