* [PATCH 01/17] t5500: add tests of error output for missing refs
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 02/17] Rename static function fetch_pack() to http_fetch_pack() mhagger
` (16 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
If "git fetch-pack" is called with reference names that do not exist
on the remote, then it should emit an error message
error: no such remote ref refs/heads/xyzzy
This is currently broken if *only* missing references are passed to
"git fetch-pack".
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
t/t5500-fetch-pack.sh | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index e80a2af..3cc3346 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -397,4 +397,34 @@ test_expect_success 'test duplicate refs from stdin' '
test_cmp expect actual
'
+test_expect_success 'set up tests of missing reference' '
+ cat >expect-error <<-\EOF
+ error: no such remote ref refs/heads/xyzzy
+ EOF
+'
+
+test_expect_failure 'test lonely missing ref' '
+ (
+ cd client &&
+ test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
+ ) >/dev/null 2>error-m &&
+ test_cmp expect-error error-m
+'
+
+test_expect_success 'test missing ref after existing' '
+ (
+ cd client &&
+ test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
+ ) >/dev/null 2>error-em &&
+ test_cmp expect-error error-em
+'
+
+test_expect_success 'test missing ref before existing' '
+ (
+ cd client &&
+ test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
+ ) >/dev/null 2>error-me &&
+ test_cmp expect-error error-me
+'
+
test_done
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 02/17] Rename static function fetch_pack() to http_fetch_pack()
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
2012-08-23 8:10 ` [PATCH 01/17] t5500: add tests of error output for missing refs mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 03/17] Fix formatting mhagger
` (15 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Avoid confusion with the non-static function of the same name from
fetch-pack.h.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
http-walker.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/http-walker.c b/http-walker.c
index 51a906e..1516c5e 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -396,7 +396,7 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo)
return ret;
}
-static int fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
+static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1)
{
struct packed_git *target;
int ret;
@@ -524,7 +524,7 @@ static int fetch(struct walker *walker, unsigned char *sha1)
if (!fetch_object(walker, altbase, sha1))
return 0;
while (altbase) {
- if (!fetch_pack(walker, altbase, sha1))
+ if (!http_fetch_pack(walker, altbase, sha1))
return 0;
fetch_alternates(walker, data->alt->base);
altbase = altbase->next;
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 03/17] Fix formatting.
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
2012-08-23 8:10 ` [PATCH 01/17] t5500: add tests of error output for missing refs mhagger
2012-08-23 8:10 ` [PATCH 02/17] Rename static function fetch_pack() to http_fetch_pack() mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 04/17] Name local variables more consistently mhagger
` (14 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 8 ++++----
fetch-pack.h | 12 ++++++------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 7912d2b..cc21047 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1062,10 +1062,10 @@ static int compare_heads(const void *a, const void *b)
struct ref *fetch_pack(struct fetch_pack_args *my_args,
int fd[], struct child_process *conn,
const struct ref *ref,
- const char *dest,
- int nr_heads,
- char **heads,
- char **pack_lockfile)
+ const char *dest,
+ int nr_heads,
+ char **heads,
+ char **pack_lockfile)
{
struct stat st;
struct ref *ref_cpy;
diff --git a/fetch-pack.h b/fetch-pack.h
index 7c2069c..1dbe90f 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -18,11 +18,11 @@ struct fetch_pack_args {
};
struct ref *fetch_pack(struct fetch_pack_args *args,
- int fd[], struct child_process *conn,
- const struct ref *ref,
- const char *dest,
- int nr_heads,
- char **heads,
- char **pack_lockfile);
+ int fd[], struct child_process *conn,
+ const struct ref *ref,
+ const char *dest,
+ int nr_heads,
+ char **heads,
+ char **pack_lockfile);
#endif
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 04/17] Name local variables more consistently
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (2 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 03/17] Fix formatting mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:39 ` Jeff King
2012-08-23 8:10 ` [PATCH 05/17] Do not check the same match_pos twice mhagger
` (13 subsequent siblings)
17 siblings, 1 reply; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Use the names (nr_heads, heads) consistently across functions, instead
of sometimes naming the same values (nr_match, match).
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 27 +++++++++++++--------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index cc21047..f11545e 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -521,7 +521,7 @@ static void mark_recent_complete_commits(unsigned long cutoff)
}
}
-static void filter_refs(struct ref **refs, int nr_match, char **match)
+static void filter_refs(struct ref **refs, int nr_heads, char **heads)
{
struct ref **return_refs;
struct ref *newlist = NULL;
@@ -530,12 +530,12 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
struct ref *fastarray[32];
int match_pos;
- if (nr_match && !args.fetch_all) {
- if (ARRAY_SIZE(fastarray) < nr_match)
- return_refs = xcalloc(nr_match, sizeof(struct ref *));
+ if (nr_heads && !args.fetch_all) {
+ if (ARRAY_SIZE(fastarray) < nr_heads)
+ return_refs = xcalloc(nr_heads, sizeof(struct ref *));
else {
return_refs = fastarray;
- memset(return_refs, 0, sizeof(struct ref *) * nr_match);
+ memset(return_refs, 0, sizeof(struct ref *) * nr_heads);
}
}
else
@@ -556,12 +556,12 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
}
else {
int cmp = -1;
- while (match_pos < nr_match) {
- cmp = strcmp(ref->name, match[match_pos]);
+ while (match_pos < nr_heads) {
+ cmp = strcmp(ref->name, heads[match_pos]);
if (cmp < 0) /* definitely do not have it */
break;
else if (cmp == 0) { /* definitely have it */
- match[match_pos][0] = '\0';
+ heads[match_pos][0] = '\0';
return_refs[match_pos] = ref;
break;
}
@@ -576,7 +576,7 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
if (!args.fetch_all) {
int i;
- for (i = 0; i < nr_match; i++) {
+ for (i = 0; i < nr_heads; i++) {
ref = return_refs[i];
if (ref) {
*newtail = ref;
@@ -595,7 +595,7 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
mark_complete(NULL, ref->old_sha1, 0, NULL);
}
-static int everything_local(struct ref **refs, int nr_match, char **match)
+static int everything_local(struct ref **refs, int nr_heads, char **heads)
{
struct ref *ref;
int retval;
@@ -646,7 +646,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
}
}
- filter_refs(refs, nr_match, match);
+ filter_refs(refs, nr_heads, heads);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
@@ -777,8 +777,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
static struct ref *do_fetch_pack(int fd[2],
const struct ref *orig_ref,
- int nr_match,
- char **match,
+ int nr_heads, char **heads,
char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
@@ -819,7 +818,7 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
- if (everything_local(&ref, nr_match, match)) {
+ if (everything_local(&ref, nr_heads, heads)) {
packet_flush(fd[1]);
goto all_done;
}
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-23 8:10 ` [PATCH 04/17] Name local variables more consistently mhagger
@ 2012-08-23 8:39 ` Jeff King
2012-08-24 7:05 ` Michael Haggerty
2012-08-26 17:39 ` Junio C Hamano
0 siblings, 2 replies; 40+ messages in thread
From: Jeff King @ 2012-08-23 8:39 UTC (permalink / raw)
To: mhagger; +Cc: Junio C Hamano, git
On Thu, Aug 23, 2012 at 10:10:29AM +0200, mhagger@alum.mit.edu wrote:
> From: Michael Haggerty <mhagger@alum.mit.edu>
>
> Use the names (nr_heads, heads) consistently across functions, instead
> of sometimes naming the same values (nr_match, match).
I think this is fine, although:
> --- a/builtin/fetch-pack.c
> +++ b/builtin/fetch-pack.c
> @@ -521,7 +521,7 @@ static void mark_recent_complete_commits(unsigned long cutoff)
> }
> }
>
> -static void filter_refs(struct ref **refs, int nr_match, char **match)
> +static void filter_refs(struct ref **refs, int nr_heads, char **heads)
> {
> struct ref **return_refs;
> struct ref *newlist = NULL;
> @@ -530,12 +530,12 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
> struct ref *fastarray[32];
> int match_pos;
This match_pos is an index into the "match" array, which becomes "head".
Should it become head_pos?
And then bits like this:
> - while (match_pos < nr_match) {
> - cmp = strcmp(ref->name, match[match_pos]);
> + while (match_pos < nr_heads) {
> + cmp = strcmp(ref->name, heads[match_pos]);
Would be:
while (head_pos < nr_heads)
which makes more sense to me.
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-23 8:39 ` Jeff King
@ 2012-08-24 7:05 ` Michael Haggerty
2012-08-26 17:39 ` Junio C Hamano
1 sibling, 0 replies; 40+ messages in thread
From: Michael Haggerty @ 2012-08-24 7:05 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
On 08/23/2012 10:39 AM, Jeff King wrote:
> On Thu, Aug 23, 2012 at 10:10:29AM +0200, mhagger@alum.mit.edu wrote:
>
>> From: Michael Haggerty <mhagger@alum.mit.edu>
>>
>> Use the names (nr_heads, heads) consistently across functions, instead
>> of sometimes naming the same values (nr_match, match).
>
> I think this is fine, although:
>
>> --- a/builtin/fetch-pack.c
>> +++ b/builtin/fetch-pack.c
>> @@ -521,7 +521,7 @@ static void mark_recent_complete_commits(unsigned long cutoff)
>> }
>> }
>>
>> -static void filter_refs(struct ref **refs, int nr_match, char **match)
>> +static void filter_refs(struct ref **refs, int nr_heads, char **heads)
>> {
>> struct ref **return_refs;
>> struct ref *newlist = NULL;
>> @@ -530,12 +530,12 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
>> struct ref *fastarray[32];
>> int match_pos;
>
> This match_pos is an index into the "match" array, which becomes "head".
> Should it become head_pos?
>
> And then bits like this:
>
>> - while (match_pos < nr_match) {
>> - cmp = strcmp(ref->name, match[match_pos]);
>> + while (match_pos < nr_heads) {
>> + cmp = strcmp(ref->name, heads[match_pos]);
>
> Would be:
>
> while (head_pos < nr_heads)
>
> which makes more sense to me.
I was up in the air about this, because match_pos *is* the position at
which a match is attempted. But since the name also strikes you as
wrong, I will change it in the next version.
Thanks for this and all of your other comments!
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-23 8:39 ` Jeff King
2012-08-24 7:05 ` Michael Haggerty
@ 2012-08-26 17:39 ` Junio C Hamano
2012-08-27 9:22 ` Michael Haggerty
1 sibling, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2012-08-26 17:39 UTC (permalink / raw)
To: Jeff King; +Cc: mhagger, git
Jeff King <peff@peff.net> writes:
> On Thu, Aug 23, 2012 at 10:10:29AM +0200, mhagger@alum.mit.edu wrote:
>
>> From: Michael Haggerty <mhagger@alum.mit.edu>
>>
>> Use the names (nr_heads, heads) consistently across functions, instead
>> of sometimes naming the same values (nr_match, match).
>
> I think this is fine, although:
>
>> --- a/builtin/fetch-pack.c
>> +++ b/builtin/fetch-pack.c
>> @@ -521,7 +521,7 @@ static void mark_recent_complete_commits(unsigned long cutoff)
>> }
>> }
>>
>> -static void filter_refs(struct ref **refs, int nr_match, char **match)
>> +static void filter_refs(struct ref **refs, int nr_heads, char **heads)
>> {
>> struct ref **return_refs;
>> struct ref *newlist = NULL;
>> @@ -530,12 +530,12 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
>> struct ref *fastarray[32];
>> int match_pos;
>
> This match_pos is an index into the "match" array, which becomes "head".
> Should it become head_pos?
>
> And then bits like this:
>
>> - while (match_pos < nr_match) {
>> - cmp = strcmp(ref->name, match[match_pos]);
>> + while (match_pos < nr_heads) {
>> + cmp = strcmp(ref->name, heads[match_pos]);
>
> Would be:
>
> while (head_pos < nr_heads)
>
> which makes more sense to me.
Using one name is better, but I wonder "heads" is the better one
between the two.
After all, this codepath is not limited to branches these days as
the word "head" implies. Rather, <nr_thing, thing> has what we
asked for, and <refs> has what the other sides have, and we are
trying to make sure we haven't asked what they do not have in this
function.
And the way we do so is to match the "thing"s with what are in
"refs" to find unmatched ones.
So between the two, I would have chosen "match" instead of "heads"
to call the "thing".
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-26 17:39 ` Junio C Hamano
@ 2012-08-27 9:22 ` Michael Haggerty
2012-08-27 9:25 ` Jeff King
2012-08-27 16:50 ` Junio C Hamano
0 siblings, 2 replies; 40+ messages in thread
From: Michael Haggerty @ 2012-08-27 9:22 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git
On 08/26/2012 07:39 PM, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
>> On Thu, Aug 23, 2012 at 10:10:29AM +0200, mhagger@alum.mit.edu wrote:
>>
>>> From: Michael Haggerty <mhagger@alum.mit.edu>
>>>
>>> Use the names (nr_heads, heads) consistently across functions, instead
>>> of sometimes naming the same values (nr_match, match).
>>
>> I think this is fine, although:
>>
>>> --- a/builtin/fetch-pack.c
>>> +++ b/builtin/fetch-pack.c
>>> @@ -521,7 +521,7 @@ static void mark_recent_complete_commits(unsigned long cutoff)
>>> }
>>> }
>>>
>>> -static void filter_refs(struct ref **refs, int nr_match, char **match)
>>> +static void filter_refs(struct ref **refs, int nr_heads, char **heads)
>>> {
>>> struct ref **return_refs;
>>> struct ref *newlist = NULL;
>>> @@ -530,12 +530,12 @@ static void filter_refs(struct ref **refs, int nr_match, char **match)
>>> struct ref *fastarray[32];
>>> int match_pos;
>>
>> This match_pos is an index into the "match" array, which becomes "head".
>> Should it become head_pos?
>>
>> And then bits like this:
>>
>>> - while (match_pos < nr_match) {
>>> - cmp = strcmp(ref->name, match[match_pos]);
>>> + while (match_pos < nr_heads) {
>>> + cmp = strcmp(ref->name, heads[match_pos]);
>>
>> Would be:
>>
>> while (head_pos < nr_heads)
>>
>> which makes more sense to me.
>
> Using one name is better, but I wonder "heads" is the better one
> between the two.
>
> After all, this codepath is not limited to branches these days as
> the word "head" implies. Rather, <nr_thing, thing> has what we
> asked for, and <refs> has what the other sides have, and we are
> trying to make sure we haven't asked what they do not have in this
> function.
>
> And the way we do so is to match the "thing"s with what are in
> "refs" to find unmatched ones.
>
> So between the two, I would have chosen "match" instead of "heads"
> to call the "thing".
When I decided between "heads" and "match", my main consideration was
that "match" sounds like something that has already been matched, not
something that is being matched against. The word "match" also implies
to me that some nontrivial matching process is going on, like glob
expansion.
But I agree with you that "heads" also has disadvantages.
What about a third option: "refnames"? This name makes it clear that we
are talking about simple names as opposed to "struct ref" or some kind
of refname glob patterns and also makes it clear that they are not
necessarily all branches. It would also be distinct from the "refs"
linked list that is often used in the same functions.
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-27 9:22 ` Michael Haggerty
@ 2012-08-27 9:25 ` Jeff King
2012-08-27 16:55 ` Junio C Hamano
2012-08-27 16:50 ` Junio C Hamano
1 sibling, 1 reply; 40+ messages in thread
From: Jeff King @ 2012-08-27 9:25 UTC (permalink / raw)
To: Michael Haggerty; +Cc: Junio C Hamano, git
On Mon, Aug 27, 2012 at 11:22:36AM +0200, Michael Haggerty wrote:
> > Using one name is better, but I wonder "heads" is the better one
> > between the two.
> >
> > After all, this codepath is not limited to branches these days as
> > the word "head" implies. Rather, <nr_thing, thing> has what we
> > asked for, and <refs> has what the other sides have, and we are
> > trying to make sure we haven't asked what they do not have in this
> > function.
> >
> > And the way we do so is to match the "thing"s with what are in
> > "refs" to find unmatched ones.
> >
> > So between the two, I would have chosen "match" instead of "heads"
> > to call the "thing".
>
> When I decided between "heads" and "match", my main consideration was
> that "match" sounds like something that has already been matched, not
> something that is being matched against. The word "match" also implies
> to me that some nontrivial matching process is going on, like glob
> expansion.
>
> But I agree with you that "heads" also has disadvantages.
>
> What about a third option: "refnames"? This name makes it clear that we
> are talking about simple names as opposed to "struct ref" or some kind
> of refname glob patterns and also makes it clear that they are not
> necessarily all branches. It would also be distinct from the "refs"
> linked list that is often used in the same functions.
Yeah, I agree that "refnames" would be better. I think something like
"spec" or "refspec" would indicate better that they are to be matched
against, but then you run afoul of confusing that with colon-delimited
refspecs (which I do not think fetch-pack understands at all).
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-27 9:25 ` Jeff King
@ 2012-08-27 16:55 ` Junio C Hamano
0 siblings, 0 replies; 40+ messages in thread
From: Junio C Hamano @ 2012-08-27 16:55 UTC (permalink / raw)
To: Jeff King; +Cc: Michael Haggerty, git
Jeff King <peff@peff.net> writes:
> Yeah, I agree that "refnames" would be better. I think something like
> "spec" or "refspec" would indicate better that they are to be matched
> against, but then you run afoul of confusing that with colon-delimited
> refspecs (which I do not think fetch-pack understands at all).
Correct. It only takes the LHS of the refspecs, following the "one
tool does one specific thing" philosophy. The arrangement was that
the calling script interpreted refspecs, split them into LHS and
RHS, fed the LHS of the refspecs to fetch-pack, and then read the
output to learn which local refs (i.e. RHS of the refspecs) to
update with what value.
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 04/17] Name local variables more consistently
2012-08-27 9:22 ` Michael Haggerty
2012-08-27 9:25 ` Jeff King
@ 2012-08-27 16:50 ` Junio C Hamano
1 sibling, 0 replies; 40+ messages in thread
From: Junio C Hamano @ 2012-08-27 16:50 UTC (permalink / raw)
To: Michael Haggerty; +Cc: Jeff King, git
Michael Haggerty <mhagger@alum.mit.edu> writes:
> On 08/26/2012 07:39 PM, Junio C Hamano wrote:
> ...
>> After all, this codepath is not limited to branches these days as
>> the word "head" implies. Rather, <nr_thing, thing> has what we
>> asked for, and <refs> has what the other sides have, and we are
>> trying to make sure we haven't asked what they do not have in this
>> function.
>>
>> And the way we do so is to match the "thing"s with what are in
>> "refs" to find unmatched ones.
>>
>> So between the two, I would have chosen "match" instead of "heads"
>> to call the "thing".
>
> When I decided between "heads" and "match", my main consideration was
> that "match" sounds like something that has already been matched, not
> something that is being matched against. The word "match" also implies
> to me that some nontrivial matching process is going on, like glob
> expansion.
>
> But I agree with you that "heads" also has disadvantages.
>
> What about a third option: "refnames"? This name makes it clear that we
> are talking about simple names as opposed to "struct ref" or some kind
> of refname glob patterns and also makes it clear that they are not
> necessarily all branches. It would also be distinct from the "refs"
> linked list that is often used in the same functions.
"refnames" has a similar issue as "match", as you have pointed out,
and more. Was it the remote or us who populated the "refnames"
(answer: these are "our" refnames, and the other one "refs" is what
they have)? What do we have that "refnames" for (answer: these
specify what we are going to pick among what they have)?
Perhaps "asked"? At the beginning of the processing, we have all
that were asked for, and at the end, we leave what were asked for
but were not fulfilled.
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 05/17] Do not check the same match_pos twice
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (3 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 04/17] Name local variables more consistently mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:42 ` Jeff King
2012-08-23 8:10 ` [PATCH 06/17] Let fetch_pack() inform caller about number of unique heads mhagger
` (12 subsequent siblings)
17 siblings, 1 reply; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Once a match has been found at match_pos, the entry is zeroed and no
future attempts will match that entry. So increment match_pos to
avoid checking against the zeroed-out entry during the next iteration.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index f11545e..a6406e7 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -561,8 +561,8 @@ static void filter_refs(struct ref **refs, int nr_heads, char **heads)
if (cmp < 0) /* definitely do not have it */
break;
else if (cmp == 0) { /* definitely have it */
- heads[match_pos][0] = '\0';
return_refs[match_pos] = ref;
+ heads[match_pos++][0] = '\0';
break;
}
else /* might have it; keep looking */
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 05/17] Do not check the same match_pos twice
2012-08-23 8:10 ` [PATCH 05/17] Do not check the same match_pos twice mhagger
@ 2012-08-23 8:42 ` Jeff King
0 siblings, 0 replies; 40+ messages in thread
From: Jeff King @ 2012-08-23 8:42 UTC (permalink / raw)
To: mhagger; +Cc: Junio C Hamano, git
On Thu, Aug 23, 2012 at 10:10:30AM +0200, mhagger@alum.mit.edu wrote:
> From: Michael Haggerty <mhagger@alum.mit.edu>
>
> Once a match has been found at match_pos, the entry is zeroed and no
> future attempts will match that entry. So increment match_pos to
> avoid checking against the zeroed-out entry during the next iteration.
Good catch.
A subtle side effect of this zero-ing (not introduced by your patch, but
something I noticed while re-reading the code) is that we implicitly
eliminate duplicate entries from the list of remote refs. There
shouldn't generally be any duplicates, of course, but I think skipping
them is probably sane.
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 06/17] Let fetch_pack() inform caller about number of unique heads
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (4 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 05/17] Do not check the same match_pos twice mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:54 ` Jeff King
2012-08-23 8:10 ` [PATCH 07/17] Pass nr_heads to do_pack_ref() by reference mhagger
` (11 subsequent siblings)
17 siblings, 1 reply; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
fetch_pack() remotes duplicates from the list (nr_heads, heads),
thereby shrinking the list. But previously, the caller was not
informed about the shrinkage. This would cause a spurious error
message to be emitted by cmd_fetch_pack() if "git fetch-pack" is
called with duplicate refnames.
So change the signature of fetch_pack() to accept nr_heads by
reference, and if any duplicates were removed then modify it to
reflect the number of remaining references.
The last test of t5500 inexplicably *required* "git fetch-pack" to
fail when fetching a list of references that contains duplicates;
i.e., it insisted on the buggy behavior. So change the test to expect
the correct behavior.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 12 ++++++------
fetch-pack.h | 2 +-
t/t5500-fetch-pack.sh | 2 +-
transport.c | 2 +-
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index a6406e7..3c93ec4 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1021,7 +1021,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
get_remote_heads(fd[0], &ref, 0, NULL);
ref = fetch_pack(&args, fd, conn, ref, dest,
- nr_heads, heads, pack_lockfile_ptr);
+ &nr_heads, heads, pack_lockfile_ptr);
if (pack_lockfile) {
printf("lock %s\n", pack_lockfile);
fflush(stdout);
@@ -1062,7 +1062,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
int fd[], struct child_process *conn,
const struct ref *ref,
const char *dest,
- int nr_heads,
+ int *nr_heads,
char **heads,
char **pack_lockfile)
{
@@ -1077,16 +1077,16 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
st.st_mtime = 0;
}
- if (heads && nr_heads) {
- qsort(heads, nr_heads, sizeof(*heads), compare_heads);
- nr_heads = remove_duplicates(nr_heads, heads);
+ if (heads && *nr_heads) {
+ qsort(heads, *nr_heads, sizeof(*heads), compare_heads);
+ *nr_heads = remove_duplicates(*nr_heads, heads);
}
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
}
- ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
+ ref_cpy = do_fetch_pack(fd, ref, *nr_heads, heads, pack_lockfile);
if (args.depth > 0) {
struct cache_time mtime;
diff --git a/fetch-pack.h b/fetch-pack.h
index 1dbe90f..a9d505b 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -21,7 +21,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
int fd[], struct child_process *conn,
const struct ref *ref,
const char *dest,
- int nr_heads,
+ int *nr_heads,
char **heads,
char **pack_lockfile);
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 3cc3346..0d4edcb 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -391,7 +391,7 @@ test_expect_success 'fetch mixed refs from cmdline and stdin' '
test_expect_success 'test duplicate refs from stdin' '
(
cd client &&
- test_must_fail git fetch-pack --stdin --no-progress .. <../input.dup
+ git fetch-pack --stdin --no-progress .. <../input.dup
) >output &&
cut -d " " -f 2 <output | sort >actual &&
test_cmp expect actual
diff --git a/transport.c b/transport.c
index 1811b50..f7bbf58 100644
--- a/transport.c
+++ b/transport.c
@@ -548,7 +548,7 @@ static int fetch_refs_via_pack(struct transport *transport,
refs = fetch_pack(&args, data->fd, data->conn,
refs_tmp ? refs_tmp : transport->remote_refs,
- dest, nr_heads, heads, &transport->pack_lockfile);
+ dest, &nr_heads, heads, &transport->pack_lockfile);
close(data->fd[0]);
close(data->fd[1]);
if (finish_connect(data->conn))
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 06/17] Let fetch_pack() inform caller about number of unique heads
2012-08-23 8:10 ` [PATCH 06/17] Let fetch_pack() inform caller about number of unique heads mhagger
@ 2012-08-23 8:54 ` Jeff King
2012-08-25 5:05 ` Michael Haggerty
0 siblings, 1 reply; 40+ messages in thread
From: Jeff King @ 2012-08-23 8:54 UTC (permalink / raw)
To: mhagger; +Cc: Junio C Hamano, git
On Thu, Aug 23, 2012 at 10:10:31AM +0200, mhagger@alum.mit.edu wrote:
> From: Michael Haggerty <mhagger@alum.mit.edu>
>
> fetch_pack() remotes duplicates from the list (nr_heads, heads),
> thereby shrinking the list. But previously, the caller was not
> informed about the shrinkage. This would cause a spurious error
> message to be emitted by cmd_fetch_pack() if "git fetch-pack" is
> called with duplicate refnames.
>
> So change the signature of fetch_pack() to accept nr_heads by
> reference, and if any duplicates were removed then modify it to
> reflect the number of remaining references.
>
> The last test of t5500 inexplicably *required* "git fetch-pack" to
> fail when fetching a list of references that contains duplicates;
> i.e., it insisted on the buggy behavior. So change the test to expect
> the correct behavior.
Eek, yeah, the current behavior is obviously wrong. The
remove_duplicates code comes from 310b86d (fetch-pack: do not barf when
duplicate re patterns are given, 2006-11-25) and clearly meant for
fetch-pack to handle this case gracefully.
> diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
> index 3cc3346..0d4edcb 100755
> --- a/t/t5500-fetch-pack.sh
> +++ b/t/t5500-fetch-pack.sh
> @@ -391,7 +391,7 @@ test_expect_success 'fetch mixed refs from cmdline and stdin' '
> test_expect_success 'test duplicate refs from stdin' '
> (
> cd client &&
> - test_must_fail git fetch-pack --stdin --no-progress .. <../input.dup
> + git fetch-pack --stdin --no-progress .. <../input.dup
> ) >output &&
> cut -d " " -f 2 <output | sort >actual &&
> test_cmp expect actual
It's interesting that the output was the same before and after the fix.
I guess that is because the error comes at the very end, when we are
making sure all of the provided heads have been consumed.
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 06/17] Let fetch_pack() inform caller about number of unique heads
2012-08-23 8:54 ` Jeff King
@ 2012-08-25 5:05 ` Michael Haggerty
0 siblings, 0 replies; 40+ messages in thread
From: Michael Haggerty @ 2012-08-25 5:05 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
On 08/23/2012 10:54 AM, Jeff King wrote:
> On Thu, Aug 23, 2012 at 10:10:31AM +0200, mhagger@alum.mit.edu wrote:
>
>> From: Michael Haggerty <mhagger@alum.mit.edu>
>>
>> fetch_pack() remotes duplicates from the list (nr_heads, heads),
>> thereby shrinking the list. But previously, the caller was not
>> informed about the shrinkage. This would cause a spurious error
>> message to be emitted by cmd_fetch_pack() if "git fetch-pack" is
>> called with duplicate refnames.
>>
>> So change the signature of fetch_pack() to accept nr_heads by
>> reference, and if any duplicates were removed then modify it to
>> reflect the number of remaining references.
>>
>> The last test of t5500 inexplicably *required* "git fetch-pack" to
>> fail when fetching a list of references that contains duplicates;
>> i.e., it insisted on the buggy behavior. So change the test to expect
>> the correct behavior.
>
> Eek, yeah, the current behavior is obviously wrong. The
> remove_duplicates code comes from 310b86d (fetch-pack: do not barf when
> duplicate re patterns are given, 2006-11-25) and clearly meant for
> fetch-pack to handle this case gracefully.
>
>> diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
>> index 3cc3346..0d4edcb 100755
>> --- a/t/t5500-fetch-pack.sh
>> +++ b/t/t5500-fetch-pack.sh
>> @@ -391,7 +391,7 @@ test_expect_success 'fetch mixed refs from cmdline and stdin' '
>> test_expect_success 'test duplicate refs from stdin' '
>> (
>> cd client &&
>> - test_must_fail git fetch-pack --stdin --no-progress .. <../input.dup
>> + git fetch-pack --stdin --no-progress .. <../input.dup
>> ) >output &&
>> cut -d " " -f 2 <output | sort >actual &&
>> test_cmp expect actual
>
> It's interesting that the output was the same before and after the fix.
> I guess that is because the error comes at the very end, when we are
> making sure all of the provided heads have been consumed.
"git fetch-pack" emits information about successfully-received
references regardless of whether some requested references were not
received. The "no such remote ref %s" output goes to stderr. So the
only difference between before/after fix should be what is written to
stderr, whereas the test only looks at stdout.
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 07/17] Pass nr_heads to do_pack_ref() by reference
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (5 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 06/17] Let fetch_pack() inform caller about number of unique heads mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 08/17] Pass nr_heads to everything_local() " mhagger
` (10 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
This is the first of a few baby steps towards changing filter_refs()
to compress matched refs out of the list rather than overwriting the
first character of such references with '\0'.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 3c93ec4..d3ff166 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -777,7 +777,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
static struct ref *do_fetch_pack(int fd[2],
const struct ref *orig_ref,
- int nr_heads, char **heads,
+ int *nr_heads, char **heads,
char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
@@ -818,7 +818,7 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
- if (everything_local(&ref, nr_heads, heads)) {
+ if (everything_local(&ref, *nr_heads, heads)) {
packet_flush(fd[1]);
goto all_done;
}
@@ -1086,7 +1086,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
packet_flush(fd[1]);
die("no matching remote head");
}
- ref_cpy = do_fetch_pack(fd, ref, *nr_heads, heads, pack_lockfile);
+ ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
if (args.depth > 0) {
struct cache_time mtime;
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 08/17] Pass nr_heads to everything_local() by reference
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (6 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 07/17] Pass nr_heads to do_pack_ref() by reference mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 09/17] Pass nr_heads to filter_refs() " mhagger
` (9 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index d3ff166..5905dae 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -595,7 +595,7 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
mark_complete(NULL, ref->old_sha1, 0, NULL);
}
-static int everything_local(struct ref **refs, int nr_heads, char **heads)
+static int everything_local(struct ref **refs, int *nr_heads, char **heads)
{
struct ref *ref;
int retval;
@@ -646,7 +646,7 @@ static int everything_local(struct ref **refs, int nr_heads, char **heads)
}
}
- filter_refs(refs, nr_heads, heads);
+ filter_refs(refs, *nr_heads, heads);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
@@ -818,7 +818,7 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
- if (everything_local(&ref, *nr_heads, heads)) {
+ if (everything_local(&ref, nr_heads, heads)) {
packet_flush(fd[1]);
goto all_done;
}
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 09/17] Pass nr_heads to filter_refs() by reference
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (7 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 08/17] Pass nr_heads to everything_local() " mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 10/17] Remove ineffective optimization mhagger
` (8 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 5905dae..0426cf4 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -521,7 +521,7 @@ static void mark_recent_complete_commits(unsigned long cutoff)
}
}
-static void filter_refs(struct ref **refs, int nr_heads, char **heads)
+static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
{
struct ref **return_refs;
struct ref *newlist = NULL;
@@ -530,12 +530,12 @@ static void filter_refs(struct ref **refs, int nr_heads, char **heads)
struct ref *fastarray[32];
int match_pos;
- if (nr_heads && !args.fetch_all) {
- if (ARRAY_SIZE(fastarray) < nr_heads)
- return_refs = xcalloc(nr_heads, sizeof(struct ref *));
+ if (*nr_heads && !args.fetch_all) {
+ if (ARRAY_SIZE(fastarray) < *nr_heads)
+ return_refs = xcalloc(*nr_heads, sizeof(struct ref *));
else {
return_refs = fastarray;
- memset(return_refs, 0, sizeof(struct ref *) * nr_heads);
+ memset(return_refs, 0, sizeof(struct ref *) * *nr_heads);
}
}
else
@@ -556,7 +556,7 @@ static void filter_refs(struct ref **refs, int nr_heads, char **heads)
}
else {
int cmp = -1;
- while (match_pos < nr_heads) {
+ while (match_pos < *nr_heads) {
cmp = strcmp(ref->name, heads[match_pos]);
if (cmp < 0) /* definitely do not have it */
break;
@@ -576,7 +576,7 @@ static void filter_refs(struct ref **refs, int nr_heads, char **heads)
if (!args.fetch_all) {
int i;
- for (i = 0; i < nr_heads; i++) {
+ for (i = 0; i < *nr_heads; i++) {
ref = return_refs[i];
if (ref) {
*newtail = ref;
@@ -646,7 +646,7 @@ static int everything_local(struct ref **refs, int *nr_heads, char **heads)
}
}
- filter_refs(refs, *nr_heads, heads);
+ filter_refs(refs, nr_heads, heads);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 10/17] Remove ineffective optimization
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (8 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 09/17] Pass nr_heads to filter_refs() " mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 11/17] filter_refs(): do not leave gaps in return_refs mhagger
` (7 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
I cannot find a scenario in which this function is called any
significant number of times, so simplify the code by always allocating
an array for return_refs rather than trying to use a stack-allocated
array for small lists.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 0426cf4..9398059 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -527,17 +527,10 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
- struct ref *fastarray[32];
int match_pos;
- if (*nr_heads && !args.fetch_all) {
- if (ARRAY_SIZE(fastarray) < *nr_heads)
- return_refs = xcalloc(*nr_heads, sizeof(struct ref *));
- else {
- return_refs = fastarray;
- memset(return_refs, 0, sizeof(struct ref *) * *nr_heads);
- }
- }
+ if (*nr_heads && !args.fetch_all)
+ return_refs = xcalloc(*nr_heads, sizeof(struct ref *));
else
return_refs = NULL;
@@ -584,8 +577,7 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
newtail = &ref->next;
}
}
- if (return_refs != fastarray)
- free(return_refs);
+ free(return_refs);
}
*refs = newlist;
}
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 11/17] filter_refs(): do not leave gaps in return_refs
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (9 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 10/17] Remove ineffective optimization mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 12/17] filter_refs(): compress unmatched refs in heads array mhagger
` (6 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
It used to be that this function processed refnames in some arbitrary
order but wanted to return them in the order that they were requested,
not the order that they were processed. Now, the refnames are
processed in sorted order, so there is no reason to go to the extra
effort.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 9398059..8366012 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -527,14 +527,13 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
- int match_pos;
+ int match_pos = 0, matched = 0;
if (*nr_heads && !args.fetch_all)
return_refs = xcalloc(*nr_heads, sizeof(struct ref *));
else
return_refs = NULL;
- match_pos = 0;
for (ref = *refs; ref; ref = next) {
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
@@ -554,7 +553,7 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
if (cmp < 0) /* definitely do not have it */
break;
else if (cmp == 0) { /* definitely have it */
- return_refs[match_pos] = ref;
+ return_refs[matched++] = ref;
heads[match_pos++][0] = '\0';
break;
}
@@ -569,13 +568,11 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
if (!args.fetch_all) {
int i;
- for (i = 0; i < *nr_heads; i++) {
+ for (i = 0; i < matched; i++) {
ref = return_refs[i];
- if (ref) {
- *newtail = ref;
- ref->next = NULL;
- newtail = &ref->next;
- }
+ *newtail = ref;
+ ref->next = NULL;
+ newtail = &ref->next;
}
free(return_refs);
}
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 12/17] filter_refs(): compress unmatched refs in heads array
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (10 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 11/17] filter_refs(): do not leave gaps in return_refs mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 13/17] cmd_fetch_pack: return early if finish_connect() returns an error mhagger
` (5 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Remove any references that were received from the remote from the list
(*nr_heads, heads) of requested references by squeezing them out of
the list (rather than overwriting their names with NUL characters, as
before). On exit, *nr_heads is the number of requested references
that were not received.
Document this aspect of fetch_pack() in a comment in the header file.
(More documentation is obviously still needed.)
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 21 +++++++++++++--------
fetch-pack.h | 6 ++++++
transport.c | 3 ++-
3 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 8366012..cc9af80 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -527,7 +527,7 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
- int match_pos = 0, matched = 0;
+ int match_pos = 0, matched = 0, unmatched = 0;
if (*nr_heads && !args.fetch_all)
return_refs = xcalloc(*nr_heads, sizeof(struct ref *));
@@ -554,11 +554,12 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
break;
else if (cmp == 0) { /* definitely have it */
return_refs[matched++] = ref;
- heads[match_pos++][0] = '\0';
+ match_pos++;
break;
}
- else /* might have it; keep looking */
- match_pos++;
+ else { /* might have it; keep looking */
+ heads[unmatched++] = heads[match_pos++];
+ }
}
if (!cmp)
continue; /* we will link it later */
@@ -568,6 +569,12 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
if (!args.fetch_all) {
int i;
+
+ /* copy remaining unmatched heads: */
+ while (match_pos < *nr_heads)
+ heads[unmatched++] = heads[match_pos++];
+ *nr_heads = unmatched;
+
for (i = 0; i < matched; i++) {
ref = return_refs[i];
*newtail = ref;
@@ -1028,10 +1035,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
* silently succeed without issuing an error.
*/
for (i = 0; i < nr_heads; i++)
- if (heads[i] && heads[i][0]) {
- error("no such remote ref %s", heads[i]);
- ret = 1;
- }
+ error("no such remote ref %s", heads[i]);
+ ret = 1;
}
while (ref) {
printf("%s %s\n",
diff --git a/fetch-pack.h b/fetch-pack.h
index a9d505b..915858e 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -17,6 +17,12 @@ struct fetch_pack_args {
stateless_rpc:1;
};
+/*
+ * (*nr_heads, heads) is an array of pointers to the full names of
+ * remote references that should be updated from. On return, both
+ * will have been changed to list only the names that were not found
+ * on the remote.
+ */
struct ref *fetch_pack(struct fetch_pack_args *args,
int fd[], struct child_process *conn,
const struct ref *ref,
diff --git a/transport.c b/transport.c
index f7bbf58..3c38d44 100644
--- a/transport.c
+++ b/transport.c
@@ -520,6 +520,7 @@ static int fetch_refs_via_pack(struct transport *transport,
struct git_transport_data *data = transport->data;
char **heads = xmalloc(nr_heads * sizeof(*heads));
char **origh = xmalloc(nr_heads * sizeof(*origh));
+ int orig_nr_heads = nr_heads;
const struct ref *refs;
char *dest = xstrdup(transport->url);
struct fetch_pack_args args;
@@ -558,7 +559,7 @@ static int fetch_refs_via_pack(struct transport *transport,
free_refs(refs_tmp);
- for (i = 0; i < nr_heads; i++)
+ for (i = 0; i < orig_nr_heads; i++)
free(origh[i]);
free(origh);
free(heads);
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 13/17] cmd_fetch_pack: return early if finish_connect() returns an error
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (11 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 12/17] filter_refs(): compress unmatched refs in heads array mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 14/17] Report missing refs even if no existing refs were received mhagger
` (4 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
This simplifies the logic without changing the behavior.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index cc9af80..4794839 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1025,10 +1025,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
close(fd[0]);
close(fd[1]);
if (finish_connect(conn))
- ref = NULL;
- ret = !ref;
+ return 1;
- if (!ret && nr_heads) {
+ ret = !ref;
+ if (ref && nr_heads) {
/* If the heads to pull were given, we should have
* consumed all of them by matching the remote.
* Otherwise, 'git fetch remote no-such-ref' would
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 14/17] Report missing refs even if no existing refs were received
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (12 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 13/17] cmd_fetch_pack: return early if finish_connect() returns an error mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 15/17] cmd_fetch_pack(): simplify computation of return value mhagger
` (3 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
This fixes a test in t5500.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 2 +-
t/t5500-fetch-pack.sh | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 4794839..5b1e603 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1028,7 +1028,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
return 1;
ret = !ref;
- if (ref && nr_heads) {
+ if (nr_heads) {
/* If the heads to pull were given, we should have
* consumed all of them by matching the remote.
* Otherwise, 'git fetch remote no-such-ref' would
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 0d4edcb..f78a118 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -403,7 +403,7 @@ test_expect_success 'set up tests of missing reference' '
EOF
'
-test_expect_failure 'test lonely missing ref' '
+test_expect_success 'test lonely missing ref' '
(
cd client &&
test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 15/17] cmd_fetch_pack(): simplify computation of return value
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (13 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 14/17] Report missing refs even if no existing refs were received mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 8:10 ` [PATCH 16/17] fetch_pack(): free matching heads mhagger
` (2 subsequent siblings)
17 siblings, 0 replies; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
Set the final value at initialization rather than initializing it then
sometimes changing it.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 5b1e603..6c1a48e 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -1027,17 +1027,16 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
if (finish_connect(conn))
return 1;
- ret = !ref;
- if (nr_heads) {
- /* If the heads to pull were given, we should have
- * consumed all of them by matching the remote.
- * Otherwise, 'git fetch remote no-such-ref' would
- * silently succeed without issuing an error.
- */
- for (i = 0; i < nr_heads; i++)
- error("no such remote ref %s", heads[i]);
- ret = 1;
- }
+ ret = !ref || nr_heads;
+
+ /*
+ * If the heads to pull were given, we should have consumed
+ * all of them by matching the remote. Otherwise, 'git fetch
+ * remote no-such-ref' would silently succeed without issuing
+ * an error.
+ */
+ for (i = 0; i < nr_heads; i++)
+ error("no such remote ref %s", heads[i]);
while (ref) {
printf("%s %s\n",
sha1_to_hex(ref->old_sha1), ref->name);
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 16/17] fetch_pack(): free matching heads
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (14 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 15/17] cmd_fetch_pack(): simplify computation of return value mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 9:04 ` Jeff King
2012-08-23 8:10 ` [PATCH 17/17] fetch_refs(): simplify logic mhagger
2012-08-23 9:26 ` [PATCH 00/17] Clean up how fetch_pack() handles the heads list Jeff King
17 siblings, 1 reply; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
fetch_pack() used to delete entries from the input list (*nr_heads,
heads) and drop them on the floor. (Even earlier versions dropped
some names on the floor and modified others.) This forced
fetch_refs_via_pack() to create a separate copy of the original list
so that it could free the all of the names.
Instead, teach fetch_pack() to free any names that it discards from
the list, and change fetch_refs_via_pack() to free only the remaining
(unmatched) names.
Document the change in the function comment in the header file.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
This change forces callers to allocate names on the heap. But the two
existing callers did so already, and the function already modified the
list, so I think the new style is no more intrusive than the old.
builtin/fetch-pack.c | 4 +++-
fetch-pack.h | 7 ++++---
transport.c | 9 +++------
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 6c1a48e..9650d04 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -554,7 +554,7 @@ static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
break;
else if (cmp == 0) { /* definitely have it */
return_refs[matched++] = ref;
- match_pos++;
+ free(heads[match_pos++]);
break;
}
else { /* might have it; keep looking */
@@ -844,6 +844,8 @@ static int remove_duplicates(int nr_heads, char **heads)
for (src = dst = 1; src < nr_heads; src++)
if (strcmp(heads[src], heads[dst-1]))
heads[dst++] = heads[src];
+ else
+ free(heads[src]);
return dst;
}
diff --git a/fetch-pack.h b/fetch-pack.h
index 915858e..d1860eb 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -19,9 +19,10 @@ struct fetch_pack_args {
/*
* (*nr_heads, heads) is an array of pointers to the full names of
- * remote references that should be updated from. On return, both
- * will have been changed to list only the names that were not found
- * on the remote.
+ * remote references that should be updated from. The names must have
+ * been allocated from the heap. The names that are found in the
+ * remote will be removed from the list and freed; the others will be
+ * left in the front of the array with *nr_heads adjusted accordingly.
*/
struct ref *fetch_pack(struct fetch_pack_args *args,
int fd[], struct child_process *conn,
diff --git a/transport.c b/transport.c
index 3c38d44..f94b753 100644
--- a/transport.c
+++ b/transport.c
@@ -519,8 +519,6 @@ static int fetch_refs_via_pack(struct transport *transport,
{
struct git_transport_data *data = transport->data;
char **heads = xmalloc(nr_heads * sizeof(*heads));
- char **origh = xmalloc(nr_heads * sizeof(*origh));
- int orig_nr_heads = nr_heads;
const struct ref *refs;
char *dest = xstrdup(transport->url);
struct fetch_pack_args args;
@@ -539,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,
args.depth = data->options.depth;
for (i = 0; i < nr_heads; i++)
- origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
+ heads[i] = xstrdup(to_fetch[i]->name);
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
@@ -559,9 +557,8 @@ static int fetch_refs_via_pack(struct transport *transport,
free_refs(refs_tmp);
- for (i = 0; i < orig_nr_heads; i++)
- free(origh[i]);
- free(origh);
+ for (i = 0; i < nr_heads; i++)
+ free(heads[i]);
free(heads);
free(dest);
return (refs ? 0 : -1);
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 16/17] fetch_pack(): free matching heads
2012-08-23 8:10 ` [PATCH 16/17] fetch_pack(): free matching heads mhagger
@ 2012-08-23 9:04 ` Jeff King
0 siblings, 0 replies; 40+ messages in thread
From: Jeff King @ 2012-08-23 9:04 UTC (permalink / raw)
To: mhagger; +Cc: Junio C Hamano, git
On Thu, Aug 23, 2012 at 10:10:41AM +0200, mhagger@alum.mit.edu wrote:
> From: Michael Haggerty <mhagger@alum.mit.edu>
>
> fetch_pack() used to delete entries from the input list (*nr_heads,
> heads) and drop them on the floor. (Even earlier versions dropped
> some names on the floor and modified others.) This forced
> fetch_refs_via_pack() to create a separate copy of the original list
> so that it could free the all of the names.
Minor typo: s/the all/all/.
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 17/17] fetch_refs(): simplify logic
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (15 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 16/17] fetch_pack(): free matching heads mhagger
@ 2012-08-23 8:10 ` mhagger
2012-08-23 9:07 ` Jeff King
2012-08-23 9:26 ` [PATCH 00/17] Clean up how fetch_pack() handles the heads list Jeff King
17 siblings, 1 reply; 40+ messages in thread
From: mhagger @ 2012-08-23 8:10 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Jeff King, git, Michael Haggerty
From: Michael Haggerty <mhagger@alum.mit.edu>
* Build linked list of return values as we go rather than recording
them in a temporary array and linking them up later.
* Handle ref in a single if...else statement in the main loop, to make
it clear that each ref has exactly two possible destinies.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/fetch-pack.c | 56 ++++++++++++++++++----------------------------------
1 file changed, 19 insertions(+), 37 deletions(-)
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 9650d04..90683ca 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -523,66 +523,48 @@ static void mark_recent_complete_commits(unsigned long cutoff)
static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
{
- struct ref **return_refs;
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
struct ref *ref, *next;
- int match_pos = 0, matched = 0, unmatched = 0;
-
- if (*nr_heads && !args.fetch_all)
- return_refs = xcalloc(*nr_heads, sizeof(struct ref *));
- else
- return_refs = NULL;
+ int match_pos = 0, unmatched = 0;
for (ref = *refs; ref; ref = next) {
+ int keep_ref = 0;
next = ref->next;
if (!memcmp(ref->name, "refs/", 5) &&
check_refname_format(ref->name, 0))
; /* trash */
else if (args.fetch_all &&
- (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
- *newtail = ref;
- ref->next = NULL;
- newtail = &ref->next;
- continue;
- }
- else {
- int cmp = -1;
+ (!args.depth || prefixcmp(ref->name, "refs/tags/")))
+ keep_ref = 1;
+ else
while (match_pos < *nr_heads) {
- cmp = strcmp(ref->name, heads[match_pos]);
- if (cmp < 0) /* definitely do not have it */
+ int cmp = strcmp(ref->name, heads[match_pos]);
+ if (cmp < 0) { /* definitely do not have it */
break;
- else if (cmp == 0) { /* definitely have it */
- return_refs[matched++] = ref;
+ } else if (cmp == 0) { /* definitely have it */
free(heads[match_pos++]);
+ keep_ref = 1;
break;
- }
- else { /* might have it; keep looking */
+ } else { /* might have it; keep looking */
heads[unmatched++] = heads[match_pos++];
}
}
- if (!cmp)
- continue; /* we will link it later */
- }
- free(ref);
- }
-
- if (!args.fetch_all) {
- int i;
- /* copy remaining unmatched heads: */
- while (match_pos < *nr_heads)
- heads[unmatched++] = heads[match_pos++];
- *nr_heads = unmatched;
-
- for (i = 0; i < matched; i++) {
- ref = return_refs[i];
+ if (keep_ref) {
*newtail = ref;
ref->next = NULL;
newtail = &ref->next;
+ } else {
+ free(ref);
}
- free(return_refs);
}
+
+ /* copy any remaining unmatched heads: */
+ while (match_pos < *nr_heads)
+ heads[unmatched++] = heads[match_pos++];
+ *nr_heads = unmatched;
+
*refs = newlist;
}
--
1.7.11.3
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 17/17] fetch_refs(): simplify logic
2012-08-23 8:10 ` [PATCH 17/17] fetch_refs(): simplify logic mhagger
@ 2012-08-23 9:07 ` Jeff King
2012-08-25 6:37 ` Michael Haggerty
0 siblings, 1 reply; 40+ messages in thread
From: Jeff King @ 2012-08-23 9:07 UTC (permalink / raw)
To: mhagger; +Cc: Junio C Hamano, git
On Thu, Aug 23, 2012 at 10:10:42AM +0200, mhagger@alum.mit.edu wrote:
> Subject: Re: [PATCH 17/17] fetch_refs(): simplify logic
> [...]
> static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
The subject should be "filter_refs", no?
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 17/17] fetch_refs(): simplify logic
2012-08-23 9:07 ` Jeff King
@ 2012-08-25 6:37 ` Michael Haggerty
0 siblings, 0 replies; 40+ messages in thread
From: Michael Haggerty @ 2012-08-25 6:37 UTC (permalink / raw)
To: Jeff King; +Cc: Junio C Hamano, git
On 08/23/2012 11:07 AM, Jeff King wrote:
> On Thu, Aug 23, 2012 at 10:10:42AM +0200, mhagger@alum.mit.edu wrote:
>
>> Subject: Re: [PATCH 17/17] fetch_refs(): simplify logic
>> [...]
>> static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
>
> The subject should be "filter_refs", no?
Yes, thanks. Also thanks for the typo correction in [PATCH 16/17], and
in general for your review of the patch series. Re-roll to follow shortly.
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 8:10 [PATCH 00/17] Clean up how fetch_pack() handles the heads list mhagger
` (16 preceding siblings ...)
2012-08-23 8:10 ` [PATCH 17/17] fetch_refs(): simplify logic mhagger
@ 2012-08-23 9:26 ` Jeff King
2012-08-23 19:13 ` Philip Oakley
17 siblings, 1 reply; 40+ messages in thread
From: Jeff King @ 2012-08-23 9:26 UTC (permalink / raw)
To: mhagger; +Cc: Junio C Hamano, git
On Thu, Aug 23, 2012 at 10:10:25AM +0200, mhagger@alum.mit.edu wrote:
> There were various confusing things (and a couple of bugs) in the way
> that fetch_pack() handled the list (nr_heads, heads) of references to
> be sought from the remote:
Aside from the minor comments I made to individual patches, this all
looks good. As usual, thanks for breaking it down; I wish all series
were as easy to review as this.
> I'm still suspicious about the logic related to args.fetch_all and
> args.depth, but I don't think I've made anything worse.
I think the point of that is that when doing "git fetch-pack --all
--depth=1", the meaning of "--all" is changed from "all refs" to
"everything but tags".
Which I kind of see the point of, because you don't want to grab ancient
tags that will be expensive. But wouldn't it make more sense to limit it
only to the contents of refs/heads in that case? Surely you wouldn't
want refs/notes, refs/remotes, or other hierarchies.
I suspect this code is never even run at all these days. All of the
callers inside git should actually provide a real list of refs, not
"--all". So it is really historical cruft for anybody who calls the
fetch-pack plumbing (I wonder if any third-party callers even exist;
this is such a deep part of the network infrastructure that any sane
scripts would probably just be calling fetch).
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 9:26 ` [PATCH 00/17] Clean up how fetch_pack() handles the heads list Jeff King
@ 2012-08-23 19:13 ` Philip Oakley
2012-08-23 19:56 ` Jeff King
0 siblings, 1 reply; 40+ messages in thread
From: Philip Oakley @ 2012-08-23 19:13 UTC (permalink / raw)
To: Jeff King, mhagger; +Cc: Junio C Hamano, git
From: "Jeff King" <peff@peff.net>
Sent: Thursday, August 23, 2012 10:26 AM
> On Thu, Aug 23, 2012 at 10:10:25AM +0200, mhagger@alum.mit.edu wrote:
>
>> There were various confusing things (and a couple of bugs) in the way
>> that fetch_pack() handled the list (nr_heads, heads) of references to
>> be sought from the remote:
>
> Aside from the minor comments I made to individual patches, this all
> looks good. As usual, thanks for breaking it down; I wish all series
> were as easy to review as this.
>
>> I'm still suspicious about the logic related to args.fetch_all and
>> args.depth, but I don't think I've made anything worse.
>
> I think the point of that is that when doing "git fetch-pack --all
> --depth=1", the meaning of "--all" is changed from "all refs" to
> "everything but tags".
>
There is a comment in \git\Documentation\technical\shallow.txt that
"- If you deepen a history, you'd want to get the tags of the
newly stored (but older!) commits. This does not work right now."
which may be the source of this restriction. That is, how is the depth
of the tag fetching to be restricted to the requested depth count?
[assuming I've undestood the problem correctly]
It may be (?) that it is a good time to think about a 'datedepth'
capability to bypass the current counted-depth shallow fetch that can
cause so much trouble. With a date limited depth the relevant tags could
also be fetched.
> Which I kind of see the point of, because you don't want to grab
> ancient
> tags that will be expensive. But wouldn't it make more sense to limit
> it
> only to the contents of refs/heads in that case? Surely you wouldn't
> want refs/notes, refs/remotes, or other hierarchies.
>
> I suspect this code is never even run at all these days. All of the
> callers inside git should actually provide a real list of refs, not
> "--all". So it is really historical cruft for anybody who calls the
> fetch-pack plumbing (I wonder if any third-party callers even exist;
> this is such a deep part of the network infrastructure that any sane
> scripts would probably just be calling fetch).
>
> -Peff
> --
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 19:13 ` Philip Oakley
@ 2012-08-23 19:56 ` Jeff King
2012-08-23 20:31 ` Jeff King
` (2 more replies)
0 siblings, 3 replies; 40+ messages in thread
From: Jeff King @ 2012-08-23 19:56 UTC (permalink / raw)
To: Philip Oakley; +Cc: mhagger, Junio C Hamano, git
On Thu, Aug 23, 2012 at 08:13:29PM +0100, Philip Oakley wrote:
> >>I'm still suspicious about the logic related to args.fetch_all and
> >>args.depth, but I don't think I've made anything worse.
> >
> >I think the point of that is that when doing "git fetch-pack --all
> >--depth=1", the meaning of "--all" is changed from "all refs" to
> >"everything but tags".
> >
>
> There is a comment in \git\Documentation\technical\shallow.txt that
> "- If you deepen a history, you'd want to get the tags of the
> newly stored (but older!) commits. This does not work right now."
> which may be the source of this restriction. That is, how is the depth
> of the tag fetching to be restricted to the requested depth count?
> [assuming I've undestood the problem correctly]
I don't think this is about deepening, but rather about making sure we
remain shallow for the initial fetch. Remember that this is on the
"fetch-pack --all" code path, which used to be used by "git clone" when
it was a shell script (these days, clone is a C builtin and will
actually feed the list of refs to fetch-pack).
This code blames back to:
commit 4bcb310c2539b66d535e87508d1b7a90fe29c083
Author: Alexandre Julliard <julliard@winehq.org>
Date: Fri Nov 24 16:00:13 2006 +0100
fetch-pack: Do not fetch tags for shallow clones.
A better fix may be to only fetch tags that point to commits that we
are downloading, but git-clone doesn't have support for following
tags. This will happen automatically on the next git-fetch though.
So it is about making sure that "git clone --depth=1" does not
accidentally pull a single commit from v1.0, v1.1, v1.2, and so forth,
losing the purpose of using --depth in the first place. These days it is
largely irrelevant, since this code path is not followed by clone, and
clone will automatically restrict its list of fetched refs to a single
branch if --depth is used.
The bug that shallow.txt talks about (and which is mentioned in that
commit message) is that we will not properly auto-follow tags during
such a clone (i.e., when we fetch a tag because it is pointing to a
commit that we already have or are already pulling). I'm not sure if
that is still the case or not. But assuming your workflow is something
like:
[make an initial, cheap clone]
git clone --depth=1 $repo
[the next day, you do a regular fetch, which will just get new stuff
on top of what you already have]
git fetch
Then that second fetch will auto-follow the tags, anyway. And that is
what the commit message is pointing: it's a bug, but one you can work
around.
> It may be (?) that it is a good time to think about a 'datedepth'
> capability to bypass the current counted-depth shallow fetch that can
> cause so much trouble. With a date limited depth the relevant tags
> could also be fetched.
I don't have anything against such an idea, but I think it is orthogonal
to the issue being discussed here.
-Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 19:56 ` Jeff King
@ 2012-08-23 20:31 ` Jeff King
2012-08-25 7:05 ` Michael Haggerty
2012-09-02 7:02 ` Michael Haggerty
2012-08-23 22:09 ` Philip Oakley
2012-08-24 4:23 ` Junio C Hamano
2 siblings, 2 replies; 40+ messages in thread
From: Jeff King @ 2012-08-23 20:31 UTC (permalink / raw)
To: Philip Oakley; +Cc: mhagger, Junio C Hamano, git
On Thu, Aug 23, 2012 at 03:56:48PM -0400, Jeff King wrote:
> This code blames back to:
>
> commit 4bcb310c2539b66d535e87508d1b7a90fe29c083
> Author: Alexandre Julliard <julliard@winehq.org>
> Date: Fri Nov 24 16:00:13 2006 +0100
>
> fetch-pack: Do not fetch tags for shallow clones.
>
> A better fix may be to only fetch tags that point to commits that we
> are downloading, but git-clone doesn't have support for following
> tags. This will happen automatically on the next git-fetch though.
>
> So it is about making sure that "git clone --depth=1" does not
> accidentally pull a single commit from v1.0, v1.1, v1.2, and so forth,
> losing the purpose of using --depth in the first place. These days it is
> largely irrelevant, since this code path is not followed by clone, and
> clone will automatically restrict its list of fetched refs to a single
> branch if --depth is used.
I think part of the confusion of this code is that inside the loop over
the refs it is sometimes checking aspects of the ref, and sometimes
checking invariants of the loop (like args.fetch_all). Splitting it into
separate loops makes it easier to see what is going on, like the patch
below (on top of Michael's series).
I'm not sure if it ends up being more readable, since the generic "cut
down this linked list" function has to operate through callbacks with a
void pointer. On the other hand, that function could also be used
elsewhere.
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 90683ca..877cf38 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -521,51 +521,80 @@ static void mark_recent_complete_commits(unsigned long cutoff)
}
}
-static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
+static void filter_refs_callback(struct ref **refs,
+ int (*want)(struct ref *, void *),
+ void *data)
{
- struct ref *newlist = NULL;
- struct ref **newtail = &newlist;
+ struct ref **tail = refs;
struct ref *ref, *next;
- int match_pos = 0, unmatched = 0;
for (ref = *refs; ref; ref = next) {
- int keep_ref = 0;
next = ref->next;
- if (!memcmp(ref->name, "refs/", 5) &&
- check_refname_format(ref->name, 0))
- ; /* trash */
- else if (args.fetch_all &&
- (!args.depth || prefixcmp(ref->name, "refs/tags/")))
- keep_ref = 1;
- else
- while (match_pos < *nr_heads) {
- int cmp = strcmp(ref->name, heads[match_pos]);
- if (cmp < 0) { /* definitely do not have it */
- break;
- } else if (cmp == 0) { /* definitely have it */
- free(heads[match_pos++]);
- keep_ref = 1;
- break;
- } else { /* might have it; keep looking */
- heads[unmatched++] = heads[match_pos++];
- }
- }
-
- if (keep_ref) {
- *newtail = ref;
- ref->next = NULL;
- newtail = &ref->next;
- } else {
+ if (want(ref, data))
+ tail = &ref->next;
+ else {
free(ref);
+ *tail = next;
}
}
+}
- /* copy any remaining unmatched heads: */
- while (match_pos < *nr_heads)
- heads[unmatched++] = heads[match_pos++];
- *nr_heads = unmatched;
+static int ref_name_is_ok(struct ref *ref, void *data)
+{
+ return memcmp(ref->name, "refs/", 5) ||
+ !check_refname_format(ref->name, 0);
+}
+
+static int ref_ok_for_shallow(struct ref *ref, void *data)
+{
+ return prefixcmp(ref->name, "refs/tags/");
+}
- *refs = newlist;
+struct filter_by_name_data {
+ char **heads;
+ int nr_heads;
+ int match_pos;
+ int unmatched;
+};
+
+static int want_ref_name(struct ref *ref, void *data)
+{
+ struct filter_by_name_data *f = data;
+
+ while (f->match_pos < f->nr_heads) {
+ int cmp = strcmp(ref->name, f->heads[f->match_pos]);
+ if (cmp < 0) /* definitely do not have it */
+ return 0;
+ else if (cmp == 0) { /* definitely have it */
+ free(f->heads[f->match_pos++]);
+ return 1;
+ } else /* might have it; keep looking */
+ f->heads[f->unmatched++] = f->heads[f->match_pos++];
+ }
+ return 0;
+}
+
+static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
+{
+ struct filter_by_name_data f;
+
+ filter_refs_callback(refs, ref_name_is_ok, NULL);
+
+ if (args.fetch_all) {
+ if (args.depth)
+ filter_refs_callback(refs, ref_ok_for_shallow, NULL);
+ return;
+ }
+
+ memset(&f, 0, sizeof(f));
+ f.heads = heads;
+ f.nr_heads = *nr_heads;
+ filter_refs_callback(refs, want_ref_name, &f);
+
+ /* copy any remaining unmatched heads: */
+ while (f.match_pos < f.nr_heads)
+ heads[f.unmatched++] = heads[f.match_pos++];
+ *nr_heads = f.unmatched;
}
static void mark_alternate_complete(const struct ref *ref, void *unused)
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 20:31 ` Jeff King
@ 2012-08-25 7:05 ` Michael Haggerty
2012-09-02 7:02 ` Michael Haggerty
1 sibling, 0 replies; 40+ messages in thread
From: Michael Haggerty @ 2012-08-25 7:05 UTC (permalink / raw)
To: Jeff King; +Cc: Philip Oakley, Junio C Hamano, git
[-- Attachment #1: Type: text/plain, Size: 1511 bytes --]
On 08/23/2012 10:31 PM, Jeff King wrote:
> I think part of the confusion of this code is that inside the loop over
> the refs it is sometimes checking aspects of the ref, and sometimes
> checking invariants of the loop (like args.fetch_all). Splitting it into
> separate loops makes it easier to see what is going on, like the patch
> below (on top of Michael's series).
>
> I'm not sure if it ends up being more readable, since the generic "cut
> down this linked list" function has to operate through callbacks with a
> void pointer. On the other hand, that function could also be used
> elsewhere.
> [...]
Despite requiring a bit more boilerplate, I think that your change makes
the logic clearer.
*If* we want to switch to using callbacks, then we can get even more
bang for the buck, as in the attached patch (which applies on top of my
patch v2). Beyond your suggestion, this patch:
* Inlines your filter_refs() into everything_local(), because (a) it's
short and (b) the policy work implemented there logically belongs
higher-up in the call chain.
* Renames your filter_refs_callback() to filter_refs().
* Moves the initialization of the filter_by_name_data structure
(including sorting and de-duping) all the way up to fetch_pack(), and
passes a filter_by_name_data* (rather than (nr_heads, heads)) down to
the callees.
If you like this change, let me know and I'll massage it into a
digestible patch series.
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
[-- Attachment #2: jk-filter-callback.patch --]
[-- Type: text/x-patch, Size: 6844 bytes --]
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index db77ee6..d1dabcf 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -521,51 +521,91 @@ static void mark_recent_complete_commits(unsigned long cutoff)
}
}
-static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
+static void filter_refs(struct ref **refs,
+ int (*want)(struct ref *, void *),
+ void *data)
{
- struct ref *newlist = NULL;
- struct ref **newtail = &newlist;
+ struct ref **tail = refs;
struct ref *ref, *next;
- int head_pos = 0, unmatched = 0;
for (ref = *refs; ref; ref = next) {
- int keep_ref = 0;
next = ref->next;
- if (!memcmp(ref->name, "refs/", 5) &&
- check_refname_format(ref->name, 0))
- ; /* trash */
- else if (args.fetch_all &&
- (!args.depth || prefixcmp(ref->name, "refs/tags/")))
- keep_ref = 1;
- else
- while (head_pos < *nr_heads) {
- int cmp = strcmp(ref->name, heads[head_pos]);
- if (cmp < 0) { /* definitely do not have it */
- break;
- } else if (cmp == 0) { /* definitely have it */
- free(heads[head_pos++]);
- keep_ref = 1;
- break;
- } else { /* might have it; keep looking */
- heads[unmatched++] = heads[head_pos++];
- }
- }
-
- if (keep_ref) {
- *newtail = ref;
- ref->next = NULL;
- newtail = &ref->next;
- } else {
+ if (want(ref, data))
+ tail = &ref->next;
+ else {
free(ref);
+ *tail = next;
}
}
+}
- /* copy any remaining unmatched heads: */
- while (head_pos < *nr_heads)
- heads[unmatched++] = heads[head_pos++];
- *nr_heads = unmatched;
+static int ref_name_is_ok(struct ref *ref, void *data)
+{
+ return memcmp(ref->name, "refs/", 5) ||
+ !check_refname_format(ref->name, 0);
+}
+
+static int ref_ok_for_shallow(struct ref *ref, void *data)
+{
+ return prefixcmp(ref->name, "refs/tags/");
+}
+
+static int compare_heads(const void *a, const void *b)
+{
+ return strcmp(*(const char **)a, *(const char **)b);
+}
+
+static int remove_duplicates(int nr_heads, char **heads)
+{
+ int src, dst;
+ for (src = dst = 1; src < nr_heads; src++)
+ if (strcmp(heads[src], heads[dst-1]))
+ heads[dst++] = heads[src];
+ else
+ free(heads[src]);
+ return dst;
+}
- *refs = newlist;
+struct filter_by_name_data {
+ char **heads;
+ int *nr_heads;
+ int head_pos;
+ int unmatched;
+};
+
+static void filter_by_name_init(struct filter_by_name_data *f,
+ int *nr_heads, char **heads)
+{
+ memset(f, 0, sizeof(*f));
+ f->heads = heads;
+ f->nr_heads = nr_heads;
+ qsort(f->heads, *f->nr_heads, sizeof(*f->heads), compare_heads);
+ *f->nr_heads = remove_duplicates(*f->nr_heads, f->heads);
+}
+
+static int filter_by_name_want(struct ref *ref, void *data)
+{
+ struct filter_by_name_data *f = data;
+
+ while (f->head_pos < *f->nr_heads) {
+ int cmp = strcmp(ref->name, f->heads[f->head_pos]);
+ if (cmp < 0) /* definitely do not have it */
+ return 0;
+ else if (cmp == 0) { /* definitely have it */
+ free(f->heads[f->head_pos++]);
+ return 1;
+ } else /* might have it; keep looking */
+ f->heads[f->unmatched++] = f->heads[f->head_pos++];
+ }
+ return 0;
+}
+
+static void filter_by_name_finish(struct filter_by_name_data *f)
+{
+ /* copy any remaining unmatched heads: */
+ while (f->head_pos < *f->nr_heads)
+ f->heads[f->unmatched++] = f->heads[f->head_pos++];
+ *f->nr_heads = f->unmatched;
}
static void mark_alternate_complete(const struct ref *ref, void *unused)
@@ -573,7 +613,8 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
mark_complete(NULL, ref->old_sha1, 0, NULL);
}
-static int everything_local(struct ref **refs, int *nr_heads, char **heads)
+static int everything_local(struct ref **refs,
+ struct filter_by_name_data *filter_by_name_data)
{
struct ref *ref;
int retval;
@@ -624,7 +665,14 @@ static int everything_local(struct ref **refs, int *nr_heads, char **heads)
}
}
- filter_refs(refs, nr_heads, heads);
+ filter_refs(refs, ref_name_is_ok, NULL);
+
+ if (args.fetch_all) {
+ if (args.depth)
+ filter_refs(refs, ref_ok_for_shallow, NULL);
+ } else if (filter_by_name_data) {
+ filter_refs(refs, filter_by_name_want, filter_by_name_data);
+ }
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
const unsigned char *remote = ref->old_sha1;
@@ -754,9 +802,9 @@ static int get_pack(int xd[2], char **pack_lockfile)
}
static struct ref *do_fetch_pack(int fd[2],
- const struct ref *orig_ref,
- int *nr_heads, char **heads,
- char **pack_lockfile)
+ const struct ref *orig_ref,
+ struct filter_by_name_data *filter_by_name_data,
+ char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
unsigned char sha1[20];
@@ -796,7 +844,7 @@ static struct ref *do_fetch_pack(int fd[2],
fprintf(stderr, "Server supports ofs-delta\n");
} else
prefer_ofs_delta = 0;
- if (everything_local(&ref, nr_heads, heads)) {
+ if (everything_local(&ref, filter_by_name_data)) {
packet_flush(fd[1]);
goto all_done;
}
@@ -816,21 +864,6 @@ static struct ref *do_fetch_pack(int fd[2],
return ref;
}
-static int remove_duplicates(int nr_heads, char **heads)
-{
- int src, dst;
-
- if (!nr_heads)
- return 0;
-
- for (src = dst = 1; src < nr_heads; src++)
- if (strcmp(heads[src], heads[dst-1]))
- heads[dst++] = heads[src];
- else
- free(heads[src]);
- return dst;
-}
-
static int fetch_pack_config(const char *var, const char *value, void *cb)
{
if (strcmp(var, "fetch.unpacklimit") == 0) {
@@ -1030,11 +1063,6 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
return ret;
}
-static int compare_heads(const void *a, const void *b)
-{
- return strcmp(*(const char **)a, *(const char **)b);
-}
-
struct ref *fetch_pack(struct fetch_pack_args *my_args,
int fd[], struct child_process *conn,
const struct ref *ref,
@@ -1045,6 +1073,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
{
struct stat st;
struct ref *ref_cpy;
+ struct filter_by_name_data name_data, *filter_by_name_data;
fetch_pack_setup();
if (&args != my_args)
@@ -1054,16 +1083,20 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
st.st_mtime = 0;
}
- if (heads && *nr_heads) {
- qsort(heads, *nr_heads, sizeof(*heads), compare_heads);
- *nr_heads = remove_duplicates(*nr_heads, heads);
- }
-
if (!ref) {
packet_flush(fd[1]);
die("no matching remote head");
}
- ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile);
+ if (heads && *nr_heads) {
+ filter_by_name_data = &name_data;
+ filter_by_name_init(filter_by_name_data, nr_heads, heads);
+ } else
+ filter_by_name_data = NULL;
+
+ ref_cpy = do_fetch_pack(fd, ref, filter_by_name_data, pack_lockfile);
+
+ if (filter_by_name_data)
+ filter_by_name_finish(filter_by_name_data);
if (args.depth > 0) {
struct cache_time mtime;
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 20:31 ` Jeff King
2012-08-25 7:05 ` Michael Haggerty
@ 2012-09-02 7:02 ` Michael Haggerty
1 sibling, 0 replies; 40+ messages in thread
From: Michael Haggerty @ 2012-09-02 7:02 UTC (permalink / raw)
To: Jeff King; +Cc: Philip Oakley, Junio C Hamano, git
On 08/23/2012 10:31 PM, Jeff King wrote:
> On Thu, Aug 23, 2012 at 03:56:48PM -0400, Jeff King wrote:
>
>> This code blames back to:
>>
>> commit 4bcb310c2539b66d535e87508d1b7a90fe29c083
>> Author: Alexandre Julliard <julliard@winehq.org>
>> Date: Fri Nov 24 16:00:13 2006 +0100
>>
>> fetch-pack: Do not fetch tags for shallow clones.
>>
>> A better fix may be to only fetch tags that point to commits that we
>> are downloading, but git-clone doesn't have support for following
>> tags. This will happen automatically on the next git-fetch though.
>>
>> So it is about making sure that "git clone --depth=1" does not
>> accidentally pull a single commit from v1.0, v1.1, v1.2, and so forth,
>> losing the purpose of using --depth in the first place. These days it is
>> largely irrelevant, since this code path is not followed by clone, and
>> clone will automatically restrict its list of fetched refs to a single
>> branch if --depth is used.
>
> I think part of the confusion of this code is that inside the loop over
> the refs it is sometimes checking aspects of the ref, and sometimes
> checking invariants of the loop (like args.fetch_all). Splitting it into
> separate loops makes it easier to see what is going on, like the patch
> below (on top of Michael's series).
>
> I'm not sure if it ends up being more readable, since the generic "cut
> down this linked list" function has to operate through callbacks with a
> void pointer. On the other hand, that function could also be used
> elsewhere.
>
> diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
> index 90683ca..877cf38 100644
> --- a/builtin/fetch-pack.c
> +++ b/builtin/fetch-pack.c
> @@ -521,51 +521,80 @@ static void mark_recent_complete_commits(unsigned long cutoff)
> }
> }
>
> -static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
> +static void filter_refs_callback(struct ref **refs,
> + int (*want)(struct ref *, void *),
> + void *data)
> {
> - struct ref *newlist = NULL;
> - struct ref **newtail = &newlist;
> + struct ref **tail = refs;
> struct ref *ref, *next;
> - int match_pos = 0, unmatched = 0;
>
> for (ref = *refs; ref; ref = next) {
> - int keep_ref = 0;
> next = ref->next;
> - if (!memcmp(ref->name, "refs/", 5) &&
> - check_refname_format(ref->name, 0))
> - ; /* trash */
> - else if (args.fetch_all &&
> - (!args.depth || prefixcmp(ref->name, "refs/tags/")))
> - keep_ref = 1;
> - else
> - while (match_pos < *nr_heads) {
> - int cmp = strcmp(ref->name, heads[match_pos]);
> - if (cmp < 0) { /* definitely do not have it */
> - break;
> - } else if (cmp == 0) { /* definitely have it */
> - free(heads[match_pos++]);
> - keep_ref = 1;
> - break;
> - } else { /* might have it; keep looking */
> - heads[unmatched++] = heads[match_pos++];
> - }
> - }
> -
> - if (keep_ref) {
> - *newtail = ref;
> - ref->next = NULL;
> - newtail = &ref->next;
> - } else {
> + if (want(ref, data))
> + tail = &ref->next;
> + else {
> free(ref);
> + *tail = next;
> }
> }
> +}
>
> - /* copy any remaining unmatched heads: */
> - while (match_pos < *nr_heads)
> - heads[unmatched++] = heads[match_pos++];
> - *nr_heads = unmatched;
> +static int ref_name_is_ok(struct ref *ref, void *data)
> +{
> + return memcmp(ref->name, "refs/", 5) ||
> + !check_refname_format(ref->name, 0);
> +}
> +
> +static int ref_ok_for_shallow(struct ref *ref, void *data)
> +{
> + return prefixcmp(ref->name, "refs/tags/");
> +}
>
> - *refs = newlist;
> +struct filter_by_name_data {
> + char **heads;
> + int nr_heads;
> + int match_pos;
> + int unmatched;
> +};
> +
> +static int want_ref_name(struct ref *ref, void *data)
> +{
> + struct filter_by_name_data *f = data;
> +
> + while (f->match_pos < f->nr_heads) {
> + int cmp = strcmp(ref->name, f->heads[f->match_pos]);
> + if (cmp < 0) /* definitely do not have it */
> + return 0;
> + else if (cmp == 0) { /* definitely have it */
> + free(f->heads[f->match_pos++]);
> + return 1;
> + } else /* might have it; keep looking */
> + f->heads[f->unmatched++] = f->heads[f->match_pos++];
> + }
> + return 0;
> +}
> +
> +static void filter_refs(struct ref **refs, int *nr_heads, char **heads)
> +{
> + struct filter_by_name_data f;
> +
> + filter_refs_callback(refs, ref_name_is_ok, NULL);
> +
> + if (args.fetch_all) {
> + if (args.depth)
> + filter_refs_callback(refs, ref_ok_for_shallow, NULL);
> + return;
> + }
> +
> + memset(&f, 0, sizeof(f));
> + f.heads = heads;
> + f.nr_heads = *nr_heads;
> + filter_refs_callback(refs, want_ref_name, &f);
> +
> + /* copy any remaining unmatched heads: */
> + while (f.match_pos < f.nr_heads)
> + heads[f.unmatched++] = heads[f.match_pos++];
> + *nr_heads = f.unmatched;
> }
>
> static void mark_alternate_complete(const struct ref *ref, void *unused)
>
I need a sanity check here. I don't see that it is forbidden to call
"git fetch-pack --all --depth=N" and also specify some explicit
references. (This usage would make it possible for the user to do a
shallow clone while also retrieving some specific tags.)
However, if I try to do this I get (before your change)
$ git fetch-pack -v --all --depth=1 $URL refs/heads/master
Server supports multi_ack_detailed
Server supports side-band-64k
Server supports ofs-delta
want 9c9f2f4453a7260d0d60926c73811f025be98ded (HEAD)
want 9c9f2f4453a7260d0d60926c73811f025be98ded (refs/heads/master)
done
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
error: no such remote ref refs/heads/master
9c9f2f4453a7260d0d60926c73811f025be98ded HEAD
9c9f2f4453a7260d0d60926c73811f025be98ded refs/heads/master
Note the error message "no such remote ref refs/heads/master", which is
incorrect. This happens because when the (fetch_all && (!depth ||
ref_non_tag(ref))) branch of the if statement succeeds, then the
look-in-heads-list branch is never executed, and therefore the requested
reference is never marked as having been found among the remote references.
On the other hand, if I try the same with an (existing) tag, before your
change I get
$ git fetch-pack -v --all --depth=1 $URL refs/tags/c
Server supports multi_ack_detailed
Server supports side-band-64k
Server supports ofs-delta
fatal: The remote end hung up unexpectedly
Now this is obviously broken in git-fetch-pack, because when fetch_all
is specified, return_refs is never initialized, but the
look-in-heads-list branch of the if is executed nevertheless.
It is not hard to fix these problems in the old code: (1)
unconditionally initialize return_refs, and (2) exchange the
look-in-heads-list handling and the fetch_all handling.
After your change the first problem (spurious error when asking for a
head) remains unchanged. The second problem is changed from a crash to
a misbehavior:
$ git fetch-pack -v --all --depth=1 $URL refs/tags/c
Initialized empty Git repository in /home/mhagger/tmp/cl/c/.git/
Server supports multi_ack_detailed
Server supports side-band-64k
Server supports ofs-delta
want 9c9f2f4453a7260d0d60926c73811f025be98ded (HEAD)
want 9c9f2f4453a7260d0d60926c73811f025be98ded (refs/heads/master)
done
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
error: no such remote ref refs/tags/c
9c9f2f4453a7260d0d60926c73811f025be98ded HEAD
9c9f2f4453a7260d0d60926c73811f025be98ded refs/heads/master
Note that "refs/tags/c" is not obtained from the remote even though it
was specifically requested. The reason that your patch makes the crash
disappear is that when fetch_all is true, then the look-in-heads-list
branch is not executed at all -- but this is also why "refs/tags/c" is
not obtained.
So, assuming that we want to support
git fetch-pack --all --depth=N $URL refs/tags/TAG
correctly, then your filter_refs_callback() refactoring won't work, at
least not the way that you have written it. I will continue working on
this.
Michael
--
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 19:56 ` Jeff King
2012-08-23 20:31 ` Jeff King
@ 2012-08-23 22:09 ` Philip Oakley
2012-08-24 4:23 ` Junio C Hamano
2 siblings, 0 replies; 40+ messages in thread
From: Philip Oakley @ 2012-08-23 22:09 UTC (permalink / raw)
To: Jeff King; +Cc: mhagger, Junio C Hamano, git
From: "Jeff King" <peff@peff.net>
Sent: Thursday, August 23, 2012 8:56 PM
> On Thu, Aug 23, 2012 at 08:13:29PM +0100, Philip Oakley wrote:
>
>> >>I'm still suspicious about the logic related to args.fetch_all and
>> >>args.depth, but I don't think I've made anything worse.
>> >
>> >I think the point of that is that when doing "git fetch-pack --all
>> >--depth=1", the meaning of "--all" is changed from "all refs" to
>> >"everything but tags".
>> >
>>
>> There is a comment in \git\Documentation\technical\shallow.txt that
>> "- If you deepen a history, you'd want to get the tags of the
>> newly stored (but older!) commits. This does not work right now."
>> which may be the source of this restriction. That is, how is the
>> depth
>> of the tag fetching to be restricted to the requested depth count?
>> [assuming I've undestood the problem correctly]
>
> I don't think this is about deepening, but rather about making sure we
> remain shallow for the initial fetch. Remember that this is on the
> "fetch-pack --all" code path, which used to be used by "git clone"
> when
> it was a shell script (these days, clone is a C builtin and will
> actually feed the list of refs to fetch-pack).
OK
>
> This code blames back to:
>
> commit 4bcb310c2539b66d535e87508d1b7a90fe29c083
> Author: Alexandre Julliard <julliard@winehq.org>
> Date: Fri Nov 24 16:00:13 2006 +0100
>
> fetch-pack: Do not fetch tags for shallow clones.
>
> A better fix may be to only fetch tags that point to commits that
> we
> are downloading, but git-clone doesn't have support for following
> tags. This will happen automatically on the next git-fetch though.
>
> So it is about making sure that "git clone --depth=1" does not
> accidentally pull a single commit from v1.0, v1.1, v1.2, and so forth,
> losing the purpose of using --depth in the first place. These days it
> is
> largely irrelevant, since this code path is not followed by clone, and
> clone will automatically restrict its list of fetched refs to a single
> branch if --depth is used.
>
> The bug that shallow.txt talks about (and which is mentioned in that
> commit message) is that we will not properly auto-follow tags during
> such a clone (i.e., when we fetch a tag because it is pointing to a
> commit that we already have or are already pulling). I'm not sure if
> that is still the case or not. But assuming your workflow is something
> like:
>
> [make an initial, cheap clone]
> git clone --depth=1 $repo
>
> [the next day, you do a regular fetch, which will just get new stuff
> on top of what you already have]
> git fetch
>
> Then that second fetch will auto-follow the tags, anyway. And that is
> what the commit message is pointing: it's a bug, but one you can work
> around.
I hadn't appreciated that the fetch would limit itself to the original
shallow
depth. I'd gained the impression that one need to use the --depth to
control what was being fetched.
>
>> It may be (?) that it is a good time to think about a 'datedepth'
>> capability to bypass the current counted-depth shallow fetch that can
>> cause so much trouble. With a date limited depth the relevant tags
>> could also be fetched.
>
> I don't have anything against such an idea, but I think it is
> orthogonal
> to the issue being discussed here.
OK. I'll stick it on my slow-burner pile ;-)
>
> -Peff
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-23 19:56 ` Jeff King
2012-08-23 20:31 ` Jeff King
2012-08-23 22:09 ` Philip Oakley
@ 2012-08-24 4:23 ` Junio C Hamano
2012-08-24 12:46 ` Philip Oakley
2 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2012-08-24 4:23 UTC (permalink / raw)
To: Philip Oakley, Jeff King; +Cc: mhagger, git
Jeff King <peff@peff.net> writes:
>> It may be (?) that it is a good time to think about a 'datedepth'
>> capability to bypass the current counted-depth shallow fetch that can
>> cause so much trouble. With a date limited depth the relevant tags
>> could also be fetched.
>
> I don't have anything against such an idea, but I think it is orthogonal
> to the issue being discussed here.
Correct.
The biggest problem with the "shallow" hack is that the deepening
fetch counts from the tip of the refs at the time of deepening when
deepening the history (i.e. "clone --depth", followed by number of
"fetch", followed by "fetch --depth"). If you start from a shallow
clone of depth 1, repeated fetch to keep up while the history grew
by 100, you would still have a connected history down to the initial
cauterization point, and "fetch --depth=200" would give you a
history that is deeper than your original clone by 100 commits. But
if you start from the same shallow clone of depth 1, did not do
anything while the history grew by 100, and then decide to fetch
again with "fetch --depth=20", it does not grow. It just makes
20-commit deep history from the updated tip, and leave the commit
from your original clone dangling.
The problem with "depth" does not have anything to do with how it is
specified at the UI level. The end-user input is used to find out
at what set of commits the history is cauterized, and once they are
computed, the "shallow" logic solely works on "is the history before
these cauterization points, or after, in topological terms." (and it
has to be that way to make sure we get reproducible results). Even
if a new way to specify these cauterization points in terms of date
is introduced, it does not change anything and does not solve the
fundamental problem the code has when deepening.
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 00/17] Clean up how fetch_pack() handles the heads list
2012-08-24 4:23 ` Junio C Hamano
@ 2012-08-24 12:46 ` Philip Oakley
0 siblings, 0 replies; 40+ messages in thread
From: Philip Oakley @ 2012-08-24 12:46 UTC (permalink / raw)
To: Junio C Hamano, Jeff King; +Cc: mhagger, git
From: "Junio C Hamano" <gitster@pobox.com>
Sent: Friday, August 24, 2012 5:23 AM
> Jeff King <peff@peff.net> writes:
>
>>> It may be (?) that it is a good time to think about a 'datedepth'
>>> capability to bypass the current counted-depth shallow fetch that
>>> can
>>> cause so much trouble. With a date limited depth the relevant tags
>>> could also be fetched.
>>
>> I don't have anything against such an idea, but I think it is
>> orthogonal
>> to the issue being discussed here.
>
> Correct.
>
> The biggest problem with the "shallow" hack is that the deepening
> fetch counts from the tip of the refs at the time of deepening when
> deepening the history (i.e. "clone --depth", followed by number of
> "fetch", followed by "fetch --depth"). If you start from a shallow
> clone of depth 1, repeated fetch to keep up while the history grew
> by 100, you would still have a connected history down to the initial
> cauterization point, and "fetch --depth=200" would give you a
> history that is deeper than your original clone by 100 commits. But
> if you start from the same shallow clone of depth 1, did not do
> anything while the history grew by 100, and then decide to fetch
> again with "fetch --depth=20", it does not grow. It just makes
> 20-commit deep history from the updated tip, and leave the commit
> from your original clone dangling.
>
> The problem with "depth" does not have anything to do with how it is
> specified at the UI level.
That is, correct me if I'm wrong, the server currently lacks a
capability to provide anything other than the counted depth shallow
response (if available). Hence my comment about needing an additional
server side capability, though that would need a well thought out set of
use cases to make it useful.
> The end-user input is used to
> find out
> at what set of commits the history is cauterized, and once they are
> computed, the "shallow" logic solely works on "is the history before
> these cauterization points, or after, in topological terms." (and it
> has to be that way to make sure we get reproducible results). Even
> if a new way to specify these cauterization points in terms of date
> is introduced, it does not change anything and does not solve the
> fundamental problem the code has when deepening.
fundamental problem = no server capability other than counted depth.
>
^ permalink raw reply [flat|nested] 40+ messages in thread