Skip to content

Commit f5f6cb8

Browse files
committed
Merge branch 'sp/fetch-fix'
* sp/fetch-fix: git-fetch: avoid local fetching from alternate (again) rev-list: Introduce --quiet to avoid /dev/null redirects run-command: Support sending stderr to /dev/null git-fetch: Always fetch tags if the object they reference exists
2 parents b2e1632 + 4191c35 commit f5f6cb8

File tree

6 files changed

+137
-9
lines changed

6 files changed

+137
-9
lines changed

Documentation/git-rev-list.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ SYNOPSIS
2020
[ \--not ]
2121
[ \--all ]
2222
[ \--stdin ]
23+
[ \--quiet ]
2324
[ \--topo-order ]
2425
[ \--parents ]
2526
[ \--timestamp ]
@@ -270,6 +271,14 @@ limiting may be applied.
270271
In addition to the '<commit>' listed on the command
271272
line, read them from the standard input.
272273

274+
--quiet::
275+
276+
Don't print anything to standard output. This form of
277+
git-rev-list is primarly meant to allow the caller to
278+
test the exit status to see if a range of objects is fully
279+
connected (or not). It is faster than redirecting stdout
280+
to /dev/null as the output does not have to be formatted.
281+
273282
--cherry-pick::
274283

275284
Omit any commit that introduces the same change as

builtin-fetch.c

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
#include "path-list.h"
99
#include "remote.h"
1010
#include "transport.h"
11+
#include "run-command.h"
1112

1213
static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]";
1314

1415
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
16+
static const char *depth;
1517
static char *default_rla = NULL;
1618
static struct transport *transport;
1719

@@ -335,9 +337,72 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
335337
fclose(fp);
336338
}
337339

340+
/*
341+
* We would want to bypass the object transfer altogether if
342+
* everything we are going to fetch already exists and connected
343+
* locally.
344+
*
345+
* The refs we are going to fetch are in to_fetch (nr_heads in
346+
* total). If running
347+
*
348+
* $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
349+
*
350+
* does not error out, that means everything reachable from the
351+
* refs we are going to fetch exists and is connected to some of
352+
* our existing refs.
353+
*/
354+
static int quickfetch(struct ref *ref_map)
355+
{
356+
struct child_process revlist;
357+
struct ref *ref;
358+
char **argv;
359+
int i, err;
360+
361+
/*
362+
* If we are deepening a shallow clone we already have these
363+
* objects reachable. Running rev-list here will return with
364+
* a good (0) exit status and we'll bypass the fetch that we
365+
* really need to perform. Claiming failure now will ensure
366+
* we perform the network exchange to deepen our history.
367+
*/
368+
if (depth)
369+
return -1;
370+
371+
for (i = 0, ref = ref_map; ref; ref = ref->next)
372+
i++;
373+
if (!i)
374+
return 0;
375+
376+
argv = xmalloc(sizeof(*argv) * (i + 6));
377+
i = 0;
378+
argv[i++] = xstrdup("rev-list");
379+
argv[i++] = xstrdup("--quiet");
380+
argv[i++] = xstrdup("--objects");
381+
for (ref = ref_map; ref; ref = ref->next)
382+
argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1));
383+
argv[i++] = xstrdup("--not");
384+
argv[i++] = xstrdup("--all");
385+
argv[i++] = NULL;
386+
387+
memset(&revlist, 0, sizeof(revlist));
388+
revlist.argv = (const char**)argv;
389+
revlist.git_cmd = 1;
390+
revlist.no_stdin = 1;
391+
revlist.no_stdout = 1;
392+
revlist.no_stderr = 1;
393+
err = run_command(&revlist);
394+
395+
for (i = 0; argv[i]; i++)
396+
free(argv[i]);
397+
free(argv);
398+
return err;
399+
}
400+
338401
static int fetch_refs(struct transport *transport, struct ref *ref_map)
339402
{
340-
int ret = transport_fetch_refs(transport, ref_map);
403+
int ret = quickfetch(ref_map);
404+
if (ret)
405+
ret = transport_fetch_refs(transport, ref_map);
341406
if (!ret)
342407
store_updated_refs(transport->url, ref_map);
343408
transport_unlock_pack(transport);
@@ -389,7 +454,7 @@ static struct ref *find_non_local_tags(struct transport *transport,
389454

390455
if (!path_list_has_path(&existing_refs, ref_name) &&
391456
!path_list_has_path(&new_refs, ref_name) &&
392-
lookup_object(ref->old_sha1)) {
457+
has_sha1_file(ref->old_sha1)) {
393458
path_list_insert(ref_name, &new_refs);
394459

395460
rm = alloc_ref(strlen(ref_name) + 1);
@@ -473,7 +538,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
473538
static const char **refs = NULL;
474539
int ref_nr = 0;
475540
int cmd_len = 0;
476-
const char *depth = NULL, *upload_pack = NULL;
541+
const char *upload_pack = NULL;
477542
int keep = 0;
478543

479544
for (i = 1; i < argc; i++) {

builtin-rev-list.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static const char rev_list_usage[] =
2626
" --remove-empty\n"
2727
" --all\n"
2828
" --stdin\n"
29+
" --quiet\n"
2930
" ordering output:\n"
3031
" --topo-order\n"
3132
" --date-order\n"
@@ -50,6 +51,7 @@ static int show_timestamp;
5051
static int hdr_termination;
5152
static const char *header_prefix;
5253

54+
static void finish_commit(struct commit *commit);
5355
static void show_commit(struct commit *commit)
5456
{
5557
if (show_timestamp)
@@ -93,6 +95,11 @@ static void show_commit(struct commit *commit)
9395
strbuf_release(&buf);
9496
}
9597
maybe_flush_or_die(stdout, "stdout");
98+
finish_commit(commit);
99+
}
100+
101+
static void finish_commit(struct commit *commit)
102+
{
96103
if (commit->parents) {
97104
free_commit_list(commit->parents);
98105
commit->parents = NULL;
@@ -101,16 +108,20 @@ static void show_commit(struct commit *commit)
101108
commit->buffer = NULL;
102109
}
103110

111+
static void finish_object(struct object_array_entry *p)
112+
{
113+
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
114+
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
115+
}
116+
104117
static void show_object(struct object_array_entry *p)
105118
{
106119
/* An object with name "foo\n0000000..." can be used to
107120
* confuse downstream git-pack-objects very badly.
108121
*/
109122
const char *ep = strchr(p->name, '\n');
110123

111-
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
112-
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
113-
124+
finish_object(p);
114125
if (ep) {
115126
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
116127
(int) (ep - p->name),
@@ -527,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
527538
int read_from_stdin = 0;
528539
int bisect_show_vars = 0;
529540
int bisect_find_all = 0;
541+
int quiet = 0;
530542

531543
git_config(git_default_config);
532544
init_revisions(&revs, prefix);
@@ -565,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
565577
read_revisions_from_stdin(&revs);
566578
continue;
567579
}
580+
if (!strcmp(arg, "--quiet")) {
581+
quiet = 1;
582+
continue;
583+
}
568584
usage(rev_list_usage);
569585

570586
}
@@ -640,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
640656
}
641657
}
642658

643-
traverse_commit_list(&revs, show_commit, show_object);
659+
traverse_commit_list(&revs,
660+
quiet ? finish_commit : show_commit,
661+
quiet ? finish_object : show_object);
644662

645663
return 0;
646664
}

run-command.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ int start_command(struct child_process *cmd)
4141
cmd->close_out = 1;
4242
}
4343

44-
need_err = cmd->err < 0;
44+
need_err = !cmd->no_stderr && cmd->err < 0;
4545
if (need_err) {
4646
if (pipe(fderr) < 0) {
4747
if (need_in)
@@ -87,7 +87,9 @@ int start_command(struct child_process *cmd)
8787
close(cmd->out);
8888
}
8989

90-
if (need_err) {
90+
if (cmd->no_stderr)
91+
dup_devnull(2);
92+
else if (need_err) {
9193
dup2(fderr[1], 2);
9294
close_pair(fderr);
9395
}

run-command.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ struct child_process {
2323
unsigned close_out:1;
2424
unsigned no_stdin:1;
2525
unsigned no_stdout:1;
26+
unsigned no_stderr:1;
2627
unsigned git_cmd:1; /* if this is to be git sub-command */
2728
unsigned stdout_to_stderr:1;
2829
};

t/t5502-quickfetch.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' '
8686
8787
'
8888

89+
test_expect_success 'quickfetch should not copy from alternate' '
90+
91+
(
92+
mkdir quickclone &&
93+
cd quickclone &&
94+
git init-db &&
95+
(cd ../.git/objects && pwd) >.git/objects/info/alternates &&
96+
git remote add origin .. &&
97+
git fetch -k -k
98+
) &&
99+
obj_cnt=$( (
100+
cd quickclone &&
101+
git count-objects | sed -e "s/ *objects,.*//"
102+
) ) &&
103+
pck_cnt=$( (
104+
cd quickclone &&
105+
git count-objects -v | sed -n -e "/packs:/{
106+
s/packs://
107+
p
108+
q
109+
}"
110+
) ) &&
111+
origin_master=$( (
112+
cd quickclone &&
113+
git rev-parse origin/master
114+
) ) &&
115+
echo "loose objects: $obj_cnt, packfiles: $pck_cnt" &&
116+
test $obj_cnt -eq 0 &&
117+
test $pck_cnt -eq 0 &&
118+
test z$origin_master = z$(git rev-parse master)
119+
120+
'
121+
89122
test_done

0 commit comments

Comments
 (0)