summaryrefslogtreecommitdiff
path: root/fetch-pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'fetch-pack.c')
-rw-r--r--fetch-pack.c280
1 files changed, 221 insertions, 59 deletions
diff --git a/fetch-pack.c b/fetch-pack.c
index 1734a573b0..d467edc24e 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -15,13 +15,14 @@
#include "connect.h"
#include "transport.h"
#include "version.h"
-#include "sha1-array.h"
+#include "oid-array.h"
#include "oidset.h"
#include "packfile.h"
#include "object-store.h"
#include "connected.h"
#include "fetch-negotiator.h"
#include "fsck.h"
+#include "shallow.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -34,9 +35,10 @@ static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int agent_supported;
static int server_supports_filtering;
-static struct lock_file shallow_lock;
+static struct shallow_lock shallow_lock;
static const char *alternate_shallow_file;
static struct strbuf fsck_msg_types = STRBUF_INIT;
+static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
/* Remember to update object flag allocation in object.h */
#define COMPLETE (1U << 0)
@@ -793,7 +795,8 @@ static void write_promisor_file(const char *keep_name,
}
static int get_pack(struct fetch_pack_args *args,
- int xd[2], char **pack_lockfile,
+ int xd[2], struct string_list *pack_lockfiles,
+ int only_packfile,
struct ref **sought, int nr_sought)
{
struct async demux;
@@ -832,50 +835,60 @@ static int get_pack(struct fetch_pack_args *args,
}
if (alternate_shallow_file) {
- argv_array_push(&cmd.args, "--shallow-file");
- argv_array_push(&cmd.args, alternate_shallow_file);
+ strvec_push(&cmd.args, "--shallow-file");
+ strvec_push(&cmd.args, alternate_shallow_file);
}
if (do_keep || args->from_promisor) {
- if (pack_lockfile)
+ if (pack_lockfiles)
cmd.out = -1;
cmd_name = "index-pack";
- argv_array_push(&cmd.args, cmd_name);
- argv_array_push(&cmd.args, "--stdin");
+ strvec_push(&cmd.args, cmd_name);
+ strvec_push(&cmd.args, "--stdin");
if (!args->quiet && !args->no_progress)
- argv_array_push(&cmd.args, "-v");
+ strvec_push(&cmd.args, "-v");
if (args->use_thin_pack)
- argv_array_push(&cmd.args, "--fix-thin");
+ strvec_push(&cmd.args, "--fix-thin");
if (do_keep && (args->lock_pack || unpack_limit)) {
char hostname[HOST_NAME_MAX + 1];
if (xgethostname(hostname, sizeof(hostname)))
xsnprintf(hostname, sizeof(hostname), "localhost");
- argv_array_pushf(&cmd.args,
- "--keep=fetch-pack %"PRIuMAX " on %s",
- (uintmax_t)getpid(), hostname);
+ strvec_pushf(&cmd.args,
+ "--keep=fetch-pack %"PRIuMAX " on %s",
+ (uintmax_t)getpid(), hostname);
}
- if (args->check_self_contained_and_connected)
- argv_array_push(&cmd.args, "--check-self-contained-and-connected");
- /*
- * If we're obtaining the filename of a lockfile, we'll use
- * that filename to write a .promisor file with more
- * information below. If not, we need index-pack to do it for
- * us.
- */
- if (!(do_keep && pack_lockfile) && args->from_promisor)
- argv_array_push(&cmd.args, "--promisor");
+ if (only_packfile && args->check_self_contained_and_connected)
+ strvec_push(&cmd.args, "--check-self-contained-and-connected");
+ else
+ /*
+ * We cannot perform any connectivity checks because
+ * not all packs have been downloaded; let the caller
+ * have this responsibility.
+ */
+ args->check_self_contained_and_connected = 0;
+
+ if (args->from_promisor)
+ /*
+ * write_promisor_file() may be called afterwards but
+ * we still need index-pack to know that this is a
+ * promisor pack. For example, if transfer.fsckobjects
+ * is true, index-pack needs to know that .gitmodules
+ * is a promisor object (so that it won't complain if
+ * it is missing).
+ */
+ strvec_push(&cmd.args, "--promisor");
}
else {
cmd_name = "unpack-objects";
- argv_array_push(&cmd.args, cmd_name);
+ strvec_push(&cmd.args, cmd_name);
if (args->quiet || args->no_progress)
- argv_array_push(&cmd.args, "-q");
+ strvec_push(&cmd.args, "-q");
args->check_self_contained_and_connected = 0;
}
if (pass_header)
- argv_array_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
- ntohl(header.hdr_version),
+ strvec_pushf(&cmd.args, "--pack_header=%"PRIu32",%"PRIu32,
+ ntohl(header.hdr_version),
ntohl(header.hdr_entries));
if (fetch_fsck_objects >= 0
? fetch_fsck_objects
@@ -888,18 +901,19 @@ static int get_pack(struct fetch_pack_args *args,
* checks both broken objects and links, but we only
* want to check for broken objects.
*/
- argv_array_push(&cmd.args, "--fsck-objects");
+ strvec_push(&cmd.args, "--fsck-objects");
else
- argv_array_pushf(&cmd.args, "--strict%s",
- fsck_msg_types.buf);
+ strvec_pushf(&cmd.args, "--strict%s",
+ fsck_msg_types.buf);
}
cmd.in = demux.out;
cmd.git_cmd = 1;
if (start_command(&cmd))
die(_("fetch-pack: unable to fork off %s"), cmd_name);
- if (do_keep && pack_lockfile) {
- *pack_lockfile = index_pack_lockfile(cmd.out);
+ if (do_keep && pack_lockfiles) {
+ string_list_append_nodup(pack_lockfiles,
+ index_pack_lockfile(cmd.out));
close(cmd.out);
}
@@ -921,8 +935,8 @@ static int get_pack(struct fetch_pack_args *args,
* Now that index-pack has succeeded, write the promisor file using the
* obtained .keep filename if necessary
*/
- if (do_keep && pack_lockfile && args->from_promisor)
- write_promisor_file(*pack_lockfile, sought, nr_sought);
+ if (do_keep && pack_lockfiles && pack_lockfiles->nr && args->from_promisor)
+ write_promisor_file(pack_lockfiles->items[0].string, sought, nr_sought);
return 0;
}
@@ -939,7 +953,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
const struct ref *orig_ref,
struct ref **sought, int nr_sought,
struct shallow_info *si,
- char **pack_lockfile)
+ struct string_list *pack_lockfiles)
{
struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
@@ -1039,6 +1053,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
print_verbose(args, _("Server supports %s"), "deepen-relative");
else if (args->deepen_relative)
die(_("Server does not support --deepen"));
+ if (!server_supports_hash(the_hash_algo->name, NULL))
+ die(_("Server does not support this repository's object format"));
if (!args->no_dependents) {
mark_complete_and_common_ref(negotiator, args, &ref);
@@ -1066,7 +1082,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
alternate_shallow_file = setup_temporary_shallow(si->shallow);
else
alternate_shallow_file = NULL;
- if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
+ if (get_pack(args, fd, pack_lockfiles, 1, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));
all_done:
@@ -1143,6 +1159,7 @@ static void add_common(struct strbuf *req_buf, struct oidset *common)
}
static int add_haves(struct fetch_negotiator *negotiator,
+ int seen_ack,
struct strbuf *req_buf,
int *haves_to_send, int *in_vain)
{
@@ -1157,7 +1174,7 @@ static int add_haves(struct fetch_negotiator *negotiator,
}
*in_vain += haves_added;
- if (!haves_added || *in_vain >= MAX_IN_VAIN) {
+ if (!haves_added || (seen_ack && *in_vain >= MAX_IN_VAIN)) {
/* Send Done */
packet_buf_write(req_buf, "done\n");
ret = 1;
@@ -1173,9 +1190,10 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
struct fetch_pack_args *args,
const struct ref *wants, struct oidset *common,
int *haves_to_send, int *in_vain,
- int sideband_all)
+ int sideband_all, int seen_ack)
{
int ret = 0;
+ const char *hash_name;
struct strbuf req_buf = STRBUF_INIT;
if (server_supports_v2("fetch", 1))
@@ -1190,6 +1208,17 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
args->server_options->items[i].string);
}
+ if (server_feature_v2("object-format", &hash_name)) {
+ int hash_algo = hash_algo_by_name(hash_name);
+ if (hash_algo_by_ptr(the_hash_algo) != hash_algo)
+ die(_("mismatched algorithms: client %s; server %s"),
+ the_hash_algo->name, hash_name);
+ packet_write_fmt(fd_out, "object-format=%s", the_hash_algo->name);
+ } else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {
+ die(_("the server does not support algorithm '%s'"),
+ the_hash_algo->name);
+ }
+
packet_buf_delim(&req_buf);
if (args->use_thin_pack)
packet_buf_write(&req_buf, "thin-pack");
@@ -1219,6 +1248,26 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
warning("filtering not recognized by server, ignoring");
}
+ if (server_supports_feature("fetch", "packfile-uris", 0)) {
+ int i;
+ struct strbuf to_send = STRBUF_INIT;
+
+ for (i = 0; i < uri_protocols.nr; i++) {
+ const char *s = uri_protocols.items[i].string;
+
+ if (!strcmp(s, "https") || !strcmp(s, "http")) {
+ if (to_send.len)
+ strbuf_addch(&to_send, ',');
+ strbuf_addstr(&to_send, s);
+ }
+ }
+ if (to_send.len) {
+ packet_buf_write(&req_buf, "packfile-uris %s",
+ to_send.buf);
+ strbuf_release(&to_send);
+ }
+ }
+
/* add wants */
add_wants(args->no_dependents, wants, &req_buf);
@@ -1230,7 +1279,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
add_common(&req_buf, common);
/* Add initial haves */
- ret = add_haves(negotiator, &req_buf, haves_to_send, in_vain);
+ ret = add_haves(negotiator, seen_ack, &req_buf,
+ haves_to_send, in_vain);
}
/* Send request */
@@ -1268,9 +1318,29 @@ static int process_section_header(struct packet_reader *reader,
return ret;
}
-static int process_acks(struct fetch_negotiator *negotiator,
- struct packet_reader *reader,
- struct oidset *common)
+enum common_found {
+ /*
+ * No commit was found to be possessed by both the client and the
+ * server, and "ready" was not received.
+ */
+ NO_COMMON_FOUND,
+
+ /*
+ * At least one commit was found to be possessed by both the client and
+ * the server, and "ready" was not received.
+ */
+ COMMON_FOUND,
+
+ /*
+ * "ready" was received, indicating that the server is ready to send
+ * the packfile without any further negotiation.
+ */
+ READY
+};
+
+static enum common_found process_acks(struct fetch_negotiator *negotiator,
+ struct packet_reader *reader,
+ struct oidset *common)
{
/* received */
int received_ready = 0;
@@ -1285,6 +1355,7 @@ static int process_acks(struct fetch_negotiator *negotiator,
if (skip_prefix(reader->line, "ACK ", &arg)) {
struct object_id oid;
+ received_ack = 1;
if (!get_oid_hex(arg, &oid)) {
struct commit *commit;
oidset_insert(common, &oid);
@@ -1319,8 +1390,8 @@ static int process_acks(struct fetch_negotiator *negotiator,
if (!received_ready && reader->status != PACKET_READ_FLUSH)
die(_("expected no other sections to be sent after no 'ready'"));
- /* return 0 if no common, 1 if there are common, or 2 if ready */
- return received_ready ? 2 : (received_ack ? 1 : 0);
+ return received_ready ? READY :
+ (received_ack ? COMMON_FOUND : NO_COMMON_FOUND);
}
static void receive_shallow_info(struct fetch_pack_args *args,
@@ -1419,6 +1490,21 @@ static void receive_wanted_refs(struct packet_reader *reader,
die(_("error processing wanted refs: %d"), reader->status);
}
+static void receive_packfile_uris(struct packet_reader *reader,
+ struct string_list *uris)
+{
+ process_section_header(reader, "packfile-uris", 0);
+ while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+ if (reader->pktlen < the_hash_algo->hexsz ||
+ reader->line[the_hash_algo->hexsz] != ' ')
+ die("expected '<hash> <uri>', got: %s\n", reader->line);
+
+ string_list_append(uris, reader->line);
+ }
+ if (reader->status != PACKET_READ_DELIM)
+ die("expected DELIM");
+}
+
enum fetch_state {
FETCH_CHECK_LOCAL = 0,
FETCH_SEND_REQUEST,
@@ -1427,13 +1513,20 @@ enum fetch_state {
FETCH_DONE,
};
+static void do_check_stateless_delimiter(const struct fetch_pack_args *args,
+ struct packet_reader *reader)
+{
+ check_stateless_delimiter(args->stateless_rpc, reader,
+ _("git fetch-pack: expected response end packet"));
+}
+
static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
int fd[2],
const struct ref *orig_ref,
struct ref **sought, int nr_sought,
struct oid_array *shallows,
struct shallow_info *si,
- char **pack_lockfile)
+ struct string_list *pack_lockfiles)
{
struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
@@ -1444,6 +1537,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
int haves_to_send = INITIAL_FLUSH;
struct fetch_negotiator negotiator_alloc;
struct fetch_negotiator *negotiator;
+ int seen_ack = 0;
+ struct string_list packfile_uris = STRING_LIST_INIT_DUP;
+ int i;
if (args->no_dependents) {
negotiator = NULL;
@@ -1500,7 +1596,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
if (send_fetch_request(negotiator, fd[1], args, ref,
&common,
&haves_to_send, &in_vain,
- reader.use_sideband))
+ reader.use_sideband,
+ seen_ack))
state = FETCH_GET_PACK;
else
state = FETCH_PROCESS_ACKS;
@@ -1508,13 +1605,19 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
case FETCH_PROCESS_ACKS:
/* Process ACKs/NAKs */
switch (process_acks(negotiator, &reader, &common)) {
- case 2:
+ case READY:
+ /*
+ * Don't check for response delimiter; get_pack() will
+ * read the rest of this response.
+ */
state = FETCH_GET_PACK;
break;
- case 1:
+ case COMMON_FOUND:
in_vain = 0;
+ seen_ack = 1;
/* fallthrough */
- default:
+ case NO_COMMON_FOUND:
+ do_check_stateless_delimiter(args, &reader);
state = FETCH_SEND_REQUEST;
break;
}
@@ -1530,10 +1633,14 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
if (process_section_header(&reader, "wanted-refs", 1))
receive_wanted_refs(&reader, sought, nr_sought);
- /* get the pack */
+ /* get the pack(s) */
+ if (process_section_header(&reader, "packfile-uris", 1))
+ receive_packfile_uris(&reader, &packfile_uris);
process_section_header(&reader, "packfile", 0);
- if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
+ if (get_pack(args, fd, pack_lockfiles,
+ !packfile_uris.nr, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));
+ do_check_stateless_delimiter(args, &reader);
state = FETCH_DONE;
break;
@@ -1542,8 +1649,55 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
}
}
+ for (i = 0; i < packfile_uris.nr; i++) {
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ char packname[GIT_MAX_HEXSZ + 1];
+ const char *uri = packfile_uris.items[i].string +
+ the_hash_algo->hexsz + 1;
+
+ strvec_push(&cmd.args, "http-fetch");
+ strvec_pushf(&cmd.args, "--packfile=%.*s",
+ (int) the_hash_algo->hexsz,
+ packfile_uris.items[i].string);
+ strvec_push(&cmd.args, uri);
+ cmd.git_cmd = 1;
+ cmd.no_stdin = 1;
+ cmd.out = -1;
+ if (start_command(&cmd))
+ die("fetch-pack: unable to spawn http-fetch");
+
+ if (read_in_full(cmd.out, packname, 5) < 0 ||
+ memcmp(packname, "keep\t", 5))
+ die("fetch-pack: expected keep then TAB at start of http-fetch output");
+
+ if (read_in_full(cmd.out, packname,
+ the_hash_algo->hexsz + 1) < 0 ||
+ packname[the_hash_algo->hexsz] != '\n')
+ die("fetch-pack: expected hash then LF at end of http-fetch output");
+
+ packname[the_hash_algo->hexsz] = '\0';
+
+ close(cmd.out);
+
+ if (finish_command(&cmd))
+ die("fetch-pack: unable to finish http-fetch");
+
+ if (memcmp(packfile_uris.items[i].string, packname,
+ the_hash_algo->hexsz))
+ die("fetch-pack: pack downloaded from %s does not match expected hash %.*s",
+ uri, (int) the_hash_algo->hexsz,
+ packfile_uris.items[i].string);
+
+ string_list_append_nodup(pack_lockfiles,
+ xstrfmt("%s/pack/pack-%s.keep",
+ get_object_directory(),
+ packname));
+ }
+ string_list_clear(&packfile_uris, 0);
+
if (negotiator)
negotiator->release(negotiator);
+
oidset_clear(&common);
return ref;
}
@@ -1580,6 +1734,14 @@ static void fetch_pack_config(void)
git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
+ if (!uri_protocols.nr) {
+ char *str;
+
+ if (!git_config_get_string("fetch.uriprotocols", &str) && str) {
+ string_list_split(&uri_protocols, str, ',', -1);
+ free(str);
+ }
+ }
git_config(fetch_pack_config_cb, NULL);
}
@@ -1629,9 +1791,9 @@ static void update_shallow(struct fetch_pack_args *args,
if (args->deepen && alternate_shallow_file) {
if (*alternate_shallow_file == '\0') { /* --unshallow */
unlink_or_warn(git_path_shallow(the_repository));
- rollback_lock_file(&shallow_lock);
+ rollback_shallow_file(the_repository, &shallow_lock);
} else
- commit_lock_file(&shallow_lock);
+ commit_shallow_file(the_repository, &shallow_lock);
alternate_shallow_file = NULL;
return;
}
@@ -1655,7 +1817,7 @@ static void update_shallow(struct fetch_pack_args *args,
setup_alternate_shallow(&shallow_lock,
&alternate_shallow_file,
&extra);
- commit_lock_file(&shallow_lock);
+ commit_shallow_file(the_repository, &shallow_lock);
alternate_shallow_file = NULL;
}
oid_array_clear(&extra);
@@ -1693,7 +1855,7 @@ static void update_shallow(struct fetch_pack_args *args,
setup_alternate_shallow(&shallow_lock,
&alternate_shallow_file,
&extra);
- commit_lock_file(&shallow_lock);
+ commit_shallow_file(the_repository, &shallow_lock);
oid_array_clear(&extra);
oid_array_clear(&ref);
alternate_shallow_file = NULL;
@@ -1732,7 +1894,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
const struct ref *ref,
struct ref **sought, int nr_sought,
struct oid_array *shallow,
- char **pack_lockfile,
+ struct string_list *pack_lockfiles,
enum protocol_version version)
{
struct ref *ref_cpy;
@@ -1767,11 +1929,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
memset(&si, 0, sizeof(si));
ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought,
&shallows_scratch, &si,
- pack_lockfile);
+ pack_lockfiles);
} else {
prepare_shallow_info(&si, shallow);
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
- &si, pack_lockfile);
+ &si, pack_lockfiles);
}
reprepare_packed_git(the_repository);
@@ -1785,7 +1947,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
error(_("remote did not send all necessary objects"));
free_refs(ref_cpy);
ref_cpy = NULL;
- rollback_lock_file(&shallow_lock);
+ rollback_shallow_file(the_repository, &shallow_lock);
goto cleanup;
}
args->connectivity_checked = 1;