upload-pack: introduce fetch server command
Introduce the 'fetch' server command. Signed-off-by: Brandon Williams <bmwill@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:

committed by
Junio C Hamano

parent
5b872fff18
commit
3145ea957d
266
upload-pack.c
266
upload-pack.c
@ -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)
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user