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
This commit is contained in:
commit
f5f6cb87de
@ -20,6 +20,7 @@ SYNOPSIS
|
|||||||
[ \--not ]
|
[ \--not ]
|
||||||
[ \--all ]
|
[ \--all ]
|
||||||
[ \--stdin ]
|
[ \--stdin ]
|
||||||
|
[ \--quiet ]
|
||||||
[ \--topo-order ]
|
[ \--topo-order ]
|
||||||
[ \--parents ]
|
[ \--parents ]
|
||||||
[ \--timestamp ]
|
[ \--timestamp ]
|
||||||
@ -270,6 +271,14 @@ limiting may be applied.
|
|||||||
In addition to the '<commit>' listed on the command
|
In addition to the '<commit>' listed on the command
|
||||||
line, read them from the standard input.
|
line, read them from the standard input.
|
||||||
|
|
||||||
|
--quiet::
|
||||||
|
|
||||||
|
Don't print anything to standard output. This form of
|
||||||
|
git-rev-list is primarly meant to allow the caller to
|
||||||
|
test the exit status to see if a range of objects is fully
|
||||||
|
connected (or not). It is faster than redirecting stdout
|
||||||
|
to /dev/null as the output does not have to be formatted.
|
||||||
|
|
||||||
--cherry-pick::
|
--cherry-pick::
|
||||||
|
|
||||||
Omit any commit that introduces the same change as
|
Omit any commit that introduces the same change as
|
||||||
|
@ -8,10 +8,12 @@
|
|||||||
#include "path-list.h"
|
#include "path-list.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
#include "run-command.h"
|
||||||
|
|
||||||
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>...]";
|
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>...]";
|
||||||
|
|
||||||
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
|
static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
|
||||||
|
static const char *depth;
|
||||||
static char *default_rla = NULL;
|
static char *default_rla = NULL;
|
||||||
static struct transport *transport;
|
static struct transport *transport;
|
||||||
|
|
||||||
@ -335,9 +337,72 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We would want to bypass the object transfer altogether if
|
||||||
|
* everything we are going to fetch already exists and connected
|
||||||
|
* locally.
|
||||||
|
*
|
||||||
|
* The refs we are going to fetch are in to_fetch (nr_heads in
|
||||||
|
* total). If running
|
||||||
|
*
|
||||||
|
* $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
|
||||||
|
*
|
||||||
|
* does not error out, that means everything reachable from the
|
||||||
|
* refs we are going to fetch exists and is connected to some of
|
||||||
|
* our existing refs.
|
||||||
|
*/
|
||||||
|
static int quickfetch(struct ref *ref_map)
|
||||||
|
{
|
||||||
|
struct child_process revlist;
|
||||||
|
struct ref *ref;
|
||||||
|
char **argv;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are deepening a shallow clone we already have these
|
||||||
|
* objects reachable. Running rev-list here will return with
|
||||||
|
* a good (0) exit status and we'll bypass the fetch that we
|
||||||
|
* really need to perform. Claiming failure now will ensure
|
||||||
|
* we perform the network exchange to deepen our history.
|
||||||
|
*/
|
||||||
|
if (depth)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0, ref = ref_map; ref; ref = ref->next)
|
||||||
|
i++;
|
||||||
|
if (!i)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
argv = xmalloc(sizeof(*argv) * (i + 6));
|
||||||
|
i = 0;
|
||||||
|
argv[i++] = xstrdup("rev-list");
|
||||||
|
argv[i++] = xstrdup("--quiet");
|
||||||
|
argv[i++] = xstrdup("--objects");
|
||||||
|
for (ref = ref_map; ref; ref = ref->next)
|
||||||
|
argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1));
|
||||||
|
argv[i++] = xstrdup("--not");
|
||||||
|
argv[i++] = xstrdup("--all");
|
||||||
|
argv[i++] = NULL;
|
||||||
|
|
||||||
|
memset(&revlist, 0, sizeof(revlist));
|
||||||
|
revlist.argv = (const char**)argv;
|
||||||
|
revlist.git_cmd = 1;
|
||||||
|
revlist.no_stdin = 1;
|
||||||
|
revlist.no_stdout = 1;
|
||||||
|
revlist.no_stderr = 1;
|
||||||
|
err = run_command(&revlist);
|
||||||
|
|
||||||
|
for (i = 0; argv[i]; i++)
|
||||||
|
free(argv[i]);
|
||||||
|
free(argv);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int fetch_refs(struct transport *transport, struct ref *ref_map)
|
static int fetch_refs(struct transport *transport, struct ref *ref_map)
|
||||||
{
|
{
|
||||||
int ret = transport_fetch_refs(transport, ref_map);
|
int ret = quickfetch(ref_map);
|
||||||
|
if (ret)
|
||||||
|
ret = transport_fetch_refs(transport, ref_map);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
store_updated_refs(transport->url, ref_map);
|
store_updated_refs(transport->url, ref_map);
|
||||||
transport_unlock_pack(transport);
|
transport_unlock_pack(transport);
|
||||||
@ -389,7 +454,7 @@ static struct ref *find_non_local_tags(struct transport *transport,
|
|||||||
|
|
||||||
if (!path_list_has_path(&existing_refs, ref_name) &&
|
if (!path_list_has_path(&existing_refs, ref_name) &&
|
||||||
!path_list_has_path(&new_refs, ref_name) &&
|
!path_list_has_path(&new_refs, ref_name) &&
|
||||||
lookup_object(ref->old_sha1)) {
|
has_sha1_file(ref->old_sha1)) {
|
||||||
path_list_insert(ref_name, &new_refs);
|
path_list_insert(ref_name, &new_refs);
|
||||||
|
|
||||||
rm = alloc_ref(strlen(ref_name) + 1);
|
rm = alloc_ref(strlen(ref_name) + 1);
|
||||||
@ -473,7 +538,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||||||
static const char **refs = NULL;
|
static const char **refs = NULL;
|
||||||
int ref_nr = 0;
|
int ref_nr = 0;
|
||||||
int cmd_len = 0;
|
int cmd_len = 0;
|
||||||
const char *depth = NULL, *upload_pack = NULL;
|
const char *upload_pack = NULL;
|
||||||
int keep = 0;
|
int keep = 0;
|
||||||
|
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
|
@ -26,6 +26,7 @@ static const char rev_list_usage[] =
|
|||||||
" --remove-empty\n"
|
" --remove-empty\n"
|
||||||
" --all\n"
|
" --all\n"
|
||||||
" --stdin\n"
|
" --stdin\n"
|
||||||
|
" --quiet\n"
|
||||||
" ordering output:\n"
|
" ordering output:\n"
|
||||||
" --topo-order\n"
|
" --topo-order\n"
|
||||||
" --date-order\n"
|
" --date-order\n"
|
||||||
@ -50,6 +51,7 @@ static int show_timestamp;
|
|||||||
static int hdr_termination;
|
static int hdr_termination;
|
||||||
static const char *header_prefix;
|
static const char *header_prefix;
|
||||||
|
|
||||||
|
static void finish_commit(struct commit *commit);
|
||||||
static void show_commit(struct commit *commit)
|
static void show_commit(struct commit *commit)
|
||||||
{
|
{
|
||||||
if (show_timestamp)
|
if (show_timestamp)
|
||||||
@ -93,6 +95,11 @@ static void show_commit(struct commit *commit)
|
|||||||
strbuf_release(&buf);
|
strbuf_release(&buf);
|
||||||
}
|
}
|
||||||
maybe_flush_or_die(stdout, "stdout");
|
maybe_flush_or_die(stdout, "stdout");
|
||||||
|
finish_commit(commit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void finish_commit(struct commit *commit)
|
||||||
|
{
|
||||||
if (commit->parents) {
|
if (commit->parents) {
|
||||||
free_commit_list(commit->parents);
|
free_commit_list(commit->parents);
|
||||||
commit->parents = NULL;
|
commit->parents = NULL;
|
||||||
@ -101,6 +108,12 @@ static void show_commit(struct commit *commit)
|
|||||||
commit->buffer = NULL;
|
commit->buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void finish_object(struct object_array_entry *p)
|
||||||
|
{
|
||||||
|
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
|
||||||
|
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
|
||||||
|
}
|
||||||
|
|
||||||
static void show_object(struct object_array_entry *p)
|
static void show_object(struct object_array_entry *p)
|
||||||
{
|
{
|
||||||
/* An object with name "foo\n0000000..." can be used to
|
/* An object with name "foo\n0000000..." can be used to
|
||||||
@ -108,9 +121,7 @@ static void show_object(struct object_array_entry *p)
|
|||||||
*/
|
*/
|
||||||
const char *ep = strchr(p->name, '\n');
|
const char *ep = strchr(p->name, '\n');
|
||||||
|
|
||||||
if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
|
finish_object(p);
|
||||||
die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
|
|
||||||
|
|
||||||
if (ep) {
|
if (ep) {
|
||||||
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
|
printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
|
||||||
(int) (ep - p->name),
|
(int) (ep - p->name),
|
||||||
@ -527,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||||||
int read_from_stdin = 0;
|
int read_from_stdin = 0;
|
||||||
int bisect_show_vars = 0;
|
int bisect_show_vars = 0;
|
||||||
int bisect_find_all = 0;
|
int bisect_find_all = 0;
|
||||||
|
int quiet = 0;
|
||||||
|
|
||||||
git_config(git_default_config);
|
git_config(git_default_config);
|
||||||
init_revisions(&revs, prefix);
|
init_revisions(&revs, prefix);
|
||||||
@ -565,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||||||
read_revisions_from_stdin(&revs);
|
read_revisions_from_stdin(&revs);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(arg, "--quiet")) {
|
||||||
|
quiet = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
usage(rev_list_usage);
|
usage(rev_list_usage);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -640,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse_commit_list(&revs, show_commit, show_object);
|
traverse_commit_list(&revs,
|
||||||
|
quiet ? finish_commit : show_commit,
|
||||||
|
quiet ? finish_object : show_object);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ int start_command(struct child_process *cmd)
|
|||||||
cmd->close_out = 1;
|
cmd->close_out = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
need_err = cmd->err < 0;
|
need_err = !cmd->no_stderr && cmd->err < 0;
|
||||||
if (need_err) {
|
if (need_err) {
|
||||||
if (pipe(fderr) < 0) {
|
if (pipe(fderr) < 0) {
|
||||||
if (need_in)
|
if (need_in)
|
||||||
@ -87,7 +87,9 @@ int start_command(struct child_process *cmd)
|
|||||||
close(cmd->out);
|
close(cmd->out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (need_err) {
|
if (cmd->no_stderr)
|
||||||
|
dup_devnull(2);
|
||||||
|
else if (need_err) {
|
||||||
dup2(fderr[1], 2);
|
dup2(fderr[1], 2);
|
||||||
close_pair(fderr);
|
close_pair(fderr);
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ struct child_process {
|
|||||||
unsigned close_out:1;
|
unsigned close_out:1;
|
||||||
unsigned no_stdin:1;
|
unsigned no_stdin:1;
|
||||||
unsigned no_stdout:1;
|
unsigned no_stdout:1;
|
||||||
|
unsigned no_stderr:1;
|
||||||
unsigned git_cmd:1; /* if this is to be git sub-command */
|
unsigned git_cmd:1; /* if this is to be git sub-command */
|
||||||
unsigned stdout_to_stderr:1;
|
unsigned stdout_to_stderr:1;
|
||||||
};
|
};
|
||||||
|
@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' '
|
|||||||
|
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'quickfetch should not copy from alternate' '
|
||||||
|
|
||||||
|
(
|
||||||
|
mkdir quickclone &&
|
||||||
|
cd quickclone &&
|
||||||
|
git init-db &&
|
||||||
|
(cd ../.git/objects && pwd) >.git/objects/info/alternates &&
|
||||||
|
git remote add origin .. &&
|
||||||
|
git fetch -k -k
|
||||||
|
) &&
|
||||||
|
obj_cnt=$( (
|
||||||
|
cd quickclone &&
|
||||||
|
git count-objects | sed -e "s/ *objects,.*//"
|
||||||
|
) ) &&
|
||||||
|
pck_cnt=$( (
|
||||||
|
cd quickclone &&
|
||||||
|
git count-objects -v | sed -n -e "/packs:/{
|
||||||
|
s/packs://
|
||||||
|
p
|
||||||
|
q
|
||||||
|
}"
|
||||||
|
) ) &&
|
||||||
|
origin_master=$( (
|
||||||
|
cd quickclone &&
|
||||||
|
git rev-parse origin/master
|
||||||
|
) ) &&
|
||||||
|
echo "loose objects: $obj_cnt, packfiles: $pck_cnt" &&
|
||||||
|
test $obj_cnt -eq 0 &&
|
||||||
|
test $pck_cnt -eq 0 &&
|
||||||
|
test z$origin_master = z$(git rev-parse master)
|
||||||
|
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user