|
|
|
@ -18,6 +18,7 @@
@@ -18,6 +18,7 @@
|
|
|
|
|
#include "prio-queue.h" |
|
|
|
|
#include "protocol.h" |
|
|
|
|
#include "upload-pack.h" |
|
|
|
|
#include "serve.h" |
|
|
|
|
|
|
|
|
|
/* Remember to update object flag allocation in object.h */ |
|
|
|
|
#define THEY_HAVE (1u << 11) |
|
|
|
@ -1065,3 +1066,268 @@ void upload_pack(struct upload_pack_options *options)
@@ -1065,3 +1066,268 @@ void upload_pack(struct upload_pack_options *options)
|
|
|
|
|
create_pack_file(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct upload_pack_data { |
|
|
|
|
struct object_array wants; |
|
|
|
|
struct oid_array haves; |
|
|
|
|
|
|
|
|
|
unsigned stateless_rpc : 1; |
|
|
|
|
|
|
|
|
|
unsigned use_thin_pack : 1; |
|
|
|
|
unsigned use_ofs_delta : 1; |
|
|
|
|
unsigned no_progress : 1; |
|
|
|
|
unsigned use_include_tag : 1; |
|
|
|
|
unsigned done : 1; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static void upload_pack_data_init(struct upload_pack_data *data) |
|
|
|
|
{ |
|
|
|
|
struct object_array wants = OBJECT_ARRAY_INIT; |
|
|
|
|
struct oid_array haves = OID_ARRAY_INIT; |
|
|
|
|
|
|
|
|
|
memset(data, 0, sizeof(*data)); |
|
|
|
|
data->wants = wants; |
|
|
|
|
data->haves = haves; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void upload_pack_data_clear(struct upload_pack_data *data) |
|
|
|
|
{ |
|
|
|
|
object_array_clear(&data->wants); |
|
|
|
|
oid_array_clear(&data->haves); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int parse_want(const char *line) |
|
|
|
|
{ |
|
|
|
|
const char *arg; |
|
|
|
|
if (skip_prefix(line, "want ", &arg)) { |
|
|
|
|
struct object_id oid; |
|
|
|
|
struct object *o; |
|
|
|
|
|
|
|
|
|
if (get_oid_hex(arg, &oid)) |
|
|
|
|
die("git upload-pack: protocol error, " |
|
|
|
|
"expected to get oid, not '%s'", line); |
|
|
|
|
|
|
|
|
|
o = parse_object(&oid); |
|
|
|
|
if (!o) { |
|
|
|
|
packet_write_fmt(1, |
|
|
|
|
"ERR upload-pack: not our ref %s", |
|
|
|
|
oid_to_hex(&oid)); |
|
|
|
|
die("git upload-pack: not our ref %s", |
|
|
|
|
oid_to_hex(&oid)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!(o->flags & WANTED)) { |
|
|
|
|
o->flags |= WANTED; |
|
|
|
|
add_object_array(o, NULL, &want_obj); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int parse_have(const char *line, struct oid_array *haves) |
|
|
|
|
{ |
|
|
|
|
const char *arg; |
|
|
|
|
if (skip_prefix(line, "have ", &arg)) { |
|
|
|
|
struct object_id oid; |
|
|
|
|
|
|
|
|
|
if (get_oid_hex(arg, &oid)) |
|
|
|
|
die("git upload-pack: expected SHA1 object, got '%s'", arg); |
|
|
|
|
oid_array_append(haves, &oid); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void process_args(struct packet_reader *request, |
|
|
|
|
struct upload_pack_data *data) |
|
|
|
|
{ |
|
|
|
|
while (packet_reader_read(request) != PACKET_READ_FLUSH) { |
|
|
|
|
const char *arg = request->line; |
|
|
|
|
|
|
|
|
|
/* process want */ |
|
|
|
|
if (parse_want(arg)) |
|
|
|
|
continue; |
|
|
|
|
/* process have line */ |
|
|
|
|
if (parse_have(arg, &data->haves)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
/* process args like thin-pack */ |
|
|
|
|
if (!strcmp(arg, "thin-pack")) { |
|
|
|
|
use_thin_pack = 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (!strcmp(arg, "ofs-delta")) { |
|
|
|
|
use_ofs_delta = 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (!strcmp(arg, "no-progress")) { |
|
|
|
|
no_progress = 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (!strcmp(arg, "include-tag")) { |
|
|
|
|
use_include_tag = 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (!strcmp(arg, "done")) { |
|
|
|
|
data->done = 1; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* ignore unknown lines maybe? */ |
|
|
|
|
die("unexpect line: '%s'", arg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int process_haves(struct oid_array *haves, struct oid_array *common) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
/* Process haves */ |
|
|
|
|
for (i = 0; i < haves->nr; i++) { |
|
|
|
|
const struct object_id *oid = &haves->oid[i]; |
|
|
|
|
struct object *o; |
|
|
|
|
int we_knew_they_have = 0; |
|
|
|
|
|
|
|
|
|
if (!has_object_file(oid)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
oid_array_append(common, oid); |
|
|
|
|
|
|
|
|
|
o = parse_object(oid); |
|
|
|
|
if (!o) |
|
|
|
|
die("oops (%s)", oid_to_hex(oid)); |
|
|
|
|
if (o->type == OBJ_COMMIT) { |
|
|
|
|
struct commit_list *parents; |
|
|
|
|
struct commit *commit = (struct commit *)o; |
|
|
|
|
if (o->flags & THEY_HAVE) |
|
|
|
|
we_knew_they_have = 1; |
|
|
|
|
else |
|
|
|
|
o->flags |= THEY_HAVE; |
|
|
|
|
if (!oldest_have || (commit->date < oldest_have)) |
|
|
|
|
oldest_have = commit->date; |
|
|
|
|
for (parents = commit->parents; |
|
|
|
|
parents; |
|
|
|
|
parents = parents->next) |
|
|
|
|
parents->item->object.flags |= THEY_HAVE; |
|
|
|
|
} |
|
|
|
|
if (!we_knew_they_have) |
|
|
|
|
add_object_array(o, NULL, &have_obj); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int send_acks(struct oid_array *acks, struct strbuf *response) |
|
|
|
|
{ |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
packet_buf_write(response, "acknowledgments\n"); |
|
|
|
|
|
|
|
|
|
/* Send Acks */ |
|
|
|
|
if (!acks->nr) |
|
|
|
|
packet_buf_write(response, "NAK\n"); |
|
|
|
|
|
|
|
|
|
for (i = 0; i < acks->nr; i++) { |
|
|
|
|
packet_buf_write(response, "ACK %s\n", |
|
|
|
|
oid_to_hex(&acks->oid[i])); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (ok_to_give_up()) { |
|
|
|
|
/* Send Ready */ |
|
|
|
|
packet_buf_write(response, "ready\n"); |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int process_haves_and_send_acks(struct upload_pack_data *data) |
|
|
|
|
{ |
|
|
|
|
struct oid_array common = OID_ARRAY_INIT; |
|
|
|
|
struct strbuf response = STRBUF_INIT; |
|
|
|
|
int ret = 0; |
|
|
|
|
|
|
|
|
|
process_haves(&data->haves, &common); |
|
|
|
|
if (data->done) { |
|
|
|
|
ret = 1; |
|
|
|
|
} else if (send_acks(&common, &response)) { |
|
|
|
|
packet_buf_delim(&response); |
|
|
|
|
ret = 1; |
|
|
|
|
} else { |
|
|
|
|
/* Add Flush */ |
|
|
|
|
packet_buf_flush(&response); |
|
|
|
|
ret = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Send response */ |
|
|
|
|
write_or_die(1, response.buf, response.len); |
|
|
|
|
strbuf_release(&response); |
|
|
|
|
|
|
|
|
|
oid_array_clear(&data->haves); |
|
|
|
|
oid_array_clear(&common); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum fetch_state { |
|
|
|
|
FETCH_PROCESS_ARGS = 0, |
|
|
|
|
FETCH_SEND_ACKS, |
|
|
|
|
FETCH_SEND_PACK, |
|
|
|
|
FETCH_DONE, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
int upload_pack_v2(struct repository *r, struct argv_array *keys, |
|
|
|
|
struct packet_reader *request) |
|
|
|
|
{ |
|
|
|
|
enum fetch_state state = FETCH_PROCESS_ARGS; |
|
|
|
|
struct upload_pack_data data; |
|
|
|
|
|
|
|
|
|
upload_pack_data_init(&data); |
|
|
|
|
use_sideband = LARGE_PACKET_MAX; |
|
|
|
|
|
|
|
|
|
while (state != FETCH_DONE) { |
|
|
|
|
switch (state) { |
|
|
|
|
case FETCH_PROCESS_ARGS: |
|
|
|
|
process_args(request, &data); |
|
|
|
|
|
|
|
|
|
if (!want_obj.nr) { |
|
|
|
|
/* |
|
|
|
|
* Request didn't contain any 'want' lines, |
|
|
|
|
* guess they didn't want anything. |
|
|
|
|
*/ |
|
|
|
|
state = FETCH_DONE; |
|
|
|
|
} else if (data.haves.nr) { |
|
|
|
|
/* |
|
|
|
|
* Request had 'have' lines, so lets ACK them. |
|
|
|
|
*/ |
|
|
|
|
state = FETCH_SEND_ACKS; |
|
|
|
|
} else { |
|
|
|
|
/* |
|
|
|
|
* Request had 'want's but no 'have's so we can |
|
|
|
|
* immedietly go to construct and send a pack. |
|
|
|
|
*/ |
|
|
|
|
state = FETCH_SEND_PACK; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case FETCH_SEND_ACKS: |
|
|
|
|
if (process_haves_and_send_acks(&data)) |
|
|
|
|
state = FETCH_SEND_PACK; |
|
|
|
|
else |
|
|
|
|
state = FETCH_DONE; |
|
|
|
|
break; |
|
|
|
|
case FETCH_SEND_PACK: |
|
|
|
|
packet_write_fmt(1, "packfile\n"); |
|
|
|
|
create_pack_file(); |
|
|
|
|
state = FETCH_DONE; |
|
|
|
|
break; |
|
|
|
|
case FETCH_DONE: |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
upload_pack_data_clear(&data); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|