Git development
 help / color / mirror / Atom feed
* Re: Can't understand the behaviour of git-diff --submodule
From: Francis Moreau @ 2012-10-28  8:26 UTC (permalink / raw)
  To: Jens Lehmann; +Cc: git
In-Reply-To: <508C6823.5060800@web.de>

On Sun, Oct 28, 2012 at 1:02 AM, Jens Lehmann <Jens.Lehmann@web.de> wrote:
> Am 26.10.2012 22:43, schrieb Francis Moreau:
>> On Fri, Oct 26, 2012 at 10:05 PM, Jens Lehmann <Jens.Lehmann@web.de> wrote:
>> [...]
>>>
>>> That is weird, "git diff --submodule" should show that too. Is there
>>> anything unusual about your setup? (The only explanation I can come
>>> up with after checking the code is that your submodule has neither a
>>> .git directory nor a gitfile or the objects directory in there doesn't
>>> contain these commits)
>>
>> Oh now you're asking, I think the submodule has been added by using
>> the --reference option of git-submodule-add.
>>
>>   $ cd configs
>>   $ cat .git
>>   gitdir: ../.git/modules/configs
>
> Thanks, I suspect the --reference option makes the difference here,
> I'll check that as soon as I find some time.

Yes that's fairly easy to reproduce:

$ mkdir super
$ cd super
$ git init
$ git submodule add --reference ~/tmp/git-submodule/public/a.git
~/tmp/git-submodule/public/a.git a
$ git commit -m "Initial creation of super project"
$ cd a
$ date >a.txt
$ git commit -a -m "add to a.txt a random change"
$ cd ..
$ git submodule summary
* a 2f8803a...a1aa4bf (1):
  > add to a.txt a random change
$ git commit -a -m "Include a's changes in super project"
$ git diff --submodule=log HEAD~1 HEAD
Submodule a 2f8803a...a1aa4bf (commits not present)

Thanks
-- 
Francis

^ permalink raw reply

* Re: Mistake in git-reset documentation
From: Krzysztof Mazur @ 2012-10-28  8:36 UTC (permalink / raw)
  To: Bojan Petrović; +Cc: git
In-Reply-To: <CABPGWqr7=Rq4qS7yP09t2vMBUJ98NFTSmHUUgMzUQ5=WVrjfqg@mail.gmail.com>

On Sat, Oct 27, 2012 at 01:21:18PM +0200, Bojan Petrović wrote:
> None of the three forms of git-reset accept: "git reset" which is the
> equivalent of "git reset -mixed".
> 
> Square brackets should be used instead of parentheses for "--soft |
> --mixed | --hard | --merge | --keep".
> 
> Bojan

Square brackets are also missing in "'git reset' --<mode> [<commit>]".

Bojan, do you want to add a Reported-by line with your name?

Krzysiek

-- >8 --
Subject: [PATCH] doc: git-reset: make "--<mode>" optional

The git-reset's "--<mode>" is an optional argument, however it was
documented as required.

Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net>
---
 Documentation/git-reset.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 117e374..1f95292 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git reset' [-q] [<commit>] [--] <paths>...
 'git reset' (--patch | -p) [<commit>] [--] [<paths>...]
-'git reset' (--soft | --mixed | --hard | --merge | --keep) [-q] [<commit>]
+'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
@@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
 you can use it to selectively reset hunks. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
-'git reset' --<mode> [<commit>]::
+'git reset' [--<mode>] [<commit>]::
 	This form resets the current branch head to <commit> and
 	possibly updates the index (resetting it to the tree of <commit>) and
 	the working tree depending on <mode>, which
-- 
1.8.0.46.gd11dae0

^ permalink raw reply related

* Re: Mistake in git-reset documentation
From: Bojan Petrović @ 2012-10-28  9:32 UTC (permalink / raw)
  To: Krzysztof Mazur; +Cc: git
In-Reply-To: <20121028083610.GA26374@shrek.podlesie.net>

No need for that. Getting this patched will be enough. :) Thank you!

On 28 October 2012 09:36, Krzysztof Mazur <krzysiek@podlesie.net> wrote:
> On Sat, Oct 27, 2012 at 01:21:18PM +0200, Bojan Petrović wrote:
>> None of the three forms of git-reset accept: "git reset" which is the
>> equivalent of "git reset -mixed".
>>
>> Square brackets should be used instead of parentheses for "--soft |
>> --mixed | --hard | --merge | --keep".
>>
>> Bojan
>
> Square brackets are also missing in "'git reset' --<mode> [<commit>]".
>
> Bojan, do you want to add a Reported-by line with your name?
>
> Krzysiek
>
> -- >8 --
> Subject: [PATCH] doc: git-reset: make "--<mode>" optional
>
> The git-reset's "--<mode>" is an optional argument, however it was
> documented as required.
>
> Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net>
> ---
>  Documentation/git-reset.txt | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
> index 117e374..1f95292 100644
> --- a/Documentation/git-reset.txt
> +++ b/Documentation/git-reset.txt
> @@ -10,7 +10,7 @@ SYNOPSIS
>  [verse]
>  'git reset' [-q] [<commit>] [--] <paths>...
>  'git reset' (--patch | -p) [<commit>] [--] [<paths>...]
> -'git reset' (--soft | --mixed | --hard | --merge | --keep) [-q] [<commit>]
> +'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
>
>  DESCRIPTION
>  -----------
> @@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
>  you can use it to selectively reset hunks. See the ``Interactive Mode''
>  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
>
> -'git reset' --<mode> [<commit>]::
> +'git reset' [--<mode>] [<commit>]::
>         This form resets the current branch head to <commit> and
>         possibly updates the index (resetting it to the tree of <commit>) and
>         the working tree depending on <mode>, which
> --
> 1.8.0.46.gd11dae0
>

^ permalink raw reply

* Re: [PATCH] mailmap: avoid out-of-bounds memory access
From: Jeff King @ 2012-10-28 11:02 UTC (permalink / raw)
  To: Romain Francoise; +Cc: git, gitster
In-Reply-To: <87k3ub4jjg.fsf@silenus.orebokech.com>

On Sun, Oct 28, 2012 at 12:49:55AM +0200, Romain Francoise wrote:

> AddressSanitizer (http://clang.llvm.org/docs/AddressSanitizer.html)
> complains of a one-byte buffer underflow in parse_name_and_email() while
> running the test suite. And indeed, if one of the lines in the mailmap
> begins with '<', we dereference the address just before the beginning of
> the buffer when looking for whitespace to remove, before checking that
> we aren't going too far.
> 
> So reverse the order of the tests to make sure that we don't read
> outside the buffer.

Thanks, I think your fix is correct.

> diff --git a/mailmap.c b/mailmap.c
> index 47aa419..ea4b471 100644
> --- a/mailmap.c
> +++ b/mailmap.c
> @@ -118,7 +118,7 @@ static char *parse_name_and_email(char *buffer, char **name,
>  	while (isspace(*nstart) && nstart < left)
>  		++nstart;
>  	nend = left-1;
> -	while (isspace(*nend) && nend > nstart)
> +	while (nend > nstart && isspace(*nend))
>  		--nend;

The fix confused me for a moment, because the problem is not actually in
the loop condition itself; working backwards from "nend > nstart", we
will at worst dereference nstart unnecessarily. The real problem is in
the "nend = left-1" above, which sets the loop precondition outside the
string to be examined.

So you could also check for "left == nstart" before the loop even
begins. I think your fix (to just make the loop more robust to that
precondition) is better, though, as the rest of the code does the right
thing with such a value of nend.

It looks like t4203 triggers this problem. Curious that valgrind does
not find it. I guess since it does not have compiler support, it cannot
find out-of-bound errors on stack buffers. Does the rest of the test
suite turn up clean with AddressSanitizer?

-Peff

^ permalink raw reply

* Re: Mistake in git-reset documentation
From: Jeff King @ 2012-10-28 11:07 UTC (permalink / raw)
  To: Krzysztof Mazur; +Cc: Bojan Petrović, git
In-Reply-To: <20121028083610.GA26374@shrek.podlesie.net>

On Sun, Oct 28, 2012 at 09:36:10AM +0100, Krzysztof Mazur wrote:

> -- >8 --
> Subject: [PATCH] doc: git-reset: make "--<mode>" optional
> 
> The git-reset's "--<mode>" is an optional argument, however it was
> documented as required.
> 
> Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net>

I think this is sane, but...

>  DESCRIPTION
>  -----------
> @@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
>  you can use it to selectively reset hunks. See the ``Interactive Mode''
>  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
>  
> -'git reset' --<mode> [<commit>]::
> +'git reset' [--<mode>] [<commit>]::
>  	This form resets the current branch head to <commit> and
>  	possibly updates the index (resetting it to the tree of <commit>) and
>  	the working tree depending on <mode>, which

Should we say something like "if --<mode> is omitted, defaults to
"--mixed"?

-Peff

^ permalink raw reply

* Re: [PATCH] Fix t9200 on case insensitive file systems
From: Jeff King @ 2012-10-28 11:10 UTC (permalink / raw)
  To: Torsten Bögershausen; +Cc: git, bdwalton, bosch, brian, robin.rosenberg
In-Reply-To: <201210261818.25620.tboegi@web.de>

On Fri, Oct 26, 2012 at 06:18:24PM +0200, Torsten Bögershausen wrote:

> t9200 defines $CVSROOT where cvs should init its repository
> $CVSROOT is set to $PWD/cvsroot.
> cvs init is supposed to create the repository inside $PWD/cvsroot/CVSROOT
> 
> "cvs init" (e.g. version  1.11.23) checks if the last element of the path is
> "CVSROOT", and if a directory with e.g. $PWD/cvsroot/CVSROOT already exists.
> 
> For such a $CVSROOT cvs refuses to init a repository here:
> "Cannot initialize repository under existing CVSROOT:
> 
> On a case insenstive file system cvsroot and CVSROOT are the same directories
> and t9200 fails.
> 
> Solution: use $PWD/tmp/cvsroot instead of cvsroot $PWD/cvsroot

Wouldn't tmp/cvsroot have the same problem, since the basename is still
cvsroot?

> diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
> index b59be9a..69934b2 100755
> --- a/t/t9200-git-cvsexportcommit.sh
> +++ b/t/t9200-git-cvsexportcommit.sh
> @@ -19,7 +19,7 @@ then
>      test_done
>  fi
>  
> -CVSROOT=$PWD/cvsroot
> +CVSROOT=$PWD/tmpcvsroot

Ah, but here you do something different, which makes sense. Should I
tweak the commit message?

-Peff

^ permalink raw reply

* Re: merge --no-commit not able to report stats more verbosely?
From: Jeff King @ 2012-10-28 11:21 UTC (permalink / raw)
  To: Scott R. Godin; +Cc: git
In-Reply-To: <k63iai$kbp$1@ger.gmane.org>

On Mon, Oct 22, 2012 at 09:39:31AM -0400, Scott R. Godin wrote:

> As you can see from the below, I can't seem to get it to give me more
> verbose results of what's being merged (as in the actual merge below)
> with --stat or -v .. is it supposed to do that?

Yes. The diffstat is shown for the completed merge, but here:

> (develop)>$ git merge --no-commit --stat -v widget_twitter
> Automatic merge went well; stopped before committing as requested

You do not complete the merge.

> (develop|MERGING)>$ git merge --abort
> 
> (develop)>$ git merge widget_twitter
> Merge made by the 'recursive' strategy.
>  .../code/community/Dnd/Magentweet/Model/User.php   |    3 ++-
>  1 files changed, 2 insertions(+), 1 deletions(-)

Whereas here you do, and you get a diffstat.

When you are in the middle of an uncompleted merge and want to know what
is happening, you should look at the index using "git status" (to get an
overview of what is ready to be committed and what is unmerged), "git
diff --cached" (to see what was automatically merged and is ready for
commit), and "git diff" (to see conflicted entries that still need to be
resolved).

-Peff

^ permalink raw reply

* Re: crash on git diff-tree -Ganything <tree> for new files with textconv filter
From: Jeff King @ 2012-10-28 12:01 UTC (permalink / raw)
  To: Peter Oberndorfer; +Cc: git, Junio C Hamano
In-Reply-To: <508C29E4.5000801@arcor.de>

On Sat, Oct 27, 2012 at 08:37:24PM +0200, Peter Oberndorfer wrote:

> It seems "git diff-tree -Ganything <tree>" crashes[1] with a null
> pointer dereference
> when run on a commit that adds a file (pdf) with a textconv filter.
> 
> It can be reproduced with vanilla git by having a commit on top that
> adds a file with a textconv filter and executing git diff-tree
> -Ganything HEAD
> But running git log -Ganything still works without a crash.
> This problem seems to exist since the feature was first added in f506b8e8b5.

Thanks for a thorough bug report. I didn't reproduce the crash, but I
can see how it happens (it happens with diff-tree because we will reuse
the working tree file in that instance; it could also happen if you
turned on textconv caching).

> While testing I also noticed the -S and -G act on the original file
> instead of the textconv munged data.
> Is this intentional or caused by accessing the wrong data?

Both, perhaps. :)

-G operates on the munged data; you can see it feed the munged data to
xdiff in diff_grep. But the optimization for handling added and removed
files accidentally fed the wrong pointer. Fixing that is a no-brainer,
since the optimization is inconsistent with the regular code path.

-S, however, predates the invention of textconv and has never used it.
It is a little less clear that textconv is the right thing here, because
it is not about grepping the diff, but about counting occurrences of the
string inside the file content. I tend to think that doing so on the
textconv'd data would be what people generally want, but it is a
behavior change.

> Wild guess: should we really access p->one->data and not mf1.ptr?

Precisely. Thanks for your wild guess; it made finding the bug very
easy. :)

> Is there some more information i should provide?

The patch below should fix it. I added tests, but please try your
real-world test case on it to double-check.

-- >8 --
Subject: [PATCH] diff_grep: use textconv buffers for add/deleted files

If you use "-G" to grep a diff, we will apply a configured
textconv filter to the data before generating the diff.
However, if the diff is an addition or deletion, we do not
bother running the diff at all, and just look for the token
in the added (or removed) content. This works because we
know that the diff must contain every line of content.

However, while we used the textconv-derived buffers in the
regular diff, we accidentally passed the original unmodified
buffers to regexec when checking the added or removed
content. This could lead to an incorrect answer.

Worse, in some cases we might have a textconv buffer but no
original buffer (e.g., if we pulled the textconv data from
cache, or if we reused a working tree file when generating
it). In that case, we could actually feed NULL to regexec
and segfault.

Reported-by: Peter Oberndorfer <kumbayo84@arcor.de>
Signed-off-by: Jeff King <peff@peff.net>
---
 diffcore-pickaxe.c       |  4 ++--
 t/t4030-diff-textconv.sh | 12 ++++++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index ed23eb4..a209376 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -104,10 +104,10 @@ static int diff_grep(struct diff_filepair *p, struct diff_options *o,
 		if (!mf2.ptr)
 			return 0; /* ignore unmerged */
 		/* created "two" -- does it have what we are looking for? */
-		hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
+		hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);
 	} else if (!mf2.ptr) {
 		/* removed "one" -- did it have what we are looking for? */
-		hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
+		hit = !regexec(regexp, mf1.ptr, 1, &regmatch, 0);
 	} else {
 		/*
 		 * We have both sides; need to run textual diff and see if
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index eebb1ee..461d27a 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -84,6 +84,18 @@ test_expect_success 'status -v produces text' '
 	git reset --soft HEAD@{1}
 '
 
+test_expect_success 'grep-diff (-G) operates on textconv data (add)' '
+	echo one >expect &&
+	git log --root --format=%s -G0 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'grep-diff (-G) operates on textconv data (modification)' '
+	echo two >expect &&
+	git log --root --format=%s -G1 >actual &&
+	test_cmp expect actual
+'
+
 cat >expect.stat <<'EOF'
  file | Bin 2 -> 4 bytes
  1 file changed, 0 insertions(+), 0 deletions(-)
-- 
1.8.0.3.g3456896

^ permalink raw reply related

* [PATCH 0/2] textconv support for "log -S"
From: Jeff King @ 2012-10-28 12:45 UTC (permalink / raw)
  To: Peter Oberndorfer; +Cc: git, Junio C Hamano
In-Reply-To: <20121028120104.GE11434@sigill.intra.peff.net>

On Sun, Oct 28, 2012 at 08:01:04AM -0400, Jeff King wrote:

> -G operates on the munged data; you can see it feed the munged data to
> xdiff in diff_grep. But the optimization for handling added and removed
> files accidentally fed the wrong pointer. Fixing that is a no-brainer,
> since the optimization is inconsistent with the regular code path.
> 
> -S, however, predates the invention of textconv and has never used it.
> It is a little less clear that textconv is the right thing here, because
> it is not about grepping the diff, but about counting occurrences of the
> string inside the file content. I tend to think that doing so on the
> textconv'd data would be what people generally want, but it is a
> behavior change.

I prepared the earlier bugfix for "-G" for maint. Modifying "-S" would
be a separate feature topic, and would look like this (I built it on top
of the bugfix patch, since the tests are a follow-on).

  [1/2]: pickaxe: hoist empty needle check
  [2/2]: pickaxe: use textconv for -S counting

-Peff

^ permalink raw reply

* [PATCH 1/2] pickaxe: hoist empty needle check
From: Jeff King @ 2012-10-28 12:46 UTC (permalink / raw)
  To: Peter Oberndorfer; +Cc: git, Junio C Hamano
In-Reply-To: <20121028124540.GF11434@sigill.intra.peff.net>

If we are given an empty pickaxe needle like "git log -S ''",
it is impossible for us to find anything (because no matter
what the content, the count will always be 0). We currently
check this at the lowest level of contains(). Let's hoist
the logic much earlier to has_changes(), so that it is
simpler to return our answer before loading any blob data.

Signed-off-by: Jeff King <peff@peff.net>
---
 diffcore-pickaxe.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index a209376..61f628c 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -163,8 +163,6 @@ static unsigned int contains(struct diff_filespec *one, struct diff_options *o,
 	unsigned int cnt;
 	unsigned long sz;
 	const char *data;
-	if (!o->pickaxe[0])
-		return 0;
 	if (diff_populate_filespec(one, 0))
 		return 0;
 
@@ -206,6 +204,9 @@ static int has_changes(struct diff_filepair *p, struct diff_options *o,
 static int has_changes(struct diff_filepair *p, struct diff_options *o,
 		       regex_t *regexp, kwset_t kws)
 {
+	if (!o->pickaxe[0])
+		return 0;
+
 	if (!DIFF_FILE_VALID(p->one)) {
 		if (!DIFF_FILE_VALID(p->two))
 			return 0; /* ignore unmerged */
-- 
1.8.0.3.g3456896

^ permalink raw reply related

* [PATCH 2/2] pickaxe: use textconv for -S counting
From: Jeff King @ 2012-10-28 12:47 UTC (permalink / raw)
  To: Peter Oberndorfer; +Cc: git, Junio C Hamano
In-Reply-To: <20121028124540.GF11434@sigill.intra.peff.net>

We currently just look at raw blob data when using "-S" to
pickaxe. This is mostly historical, as pickaxe predates the
textconv feature. If the user has bothered to define a
textconv filter, it is more likely that their search string will be
on the textconv output, as that is what they will see in the
diff (and we do not even provide a mechanism for them to
search for binary needles that contain NUL characters).

This patch teaches "-S" to use textconv, just as we
already do for "-G".

Signed-off-by: Jeff King <peff@peff.net>
---
 diffcore-pickaxe.c       | 56 +++++++++++++++++++++++++++++++++---------------
 t/t4030-diff-textconv.sh | 12 +++++++++++
 2 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 61f628c..b097fa7 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -157,17 +157,15 @@ static unsigned int contains(struct diff_filespec *one, struct diff_options *o,
 	return;
 }
 
-static unsigned int contains(struct diff_filespec *one, struct diff_options *o,
+static unsigned int contains(mmfile_t *mf, struct diff_options *o,
 			     regex_t *regexp, kwset_t kws)
 {
 	unsigned int cnt;
 	unsigned long sz;
 	const char *data;
-	if (diff_populate_filespec(one, 0))
-		return 0;
 
-	sz = one->size;
-	data = one->data;
+	sz = mf->size;
+	data = mf->ptr;
 	cnt = 0;
 
 	if (regexp) {
@@ -197,29 +195,53 @@ static int has_changes(struct diff_filepair *p, struct diff_options *o,
 			cnt++;
 		}
 	}
-	diff_free_filespec_data(one);
 	return cnt;
 }
 
 static int has_changes(struct diff_filepair *p, struct diff_options *o,
 		       regex_t *regexp, kwset_t kws)
 {
+	struct userdiff_driver *textconv_one = get_textconv(p->one);
+	struct userdiff_driver *textconv_two = get_textconv(p->two);
+	mmfile_t mf1, mf2;
+	int ret;
+
 	if (!o->pickaxe[0])
 		return 0;
 
-	if (!DIFF_FILE_VALID(p->one)) {
-		if (!DIFF_FILE_VALID(p->two))
-			return 0; /* ignore unmerged */
+	/*
+	 * If we have an unmodified pair, we know that the count will be the
+	 * same and don't even have to load the blobs. Unless textconv is in
+	 * play, _and_ we are using two different textconv filters (e.g.,
+	 * because a pair is an exact rename with different textconv attributes
+	 * for each side, which might generate different content).
+	 */
+	if (textconv_one == textconv_two && diff_unmodified_pair(p))
+		return 0;
+
+	fill_one(p->one, &mf1, &textconv_one);
+	fill_one(p->two, &mf2, &textconv_two);
+
+	if (!mf1.ptr) {
+		if (!mf2.ptr)
+			ret = 0; /* ignore unmerged */
 		/* created */
-		return contains(p->two, o, regexp, kws) != 0;
-	}
-	if (!DIFF_FILE_VALID(p->two))
-		return contains(p->one, o, regexp, kws) != 0;
-	if (!diff_unmodified_pair(p)) {
-		return contains(p->one, o, regexp, kws) !=
-		       contains(p->two, o, regexp, kws);
+		ret = contains(&mf2, o, regexp, kws) != 0;
 	}
-	return 0;
+	else if (!mf2.ptr) /* removed */
+		ret = contains(&mf1, o, regexp, kws) != 0;
+	else
+		ret = contains(&mf1, o, regexp, kws) !=
+		      contains(&mf2, o, regexp, kws);
+
+	if (textconv_one)
+		free(mf1.ptr);
+	if (textconv_two)
+		free(mf2.ptr);
+	diff_free_filespec_data(p->one);
+	diff_free_filespec_data(p->two);
+
+	return ret;
 }
 
 static void diffcore_pickaxe_count(struct diff_options *o)
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
index 461d27a..53ec330 100755
--- a/t/t4030-diff-textconv.sh
+++ b/t/t4030-diff-textconv.sh
@@ -96,6 +96,18 @@ test_expect_success 'grep-diff (-G) operates on textconv data (modification)' '
 	test_cmp expect actual
 '
 
+test_expect_success 'pickaxe (-S) operates on textconv data (add)' '
+	echo one >expect &&
+	git log --root --format=%s -S0 >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'pickaxe (-S) operates on textconv data (modification)' '
+	echo two >expect &&
+	git log --root --format=%s -S1 >actual &&
+	test_cmp expect actual
+'
+
 cat >expect.stat <<'EOF'
  file | Bin 2 -> 4 bytes
  1 file changed, 0 insertions(+), 0 deletions(-)
-- 
1.8.0.3.g3456896

^ permalink raw reply related

* Re: [PATCH] mailmap: avoid out-of-bounds memory access
From: Romain Francoise @ 2012-10-28 13:21 UTC (permalink / raw)
  To: Jeff King; +Cc: git, gitster
In-Reply-To: <20121028110207.GA11434@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> So you could also check for "left == nstart" before the loop even
> begins. I think your fix (to just make the loop more robust to that
> precondition) is better, though, as the rest of the code does the right
> thing with such a value of nend.

Yep.

> It looks like t4203 triggers this problem. Curious that valgrind does
> not find it. I guess since it does not have compiler support, it cannot
> find out-of-bound errors on stack buffers. Does the rest of the test
> suite turn up clean with AddressSanitizer?

I tested your 'master' and your 'pu' with expensive tests enabled and both
are clean after fixing t4203.

Thanks!

^ permalink raw reply

* Re: Mistake in git-reset documentation
From: Andreas Schwab @ 2012-10-28 13:39 UTC (permalink / raw)
  To: Jeff King; +Cc: Krzysztof Mazur, Bojan Petrović, git
In-Reply-To: <20121028110715.GB11434@sigill.intra.peff.net>

Jeff King <peff@peff.net> writes:

> On Sun, Oct 28, 2012 at 09:36:10AM +0100, Krzysztof Mazur wrote:
>
>>  DESCRIPTION
>>  -----------
>> @@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
>>  you can use it to selectively reset hunks. See the ``Interactive Mode''
>>  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
>>  
>> -'git reset' --<mode> [<commit>]::
>> +'git reset' [--<mode>] [<commit>]::
>>  	This form resets the current branch head to <commit> and
>>  	possibly updates the index (resetting it to the tree of <commit>) and
>>  	the working tree depending on <mode>, which
>
> Should we say something like "if --<mode> is omitted, defaults to
> "--mixed"?

Under --mixed it already says "This is the default action", though.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply

* Re: Mistake in git-reset documentation
From: Jeff King @ 2012-10-28 13:46 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: Krzysztof Mazur, Bojan Petrović, git
In-Reply-To: <m2zk36hg0q.fsf@igel.home>

On Sun, Oct 28, 2012 at 02:39:49PM +0100, Andreas Schwab wrote:

> Jeff King <peff@peff.net> writes:
> 
> > On Sun, Oct 28, 2012 at 09:36:10AM +0100, Krzysztof Mazur wrote:
> >
> >>  DESCRIPTION
> >>  -----------
> >> @@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
> >>  you can use it to selectively reset hunks. See the ``Interactive Mode''
> >>  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
> >>  
> >> -'git reset' --<mode> [<commit>]::
> >> +'git reset' [--<mode>] [<commit>]::
> >>  	This form resets the current branch head to <commit> and
> >>  	possibly updates the index (resetting it to the tree of <commit>) and
> >>  	the working tree depending on <mode>, which
> >
> > Should we say something like "if --<mode> is omitted, defaults to
> > "--mixed"?
> 
> Under --mixed it already says "This is the default action", though.

I know, but that is somewhat buried for somebody who is seeing that the
"--<mode>" bit is optional and wondering what it means to omit it.

-Peff

^ permalink raw reply

* Re: Mistake in git-reset documentation
From: Krzysztof Mazur @ 2012-10-28 14:13 UTC (permalink / raw)
  To: Jeff King; +Cc: Andreas Schwab, Bojan Petrović, git
In-Reply-To: <20121028134635.GA25519@sigill.intra.peff.net>

On Sun, Oct 28, 2012 at 09:46:35AM -0400, Jeff King wrote:
> On Sun, Oct 28, 2012 at 02:39:49PM +0100, Andreas Schwab wrote:
> 
> > Jeff King <peff@peff.net> writes:
> > 
> > > On Sun, Oct 28, 2012 at 09:36:10AM +0100, Krzysztof Mazur wrote:
> > >
> > >>  DESCRIPTION
> > >>  -----------
> > >> @@ -43,7 +43,7 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
> > >>  you can use it to selectively reset hunks. See the ``Interactive Mode''
> > >>  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
> > >>  
> > >> -'git reset' --<mode> [<commit>]::
> > >> +'git reset' [--<mode>] [<commit>]::
> > >>  	This form resets the current branch head to <commit> and
> > >>  	possibly updates the index (resetting it to the tree of <commit>) and
> > >>  	the working tree depending on <mode>, which
> > >
> > > Should we say something like "if --<mode> is omitted, defaults to
> > > "--mixed"?
> > 
> > Under --mixed it already says "This is the default action", though.
> 
> I know, but that is somewhat buried for somebody who is seeing that the
> "--<mode>" bit is optional and wondering what it means to omit it.
> 

The --mixed mode is also described as second mode, and saying that --mixed
is default earlier may save some time wasted on reading --soft
description.

There is also small inconsequence in what <mode> is, just "mixed" or
"--mixed".

Krzysiek

-- >8 --
Subject: [PATCH] doc: git-reset: make "<mode>" optional

The git-reset's "<mode>" is an optional argument, however it was
documented as required.

The "<mode>" is documented as one of: --soft, --mixed, --hard, --merge
or --keep, so "<mode>" should be used instead of "--<mode>".

Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net>
---
 Documentation/git-reset.txt | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 117e374..978d8da 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git reset' [-q] [<commit>] [--] <paths>...
 'git reset' (--patch | -p) [<commit>] [--] [<paths>...]
-'git reset' (--soft | --mixed | --hard | --merge | --keep) [-q] [<commit>]
+'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
@@ -43,11 +43,11 @@ This means that `git reset -p` is the opposite of `git add -p`, i.e.
 you can use it to selectively reset hunks. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
-'git reset' --<mode> [<commit>]::
+'git reset' [<mode>] [<commit>]::
 	This form resets the current branch head to <commit> and
 	possibly updates the index (resetting it to the tree of <commit>) and
-	the working tree depending on <mode>, which
-	must be one of the following:
+	the working tree depending on <mode>. If <mode> is omitted,
+	defaults to "--mixed". The <mode> must be one of the following:
 +
 --
 --soft::
-- 
1.8.0.47.g5b520ba

^ permalink raw reply related

* Re: [PATCH] Fix git p4 sync errors
From: Pete Wyckoff @ 2012-10-28 15:06 UTC (permalink / raw)
  To: Matt Arsenault; +Cc: Junio C Hamano, git, Luke Diamand
In-Reply-To: <F0F92B0A-D37F-40D4-A0DF-43EEDA2818B9@gmail.com>

arsenm2@gmail.com wrote on Wed, 24 Oct 2012 19:41 -0700:
> 
> On Oct 21, 2012, at 12:06 , Junio C Hamano <gitster@pobox.com> wrote:
> > 
> >> 
> >> This solves errors in some cases when syncing renamed files.
> > 
> > Can you be a bit more descriptive?  What are "errors in some case"?
> > 
> It might just be when files are renamed. I ran into this after months of using it, and I'm skeptical that in that time no files were ever renamed. I'm not sure what was special about the file that was renamed. (There also might have been deleted files in the same commit, not sure if that matters)

I set up a test case where I did a "p4 move" on a file and tried
syncing it, with and without "-s" to describe.  It works in both
cases, for an old (2009.2) and new (2012.1) version of p4.

The output of -s versus no -s does differ, and the differences
are different with server version worse yet.  But in no case is
there ever a set of file differences.  -G does seem to disable
that.

I'd love to track this down, but can't seem to provoke anything
on my own.  Let me know if you have any hints based on what is in
your depot or server/client config.  Or if you see it again.

> > In short, what I am getting at are:
> > 
> > - What breaks by not passing "-s"?  What are the user visible
> >   symptoms?
> 
> There's a key error on the line
> line 2198:        epoch = details["time"]
> The details object is an error different fields set (I don't remember what it is exactly, I'm not at work right now)

This would happen if describe did not return a "time" field, but
there's an explicit check for that:

        res = p4CmdList("describe -s %d" % newestRevision)
        newestTime = None   
        for r in res:       
            if r.has_key('time'):
                newestTime = int(r['time'])
        if newestTime is None:
            die("Output from \"describe -s\" on newest change %d did not give a time" %                     
                newestRevision) 
        details["time"] = newestTime

so I'm confused how this could happen.  Maybe your version is
older/different than what is in the git source?


I'm not against putting in your patch, since it is true we don't
want the file diff, and adding "-s" should be harmless in theory.
And it doesn't cause any existing tests to fail.  It just scares
me that there's something else going on we haven't figured out.

		-- Pete

^ permalink raw reply

* Re: [PATCH] Fix t9200 on case insensitive file systems
From: Torsten Bögershausen @ 2012-10-28 15:28 UTC (permalink / raw)
  To: Jeff King
  Cc: Torsten Bögershausen, git, bdwalton, bosch, brian,
	robin.rosenberg
In-Reply-To: <20121028111053.GC11434@sigill.intra.peff.net>

On 28.10.12 12:10, Jeff King wrote:
> On Fri, Oct 26, 2012 at 06:18:24PM +0200, Torsten Bögershausen wrote:
> 
>> t9200 defines $CVSROOT where cvs should init its repository
>> $CVSROOT is set to $PWD/cvsroot.
>> cvs init is supposed to create the repository inside $PWD/cvsroot/CVSROOT
>>
>> "cvs init" (e.g. version  1.11.23) checks if the last element of the path is
>> "CVSROOT", and if a directory with e.g. $PWD/cvsroot/CVSROOT already exists.
>>
>> For such a $CVSROOT cvs refuses to init a repository here:
>> "Cannot initialize repository under existing CVSROOT:
>>
>> On a case insenstive file system cvsroot and CVSROOT are the same directories
>> and t9200 fails.
>>
>> Solution: use $PWD/tmp/cvsroot instead of cvsroot $PWD/cvsroot
> 
> Wouldn't tmp/cvsroot have the same problem, since the basename is still
> cvsroot?
> 
>> diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
>> index b59be9a..69934b2 100755
>> --- a/t/t9200-git-cvsexportcommit.sh
>> +++ b/t/t9200-git-cvsexportcommit.sh
>> @@ -19,7 +19,7 @@ then
>>      test_done
>>  fi
>>  
>> -CVSROOT=$PWD/cvsroot
>> +CVSROOT=$PWD/tmpcvsroot
> 
> Ah, but here you do something different, which makes sense. Should I
> tweak the commit message?
> 

Yes, please do so.

Thanks for spotting,
/torsten

^ permalink raw reply

* [PATCH v4 0/8] Fix GIT_CEILING_DIRECTORIES that contain symlinks
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty

v4 of the series; see the cover letter for v3 [1] for more editorial
comments.

Changes since v3:

* For "test-path-utils longest_ancestor_length", normalize all of the
  paths using normalize_path_copy() to counteract the path mangling
  carried out by bash on Windows.  (Thanks to Johannes Sixt for his
  helpful advice.)

* Rebased onto a more recent master.

[1] http://thread.gmane.org/gmane.comp.version-control.git/208102

Michael Haggerty (8):
  Introduce new static function real_path_internal()
  real_path_internal(): add comment explaining use of cwd
  Introduce new function real_path_if_valid()
  longest_ancestor_length(): use string_list_split()
  longest_ancestor_length(): take a string_list argument for prefixes
  longest_ancestor_length(): require prefix list entries to be
    normalized
  setup_git_directory_gently_1(): resolve symlinks in ceiling paths
  string_list_longest_prefix(): remove function

 Documentation/technical/api-string-list.txt |   8 ---
 abspath.c                                   | 105 ++++++++++++++++++++++------
 cache.h                                     |   3 +-
 path.c                                      |  46 ++++++------
 setup.c                                     |  34 ++++++++-
 string-list.c                               |  20 ------
 string-list.h                               |   8 ---
 t/t0060-path-utils.sh                       |  41 ++++-------
 t/t0063-string-list.sh                      |  30 --------
 test-path-utils.c                           |  51 +++++++++++++-
 test-string-list.c                          |  20 ------
 11 files changed, 202 insertions(+), 164 deletions(-)

-- 
1.8.0

^ permalink raw reply

* [PATCH v4 1/8] Introduce new static function real_path_internal()
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>

It accepts a new parameter, die_on_error.  If die_on_error is false,
it simply cleans up after itself and returns NULL rather than dying.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 abspath.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 72 insertions(+), 21 deletions(-)

diff --git a/abspath.c b/abspath.c
index 05f2d79..a7ab8e9 100644
--- a/abspath.c
+++ b/abspath.c
@@ -15,15 +15,26 @@ int is_directory(const char *path)
 #define MAXDEPTH 5
 
 /*
- * Use this to get the real path, i.e. resolve links. If you want an
- * absolute path but don't mind links, use absolute_path.
+ * Return the real path (i.e., absolute path, with symlinks resolved
+ * and extra slashes removed) equivalent to the specified path.  (If
+ * you want an absolute path but don't mind links, use
+ * absolute_path().)  The return value is a pointer to a static
+ * buffer.
+ *
+ * The input and all intermediate paths must be shorter than MAX_PATH.
+ * The directory part of path (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.  If die_on_error is set, then die with an
+ * informative error message if there is a problem.  Otherwise, return
+ * NULL on errors (without generating any output).
  *
  * If path is our buffer, then return path, as it's already what the
  * user wants.
  */
-const char *real_path(const char *path)
+static const char *real_path_internal(const char *path, int die_on_error)
 {
 	static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+	char *retval = NULL;
 	char cwd[1024] = "";
 	int buf_index = 1;
 
@@ -35,11 +46,19 @@ const char *real_path(const char *path)
 	if (path == buf || path == next_buf)
 		return path;
 
-	if (!*path)
-		die("The empty string is not a valid path");
+	if (!*path) {
+		if (die_on_error)
+			die("The empty string is not a valid path");
+		else
+			goto error_out;
+	}
 
-	if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
-		die ("Too long path: %.*s", 60, path);
+	if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) {
+		if (die_on_error)
+			die("Too long path: %.*s", 60, path);
+		else
+			goto error_out;
+	}
 
 	while (depth--) {
 		if (!is_directory(buf)) {
@@ -54,20 +73,36 @@ const char *real_path(const char *path)
 		}
 
 		if (*buf) {
-			if (!*cwd && !getcwd(cwd, sizeof(cwd)))
-				die_errno ("Could not get current working directory");
+			if (!*cwd && !getcwd(cwd, sizeof(cwd))) {
+				if (die_on_error)
+					die_errno("Could not get current working directory");
+				else
+					goto error_out;
+			}
 
-			if (chdir(buf))
-				die_errno ("Could not switch to '%s'", buf);
+			if (chdir(buf)) {
+				if (die_on_error)
+					die_errno("Could not switch to '%s'", buf);
+				else
+					goto error_out;
+			}
+		}
+		if (!getcwd(buf, PATH_MAX)) {
+			if (die_on_error)
+				die_errno("Could not get current working directory");
+			else
+				goto error_out;
 		}
-		if (!getcwd(buf, PATH_MAX))
-			die_errno ("Could not get current working directory");
 
 		if (last_elem) {
 			size_t len = strlen(buf);
-			if (len + strlen(last_elem) + 2 > PATH_MAX)
-				die ("Too long path name: '%s/%s'",
-						buf, last_elem);
+			if (len + strlen(last_elem) + 2 > PATH_MAX) {
+				if (die_on_error)
+					die("Too long path name: '%s/%s'",
+					    buf, last_elem);
+				else
+					goto error_out;
+			}
 			if (len && !is_dir_sep(buf[len-1]))
 				buf[len++] = '/';
 			strcpy(buf + len, last_elem);
@@ -77,10 +112,18 @@ const char *real_path(const char *path)
 
 		if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
 			ssize_t len = readlink(buf, next_buf, PATH_MAX);
-			if (len < 0)
-				die_errno ("Invalid symlink '%s'", buf);
-			if (PATH_MAX <= len)
-				die("symbolic link too long: %s", buf);
+			if (len < 0) {
+				if (die_on_error)
+					die_errno("Invalid symlink '%s'", buf);
+				else
+					goto error_out;
+			}
+			if (PATH_MAX <= len) {
+				if (die_on_error)
+					die("symbolic link too long: %s", buf);
+				else
+					goto error_out;
+			}
 			next_buf[len] = '\0';
 			buf = next_buf;
 			buf_index = 1 - buf_index;
@@ -89,10 +132,18 @@ const char *real_path(const char *path)
 			break;
 	}
 
+	retval = buf;
+error_out:
+	free(last_elem);
 	if (*cwd && chdir(cwd))
 		die_errno ("Could not change back to '%s'", cwd);
 
-	return buf;
+	return retval;
+}
+
+const char *real_path(const char *path)
+{
+	return real_path_internal(path, 1);
 }
 
 static const char *get_pwd_cwd(void)
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 2/8] real_path_internal(): add comment explaining use of cwd
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>


Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 abspath.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/abspath.c b/abspath.c
index a7ab8e9..f8a526f 100644
--- a/abspath.c
+++ b/abspath.c
@@ -35,7 +35,14 @@ static const char *real_path_internal(const char *path, int die_on_error)
 {
 	static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
 	char *retval = NULL;
+
+	/*
+	 * If we have to temporarily chdir(), store the original CWD
+	 * here so that we can chdir() back to it at the end of the
+	 * function:
+	 */
 	char cwd[1024] = "";
+
 	int buf_index = 1;
 
 	int depth = MAXDEPTH;
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 3/8] Introduce new function real_path_if_valid()
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>

The function is like real_path(), except that it returns NULL on error
instead of dying.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 abspath.c | 5 +++++
 cache.h   | 1 +
 2 files changed, 6 insertions(+)

diff --git a/abspath.c b/abspath.c
index f8a526f..40cdc46 100644
--- a/abspath.c
+++ b/abspath.c
@@ -153,6 +153,11 @@ const char *real_path(const char *path)
 	return real_path_internal(path, 1);
 }
 
+const char *real_path_if_valid(const char *path)
+{
+	return real_path_internal(path, 0);
+}
+
 static const char *get_pwd_cwd(void)
 {
 	static char cwd[PATH_MAX + 1];
diff --git a/cache.h b/cache.h
index a58df84..b0d75bc 100644
--- a/cache.h
+++ b/cache.h
@@ -714,6 +714,7 @@ static inline int is_absolute_path(const char *path)
 }
 int is_directory(const char *);
 const char *real_path(const char *path);
+const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
 const char *relative_path(const char *abs, const char *base);
 int normalize_path_copy(char *dst, const char *src);
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 4/8] longest_ancestor_length(): use string_list_split()
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>


Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 path.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/path.c b/path.c
index cbbdf7d..f455e8e 100644
--- a/path.c
+++ b/path.c
@@ -12,6 +12,7 @@
  */
 #include "cache.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 static char bad_path[] = "/bad-path/";
 
@@ -582,20 +583,22 @@ int normalize_path_copy(char *dst, const char *src)
  */
 int longest_ancestor_length(const char *path, const char *prefix_list)
 {
+	struct string_list prefixes = STRING_LIST_INIT_DUP;
 	char buf[PATH_MAX+1];
-	const char *ceil, *colon;
-	int len, max_len = -1;
+	int i, max_len = -1;
 
 	if (prefix_list == NULL || !strcmp(path, "/"))
 		return -1;
 
-	for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
-		for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
-		len = colon - ceil;
+	string_list_split(&prefixes, prefix_list, PATH_SEP, -1);
+
+	for (i = 0; i < prefixes.nr; i++) {
+		const char *ceil = prefixes.items[i].string;
+		int len = strlen(ceil);
+
 		if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
 			continue;
-		strlcpy(buf, ceil, len+1);
-		if (normalize_path_copy(buf, buf) < 0)
+		if (normalize_path_copy(buf, ceil) < 0)
 			continue;
 		len = strlen(buf);
 		if (len > 0 && buf[len-1] == '/')
@@ -608,6 +611,7 @@ int longest_ancestor_length(const char *path, const char *prefix_list)
 		}
 	}
 
+	string_list_clear(&prefixes, 0);
 	return max_len;
 }
 
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 5/8] longest_ancestor_length(): take a string_list argument for prefixes
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>

Change longest_ancestor_length() to take the prefixes argument as a
string_list rather than as a colon-separated string.  This will make
it easier for the caller to alter the entries before calling
longest_ancestor_length().

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 cache.h           |  2 +-
 path.c            | 22 +++++++++-------------
 setup.c           | 11 +++++++++--
 test-path-utils.c |  8 +++++++-
 4 files changed, 26 insertions(+), 17 deletions(-)

diff --git a/cache.h b/cache.h
index b0d75bc..8103385 100644
--- a/cache.h
+++ b/cache.h
@@ -718,7 +718,7 @@ const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
 const char *relative_path(const char *abs, const char *base);
 int normalize_path_copy(char *dst, const char *src);
-int longest_ancestor_length(const char *path, const char *prefix_list);
+int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
 int daemon_avoid_alias(const char *path);
 int offset_1st_component(const char *path);
diff --git a/path.c b/path.c
index f455e8e..b80d2e6 100644
--- a/path.c
+++ b/path.c
@@ -570,30 +570,27 @@ int normalize_path_copy(char *dst, const char *src)
 
 /*
  * path = Canonical absolute path
- * prefix_list = Colon-separated list of absolute paths
+ * prefixes = string_list containing absolute paths
  *
- * Determines, for each path in prefix_list, whether the "prefix" really
+ * Determines, for each path in prefixes, whether the "prefix" really
  * is an ancestor directory of path.  Returns the length of the longest
  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
- * is an ancestor.  (Note that this means 0 is returned if prefix_list is
- * "/".) "/foo" is not considered an ancestor of "/foobar".  Directories
+ * is an ancestor.  (Note that this means 0 is returned if prefixes is
+ * ["/"].) "/foo" is not considered an ancestor of "/foobar".  Directories
  * are not considered to be their own ancestors.  path must be in a
  * canonical form: empty components, or "." or ".." components are not
- * allowed.  prefix_list may be null, which is like "".
+ * allowed.  Empty strings in prefixes are ignored.
  */
-int longest_ancestor_length(const char *path, const char *prefix_list)
+int longest_ancestor_length(const char *path, struct string_list *prefixes)
 {
-	struct string_list prefixes = STRING_LIST_INIT_DUP;
 	char buf[PATH_MAX+1];
 	int i, max_len = -1;
 
-	if (prefix_list == NULL || !strcmp(path, "/"))
+	if (!strcmp(path, "/"))
 		return -1;
 
-	string_list_split(&prefixes, prefix_list, PATH_SEP, -1);
-
-	for (i = 0; i < prefixes.nr; i++) {
-		const char *ceil = prefixes.items[i].string;
+	for (i = 0; i < prefixes->nr; i++) {
+		const char *ceil = prefixes->items[i].string;
 		int len = strlen(ceil);
 
 		if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
@@ -611,7 +608,6 @@ int longest_ancestor_length(const char *path, const char *prefix_list)
 		}
 	}
 
-	string_list_clear(&prefixes, 0);
 	return max_len;
 }
 
diff --git a/setup.c b/setup.c
index 3a1b2fd..b4cd356 100644
--- a/setup.c
+++ b/setup.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "dir.h"
+#include "string-list.h"
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
@@ -627,10 +628,11 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
 static const char *setup_git_directory_gently_1(int *nongit_ok)
 {
 	const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
+	struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
 	static char cwd[PATH_MAX+1];
 	const char *gitdirenv, *ret;
 	char *gitfile;
-	int len, offset, offset_parent, ceil_offset;
+	int len, offset, offset_parent, ceil_offset = -1;
 	dev_t current_device = 0;
 	int one_filesystem = 1;
 
@@ -655,7 +657,12 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 	if (gitdirenv)
 		return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
 
-	ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
+	if (env_ceiling_dirs) {
+		string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
+		ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
+		string_list_clear(&ceiling_dirs, 0);
+	}
+
 	if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
 		ceil_offset = 1;
 
diff --git a/test-path-utils.c b/test-path-utils.c
index 3bc20e9..acb0560 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "string-list.h"
 
 int main(int argc, char **argv)
 {
@@ -30,7 +31,12 @@ int main(int argc, char **argv)
 	}
 
 	if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
-		int len = longest_ancestor_length(argv[2], argv[3]);
+		int len;
+		struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
+
+		string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
+		len = longest_ancestor_length(argv[2], &ceiling_dirs);
+		string_list_clear(&ceiling_dirs, 0);
 		printf("%d\n", len);
 		return 0;
 	}
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 6/8] longest_ancestor_length(): require prefix list entries to be normalized
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>

Move the responsibility for normalizing prefixes from
longest_ancestor_length() to its callers. Use slightly different
normalizations at the two callers:

In setup_git_directory_gently_1(), use the old normalization, which
ignores paths that are not usable.  In the next commit we will change
this caller to also resolve symlinks in the paths from
GIT_CEILING_DIRECTORIES as part of the normalization.

In "test-path-utils longest_ancestor_length", use the old
normalization, but die() if any paths are unusable.  Also change t0060
to only pass normalized paths to the test program (no empty entries or
non-absolute paths, strip trailing slashes from the paths, and remove
tests that thereby become redundant).

The point of this change is to reduce the scope of the ancestor_length
tests in t0060 from testing normalization+longest_prefix to testing
only mostly longest_prefix.  This is necessary because when
setup_git_directory_gently_1() starts resolving symlinks as part of
its normalization, it will not be reasonable to do the same in the
test suite, because that would make the test results depend on the
contents of the root directory of the filesystem on which the test is
run.  HOWEVER: under Windows, bash mangles arguments that look like
absolute POSIX paths into DOS paths.  So we have to retain the level
of normalization done by normalize_path_copy() to convert the
bash-mangled DOS paths (which contain backslashes) into paths that use
forward slashes.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 path.c                | 26 +++++++++++---------------
 setup.c               | 23 +++++++++++++++++++++++
 t/t0060-path-utils.sh | 41 +++++++++++++----------------------------
 test-path-utils.c     | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 91 insertions(+), 44 deletions(-)

diff --git a/path.c b/path.c
index b80d2e6..d3d3f8b 100644
--- a/path.c
+++ b/path.c
@@ -570,20 +570,20 @@ int normalize_path_copy(char *dst, const char *src)
 
 /*
  * path = Canonical absolute path
- * prefixes = string_list containing absolute paths
+ * prefixes = string_list containing normalized, absolute paths without
+ * trailing slashes (except for the root directory, which is denoted by "/").
  *
- * Determines, for each path in prefixes, whether the "prefix" really
+ * Determines, for each path in prefixes, whether the "prefix"
  * is an ancestor directory of path.  Returns the length of the longest
  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
  * is an ancestor.  (Note that this means 0 is returned if prefixes is
  * ["/"].) "/foo" is not considered an ancestor of "/foobar".  Directories
  * are not considered to be their own ancestors.  path must be in a
  * canonical form: empty components, or "." or ".." components are not
- * allowed.  Empty strings in prefixes are ignored.
+ * allowed.
  */
 int longest_ancestor_length(const char *path, struct string_list *prefixes)
 {
-	char buf[PATH_MAX+1];
 	int i, max_len = -1;
 
 	if (!strcmp(path, "/"))
@@ -593,19 +593,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
 		const char *ceil = prefixes->items[i].string;
 		int len = strlen(ceil);
 
-		if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
-			continue;
-		if (normalize_path_copy(buf, ceil) < 0)
-			continue;
-		len = strlen(buf);
-		if (len > 0 && buf[len-1] == '/')
-			buf[--len] = '\0';
+		if (len == 1 && ceil[0] == '/')
+			len = 0; /* root matches anything, with length 0 */
+		else if (!strncmp(path, ceil, len) && path[len] == '/')
+			; /* match of length len */
+		else
+			continue; /* no match */
 
-		if (!strncmp(path, buf, len) &&
-		    path[len] == '/' &&
-		    len > max_len) {
+		if (len > max_len)
 			max_len = len;
-		}
 	}
 
 	return max_len;
diff --git a/setup.c b/setup.c
index b4cd356..df97ad3 100644
--- a/setup.c
+++ b/setup.c
@@ -622,6 +622,28 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
 }
 
 /*
+ * A "string_list_each_func_t" function that normalizes an entry from
+ * GIT_CEILING_DIRECTORIES or discards it if unusable.
+ */
+static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+{
+	const char *ceil = item->string;
+	int len = strlen(ceil);
+	char buf[PATH_MAX+1];
+
+	if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
+		return 0;
+	if (normalize_path_copy(buf, ceil) < 0)
+		return 0;
+	len = strlen(buf);
+	if (len > 1 && buf[len-1] == '/')
+		buf[--len] = '\0';
+	free(item->string);
+	item->string = xstrdup(buf);
+	return 1;
+}
+
+/*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
  */
@@ -659,6 +681,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 
 	if (env_ceiling_dirs) {
 		string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
+		filter_string_list(&ceiling_dirs, 0, normalize_ceiling_entry, NULL);
 		ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
 		string_list_clear(&ceiling_dirs, 0);
 	}
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 4ef2345..09a42a4 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -93,47 +93,32 @@ norm_path /d1/s1//../s2/../../d2 /d2 POSIX
 norm_path /d1/.../d2 /d1/.../d2 POSIX
 norm_path /d1/..././../d2 /d1/d2 POSIX
 
-ancestor / "" -1
 ancestor / / -1
-ancestor /foo "" -1
-ancestor /foo : -1
-ancestor /foo ::. -1
-ancestor /foo ::..:: -1
 ancestor /foo / 0
 ancestor /foo /fo -1
 ancestor /foo /foo -1
-ancestor /foo /foo/ -1
 ancestor /foo /bar -1
-ancestor /foo /bar/ -1
 ancestor /foo /foo/bar -1
-ancestor /foo /foo:/bar/ -1
-ancestor /foo /foo/:/bar/ -1
-ancestor /foo /foo::/bar/ -1
-ancestor /foo /:/foo:/bar/ 0
-ancestor /foo /foo:/:/bar/ 0
-ancestor /foo /:/bar/:/foo 0
-ancestor /foo/bar "" -1
+ancestor /foo /foo:/bar -1
+ancestor /foo /:/foo:/bar 0
+ancestor /foo /foo:/:/bar 0
+ancestor /foo /:/bar:/foo 0
 ancestor /foo/bar / 0
 ancestor /foo/bar /fo -1
-ancestor /foo/bar foo -1
 ancestor /foo/bar /foo 4
-ancestor /foo/bar /foo/ 4
 ancestor /foo/bar /foo/ba -1
 ancestor /foo/bar /:/fo 0
 ancestor /foo/bar /foo:/foo/ba 4
 ancestor /foo/bar /bar -1
-ancestor /foo/bar /bar/ -1
-ancestor /foo/bar /fo: -1
-ancestor /foo/bar :/fo -1
-ancestor /foo/bar /foo:/bar/ 4
-ancestor /foo/bar /:/foo:/bar/ 4
-ancestor /foo/bar /foo:/:/bar/ 4
-ancestor /foo/bar /:/bar/:/fo 0
-ancestor /foo/bar /:/bar/ 0
-ancestor /foo/bar .:/foo/. 4
-ancestor /foo/bar .:/foo/.:.: 4
-ancestor /foo/bar /foo/./:.:/bar 4
-ancestor /foo/bar .:/bar -1
+ancestor /foo/bar /fo -1
+ancestor /foo/bar /foo:/bar 4
+ancestor /foo/bar /:/foo:/bar 4
+ancestor /foo/bar /foo:/:/bar 4
+ancestor /foo/bar /:/bar:/fo 0
+ancestor /foo/bar /:/bar 0
+ancestor /foo/bar /foo 4
+ancestor /foo/bar /foo:/bar 4
+ancestor /foo/bar /bar -1
 
 test_expect_success 'strip_path_suffix' '
 	test c:/msysgit = $(test-path-utils strip_path_suffix \
diff --git a/test-path-utils.c b/test-path-utils.c
index acb0560..0092cbf 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -1,6 +1,33 @@
 #include "cache.h"
 #include "string-list.h"
 
+/*
+ * A "string_list_each_func_t" function that normalizes an entry from
+ * GIT_CEILING_DIRECTORIES.  If the path is unusable for some reason,
+ * die with an explanation.
+ */
+static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+{
+	const char *ceil = item->string;
+	int len = strlen(ceil);
+	char buf[PATH_MAX+1];
+
+	if (len == 0)
+		die("Empty path is not supported");
+	if (len > PATH_MAX)
+		die("Path \"%s\" is too long", ceil);
+	if (!is_absolute_path(ceil))
+		die("Path \"%s\" is not absolute", ceil);
+	if (normalize_path_copy(buf, ceil) < 0)
+		die("Path \"%s\" could not be normalized", ceil);
+	len = strlen(buf);
+	if (len > 1 && buf[len-1] == '/')
+		die("Normalized path \"%s\" ended with slash", buf);
+	free(item->string);
+	item->string = xstrdup(buf);
+	return 1;
+}
+
 int main(int argc, char **argv)
 {
 	if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@@ -33,10 +60,26 @@ int main(int argc, char **argv)
 	if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
 		int len;
 		struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
+		char *path = xstrdup(argv[2]);
 
+		/*
+		 * We have to normalize the arguments because under
+		 * Windows, bash mangles arguments that look like
+		 * absolute POSIX paths or colon-separate lists of
+		 * absolute POSIX paths into DOS paths (e.g.,
+		 * "/foo:/foo/bar" might be converted to
+		 * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
+		 * whereas longest_ancestor_length() requires paths
+		 * that use forward slashes.
+		 */
+		if (normalize_path_copy(path, path))
+			die("Path \"%s\" could not be normalized", argv[2]);
 		string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
-		len = longest_ancestor_length(argv[2], &ceiling_dirs);
+		filter_string_list(&ceiling_dirs, 0,
+				   normalize_ceiling_entry, NULL);
+		len = longest_ancestor_length(path, &ceiling_dirs);
 		string_list_clear(&ceiling_dirs, 0);
+		free(path);
 		printf("%d\n", len);
 		return 0;
 	}
-- 
1.8.0

^ permalink raw reply related

* [PATCH v4 7/8] setup_git_directory_gently_1(): resolve symlinks in ceiling paths
From: Michael Haggerty @ 2012-10-28 16:16 UTC (permalink / raw)
  To: Jeff King
  Cc: Junio C Hamano, Jiang Xin, Lea Wiemann, Johannes Sixt, git,
	Michael Haggerty
In-Reply-To: <1351440987-26636-1-git-send-email-mhagger@alum.mit.edu>

longest_ancestor_length() relies on a textual comparison of directory
parts to find the part of path that overlaps with one of the paths in
prefix_list.  But this doesn't work if any of the prefixes involves a
symbolic link, because the directories will look different even though
they might logically refer to the same directory.  So canonicalize the
paths listed in GIT_CEILING_DIRECTORIES using real_path_if_valid()
before passing them to longest_ancestor_length().  (Also rename
normalize_ceiling_entry() to canonicalize_ceiling_entry() to reflect
the change.)

path is already in canonical form, so doesn't need to be canonicalized
again.

This fixes some problems with using GIT_CEILING_DIRECTORIES that
contains paths involving symlinks, including t4035 if run with --root
set to a path involving symlinks.

Please note that test t0060 is *not* changed analogously, because that
would make the test suite results dependent on the contents of the
local root directory.  However, real_path() is already tested
independently, and the "ancestor" tests cover the non-normalization
aspects of longest_ancestor_length(), so coverage remains sufficient.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 setup.c | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/setup.c b/setup.c
index df97ad3..f108c4b 100644
--- a/setup.c
+++ b/setup.c
@@ -622,24 +622,23 @@ static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_
 }
 
 /*
- * A "string_list_each_func_t" function that normalizes an entry from
- * GIT_CEILING_DIRECTORIES or discards it if unusable.
+ * A "string_list_each_func_t" function that canonicalizes an entry
+ * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
+ * discards it if unusable.
  */
-static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+static int canonicalize_ceiling_entry(struct string_list_item *item,
+				      void *unused)
 {
-	const char *ceil = item->string;
-	int len = strlen(ceil);
-	char buf[PATH_MAX+1];
+	char *ceil = item->string;
+	const char *real_path;
 
-	if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
+	if (!*ceil || !is_absolute_path(ceil))
 		return 0;
-	if (normalize_path_copy(buf, ceil) < 0)
+	real_path = real_path_if_valid(ceil);
+	if (!real_path)
 		return 0;
-	len = strlen(buf);
-	if (len > 1 && buf[len-1] == '/')
-		buf[--len] = '\0';
 	free(item->string);
-	item->string = xstrdup(buf);
+	item->string = xstrdup(real_path);
 	return 1;
 }
 
@@ -681,7 +680,8 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
 
 	if (env_ceiling_dirs) {
 		string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
-		filter_string_list(&ceiling_dirs, 0, normalize_ceiling_entry, NULL);
+		filter_string_list(&ceiling_dirs, 0,
+				   canonicalize_ceiling_entry, NULL);
 		ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
 		string_list_clear(&ceiling_dirs, 0);
 	}
-- 
1.8.0

^ permalink raw reply related


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