aboutsummaryrefslogtreecommitdiff
path: root/upload-pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'upload-pack.c')
-rw-r--r--upload-pack.c215
1 files changed, 98 insertions, 117 deletions
diff --git a/upload-pack.c b/upload-pack.c
index 1e87ae9559..9f6d6fe48c 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -29,6 +29,7 @@
#include "commit-graph.h"
#include "commit-reach.h"
#include "shallow.h"
+#include "trace.h"
#include "write-or-die.h"
#include "json-writer.h"
#include "strmap.h"
@@ -218,7 +219,8 @@ struct output_state {
};
static int relay_pack_data(int pack_objects_out, struct output_state *os,
- int use_sideband, int write_packfile_line)
+ int use_sideband, int write_packfile_line,
+ bool *did_send_data)
{
/*
* We keep the last byte to ourselves
@@ -232,6 +234,8 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
*/
ssize_t readsz;
+ *did_send_data = false;
+
readsz = xread(pack_objects_out, os->buffer + os->used,
sizeof(os->buffer) - os->used);
if (readsz < 0) {
@@ -247,6 +251,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
if (os->packfile_uris_started)
packet_delim(1);
packet_write_fmt(1, "\1packfile\n");
+ *did_send_data = true;
}
break;
}
@@ -259,6 +264,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
}
*p = '\0';
packet_write_fmt(1, "\1%s\n", os->buffer);
+ *did_send_data = true;
os->used -= p - os->buffer + 1;
memmove(os->buffer, p + 1, os->used);
@@ -270,6 +276,13 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
}
}
+ /*
+ * Make sure that we buffer some data before sending it to the client.
+ * This significantly reduces the number of write(3p) syscalls.
+ */
+ if (readsz && os->used < (sizeof(os->buffer) * 2 / 3))
+ return readsz;
+
if (os->used > 1) {
send_client_data(1, os->buffer, os->used - 1, use_sideband);
os->buffer[0] = os->buffer[os->used - 1];
@@ -279,6 +292,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os,
os->used = 0;
}
+ *did_send_data = true;
return readsz;
}
@@ -290,6 +304,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
char progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
+ uint64_t last_sent_ms = 0;
ssize_t sz;
int i;
FILE *pipe_fd;
@@ -365,10 +380,14 @@ static void create_pack_file(struct upload_pack_data *pack_data,
*/
while (1) {
+ uint64_t now_ms = getnanotime() / 1000000;
struct pollfd pfd[2];
- int pe, pu, pollsize, polltimeout;
+ int pe, pu, pollsize, polltimeout_ms;
int ret;
+ if (!last_sent_ms)
+ last_sent_ms = now_ms;
+
reset_timeout(pack_data->timeout);
pollsize = 0;
@@ -390,11 +409,21 @@ static void create_pack_file(struct upload_pack_data *pack_data,
if (!pollsize)
break;
- polltimeout = pack_data->keepalive < 0
- ? -1
- : 1000 * pack_data->keepalive;
+ if (pack_data->keepalive < 0) {
+ polltimeout_ms = -1;
+ } else {
+ /*
+ * The polling timeout needs to be adjusted based on
+ * the time we have sent our last package. The longer
+ * it's been in the past, the shorter the timeout
+ * becomes until we eventually don't block at all.
+ */
+ polltimeout_ms = 1000 * pack_data->keepalive - (now_ms - last_sent_ms);
+ if (polltimeout_ms < 0)
+ polltimeout_ms = 0;
+ }
- ret = poll(pfd, pollsize, polltimeout);
+ ret = poll(pfd, pollsize, polltimeout_ms);
if (ret < 0) {
if (errno != EINTR) {
@@ -403,16 +432,18 @@ static void create_pack_file(struct upload_pack_data *pack_data,
}
continue;
}
+
if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
/* Status ready; we ship that in the side-band
* or dump to the standard error.
*/
sz = xread(pack_objects.err, progress,
sizeof(progress));
- if (0 < sz)
+ if (0 < sz) {
send_client_data(2, progress, sz,
pack_data->use_sideband);
- else if (sz == 0) {
+ last_sent_ms = now_ms;
+ } else if (sz == 0) {
close(pack_objects.err);
pack_objects.err = -1;
}
@@ -421,11 +452,14 @@ static void create_pack_file(struct upload_pack_data *pack_data,
/* give priority to status messages */
continue;
}
+
if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) {
+ bool did_send_data;
int result = relay_pack_data(pack_objects.out,
output_state,
pack_data->use_sideband,
- !!uri_protocols);
+ !!uri_protocols,
+ &did_send_data);
if (result == 0) {
close(pack_objects.out);
@@ -433,21 +467,34 @@ static void create_pack_file(struct upload_pack_data *pack_data,
} else if (result < 0) {
goto fail;
}
+
+ if (did_send_data)
+ last_sent_ms = now_ms;
}
/*
- * We hit the keepalive timeout without saying anything; send
- * an empty message on the data sideband just to let the other
- * side know we're still working on it, but don't have any data
- * yet.
+ * We hit the keepalive timeout without saying anything. If we
+ * have pending data we flush it out to the caller now.
+ * Otherwise, we send an empty message on the data sideband
+ * just to let the other side know we're still working on it,
+ * but don't have any data yet.
*
* If we don't have a sideband channel, there's no room in the
* protocol to say anything, so those clients are just out of
* luck.
*/
if (!ret && pack_data->use_sideband) {
- static const char buf[] = "0005\1";
- write_or_die(1, buf, 5);
+ if (output_state->packfile_started && output_state->used > 1) {
+ send_client_data(1, output_state->buffer, output_state->used - 1,
+ pack_data->use_sideband);
+ output_state->buffer[0] = output_state->buffer[output_state->used - 1];
+ output_state->used = 1;
+ } else {
+ static const char buf[] = "0005\1";
+ write_or_die(1, buf, 5);
+ }
+
+ last_sent_ms = now_ms;
}
}
@@ -457,11 +504,9 @@ static void create_pack_file(struct upload_pack_data *pack_data,
}
/* flush the data */
- if (output_state->used > 0) {
+ if (output_state->used > 0)
send_client_data(1, output_state->buffer, output_state->used,
pack_data->use_sideband);
- fprintf(stderr, "flushed.\n");
- }
free(output_state);
if (pack_data->use_sideband)
packet_flush(1);
@@ -607,10 +652,13 @@ static int allow_hidden_refs(enum allow_uor allow_uor)
return !(allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
}
-static void for_each_namespaced_ref_1(each_ref_fn fn,
+static void for_each_namespaced_ref_1(refs_for_each_cb fn,
struct upload_pack_data *data)
{
- const char **excludes = NULL;
+ struct refs_for_each_ref_options opts = {
+ .namespace = get_git_namespace(),
+ };
+
/*
* If `data->allow_uor` allows fetching hidden refs, we need to
* mark all references (including hidden ones), to check in
@@ -621,10 +669,10 @@ static void for_each_namespaced_ref_1(each_ref_fn fn,
* hidden references.
*/
if (allow_hidden_refs(data->allow_uor))
- excludes = hidden_refs_to_excludes(&data->hidden_refs);
+ opts.exclude_patterns = hidden_refs_to_excludes(&data->hidden_refs);
- refs_for_each_namespaced_ref(get_main_ref_store(the_repository),
- excludes, fn, data);
+ refs_for_each_ref_ext(get_main_ref_store(the_repository),
+ fn, data, &opts);
}
@@ -704,56 +752,6 @@ error:
return -1;
}
-static int get_reachable_list(struct upload_pack_data *data,
- struct object_array *reachable)
-{
- struct child_process cmd = CHILD_PROCESS_INIT;
- int i;
- struct object *o;
- char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
- const unsigned hexsz = the_hash_algo->hexsz;
- int ret;
-
- if (do_reachable_revlist(&cmd, &data->shallows, reachable,
- data->allow_uor) < 0) {
- ret = -1;
- goto out;
- }
-
- while ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) {
- struct object_id oid;
- const char *p;
-
- if (parse_oid_hex(namebuf, &oid, &p) || *p != '\n')
- break;
-
- o = lookup_object(the_repository, &oid);
- if (o && o->type == OBJ_COMMIT) {
- o->flags &= ~TMP_MARK;
- }
- }
- for (i = get_max_object_index(the_repository); 0 < i; i--) {
- o = get_indexed_object(the_repository, i - 1);
- if (o && o->type == OBJ_COMMIT &&
- (o->flags & TMP_MARK)) {
- add_object_array(o, NULL, reachable);
- o->flags &= ~TMP_MARK;
- }
- }
- close(cmd.out);
-
- if (finish_command(&cmd)) {
- ret = -1;
- goto out;
- }
-
- ret = 0;
-
-out:
- child_process_clear(&cmd);
- return ret;
-}
-
static int has_unreachable(struct object_array *src, enum allow_uor allow_uor)
{
struct child_process cmd = CHILD_PROCESS_INIT;
@@ -870,8 +868,8 @@ static void send_unshallow(struct upload_pack_data *data)
}
}
-static int check_ref(const char *refname_full, const char *referent UNUSED, const struct object_id *oid,
- int flag, void *cb_data);
+static int check_ref(const struct reference *ref, void *cb_data);
+
static void deepen(struct upload_pack_data *data, int depth)
{
if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
@@ -881,29 +879,11 @@ static void deepen(struct upload_pack_data *data, int depth)
struct object *object = data->shallows.objects[i].item;
object->flags |= NOT_SHALLOW;
}
- } else if (data->deepen_relative) {
- struct object_array reachable_shallows = OBJECT_ARRAY_INIT;
- struct commit_list *result;
-
- /*
- * Checking for reachable shallows requires that our refs be
- * marked with OUR_REF.
- */
- refs_head_ref_namespaced(get_main_ref_store(the_repository),
- check_ref, data);
- for_each_namespaced_ref_1(check_ref, data);
-
- get_reachable_list(data, &reachable_shallows);
- result = get_shallow_commits(&reachable_shallows,
- depth + 1,
- SHALLOW, NOT_SHALLOW);
- send_shallow(data, result);
- free_commit_list(result);
- object_array_clear(&reachable_shallows);
} else {
struct commit_list *result;
- result = get_shallow_commits(&data->want_obj, depth,
+ result = get_shallow_commits(&data->want_obj, &data->shallows,
+ data->deepen_relative, depth,
SHALLOW, NOT_SHALLOW);
send_shallow(data, result);
free_commit_list(result);
@@ -1224,13 +1204,12 @@ static int mark_our_ref(const char *refname, const char *refname_full,
return 0;
}
-static int check_ref(const char *refname_full, const char *referent UNUSED,const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int check_ref(const struct reference *ref, void *cb_data)
{
- const char *refname = strip_namespace(refname_full);
+ const char *refname = strip_namespace(ref->name);
struct upload_pack_data *data = cb_data;
- mark_our_ref(refname, refname_full, oid, &data->hidden_refs);
+ mark_our_ref(refname, ref->name, ref->oid, &data->hidden_refs);
return 0;
}
@@ -1250,15 +1229,15 @@ static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
}
static void write_v0_ref(struct upload_pack_data *data,
- const char *refname, const char *refname_nons,
- const struct object_id *oid)
+ const struct reference *ref,
+ const char *refname_nons)
{
static const char *capabilities = "multi_ack thin-pack side-band"
" side-band-64k ofs-delta shallow deepen-since deepen-not"
" deepen-relative no-progress include-tag multi_ack_detailed";
struct object_id peeled;
- if (mark_our_ref(refname_nons, refname, oid, &data->hidden_refs))
+ if (mark_our_ref(refname_nons, ref->name, ref->oid, &data->hidden_refs))
return;
if (capabilities) {
@@ -1268,7 +1247,7 @@ static void write_v0_ref(struct upload_pack_data *data,
format_symref_info(&symref_info, &data->symref);
format_session_id(&session_id, data);
packet_fwrite_fmt(stdout, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
- oid_to_hex(oid), refname_nons,
+ oid_to_hex(ref->oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
" allow-tip-sha1-in-want" : "",
@@ -1284,35 +1263,33 @@ static void write_v0_ref(struct upload_pack_data *data,
strbuf_release(&session_id);
data->sent_capabilities = 1;
} else {
- packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(oid), refname_nons);
+ packet_fwrite_fmt(stdout, "%s %s\n", oid_to_hex(ref->oid), refname_nons);
}
capabilities = NULL;
- if (!peel_iterated_oid(the_repository, oid, &peeled))
+ if (!reference_get_peeled_oid(the_repository, ref, &peeled))
packet_fwrite_fmt(stdout, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
return;
}
-static int send_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
- int flag UNUSED, void *cb_data)
+static int send_ref(const struct reference *ref, void *cb_data)
{
- write_v0_ref(cb_data, refname, strip_namespace(refname), oid);
+ write_v0_ref(cb_data, ref, strip_namespace(ref->name));
return 0;
}
-static int find_symref(const char *refname, const char *referent UNUSED,
- const struct object_id *oid UNUSED,
- int flag, void *cb_data)
+static int find_symref(const struct reference *ref, void *cb_data)
{
const char *symref_target;
struct string_list_item *item;
+ int flag;
- if ((flag & REF_ISSYMREF) == 0)
+ if ((ref->flags & REF_ISSYMREF) == 0)
return 0;
symref_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
- refname, 0, NULL, &flag);
+ ref->name, 0, NULL, &flag);
if (!symref_target || (flag & REF_ISSYMREF) == 0)
- die("'%s' is a symref but it is not?", refname);
- item = string_list_append(cb_data, strip_namespace(refname));
+ die("'%s' is a symref but it is not?", ref->name);
+ item = string_list_append(cb_data, strip_namespace(ref->name));
item->util = xstrdup(strip_namespace(symref_target));
return 0;
}
@@ -1445,8 +1422,12 @@ void upload_pack(const int advertise_refs, const int stateless_rpc,
send_ref, &data);
for_each_namespaced_ref_1(send_ref, &data);
if (!data.sent_capabilities) {
- const char *refname = "capabilities^{}";
- write_v0_ref(&data, refname, refname, null_oid(the_hash_algo));
+ struct reference ref = {
+ .name = "capabilities^{}",
+ .oid = null_oid(the_hash_algo),
+ };
+
+ write_v0_ref(&data, &ref, ref.name);
}
/*
* fflush stdout before calling advertise_shallow_grafts because send_ref