#include "refs.h"
#include "pkt-line.h"
#include "sideband.h"
+#include "repository.h"
+#include "object-store.h"
#include "tag.h"
#include "object.h"
#include "commit.h"
#include "quote.h"
#include "upload-pack.h"
#include "serve.h"
+#include "commit-graph.h"
+#include "commit-reach.h"
/* Remember to update object flag allocation in object.h */
#define THEY_HAVE (1u << 11)
#define OUR_REF (1u << 12)
#define WANTED (1u << 13)
#define COMMON_KNOWN (1u << 14)
-#define REACHABLE (1u << 15)
#define SHALLOW (1u << 16)
#define NOT_SHALLOW (1u << 17)
static int filter_capability_requested;
static int allow_filter;
+static int allow_ref_in_want;
static struct list_objects_filter_options filter_options;
static void reset_timeout(void)
if (!has_object_file(oid))
return -1;
- o = parse_object(oid);
+ o = parse_object(the_repository, oid);
if (!o)
die("oops (%s)", oid_to_hex(oid));
if (o->type == OBJ_COMMIT) {
return 0;
}
-static int reachable(struct commit *want)
-{
- struct prio_queue work = { compare_commits_by_commit_date };
-
- prio_queue_put(&work, want);
- while (work.nr) {
- struct commit_list *list;
- struct commit *commit = prio_queue_get(&work);
-
- if (commit->object.flags & THEY_HAVE) {
- want->object.flags |= COMMON_KNOWN;
- break;
- }
- if (!commit->object.parsed)
- parse_object(&commit->object.oid);
- if (commit->object.flags & REACHABLE)
- continue;
- commit->object.flags |= REACHABLE;
- if (commit->date < oldest_have)
- continue;
- for (list = commit->parents; list; list = list->next) {
- struct commit *parent = list->item;
- if (!(parent->object.flags & REACHABLE))
- prio_queue_put(&work, parent);
- }
- }
- want->object.flags |= REACHABLE;
- clear_commit_marks(want, REACHABLE);
- clear_prio_queue(&work);
- return (want->object.flags & COMMON_KNOWN);
-}
-
static int ok_to_give_up(void)
{
- int i;
+ uint32_t min_generation = GENERATION_NUMBER_ZERO;
if (!have_obj.nr)
return 0;
- for (i = 0; i < want_obj.nr; i++) {
- struct object *want = want_obj.objects[i].item;
-
- if (want->flags & COMMON_KNOWN)
- continue;
- want = deref_tag(want, "a want line", 0);
- if (!want || want->type != OBJ_COMMIT) {
- /* no way to tell if this is reachable by
- * looking at the ancestry chain alone, so
- * leave a note to ourselves not to worry about
- * this object anymore.
- */
- want_obj.objects[i].item->flags |= COMMON_KNOWN;
- continue;
- }
- if (!reachable((struct commit *)want))
- return 0;
- }
- return 1;
+ return can_all_from_reach_with_flag(&want_obj, THEY_HAVE,
+ COMMON_KNOWN, oldest_have,
+ min_generation);
}
static int get_common_commits(void)
break;
default:
got_common = 1;
- memcpy(last_hex, oid_to_hex(&oid), 41);
+ oid_to_hex_r(last_hex, &oid);
if (multi_ack == 2)
packet_write_fmt(1, "ACK %s common\n", last_hex);
else if (multi_ack)
"rev-list", "--stdin", NULL,
};
struct object *o;
- char namebuf[42]; /* ^ + SHA-1 + LF */
+ char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
int i;
+ const unsigned hexsz = the_hash_algo->hexsz;
cmd->argv = argv;
cmd->git_cmd = 1;
goto error;
namebuf[0] = '^';
- namebuf[GIT_SHA1_HEXSZ + 1] = '\n';
+ namebuf[hexsz + 1] = '\n';
for (i = get_max_object_index(); 0 < i; ) {
o = get_indexed_object(--i);
if (!o)
o->flags &= ~TMP_MARK;
if (!is_our_ref(o))
continue;
- memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
- if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 2) < 0)
+ memcpy(namebuf + 1, oid_to_hex(&o->oid), hexsz);
+ if (write_in_full(cmd->in, namebuf, hexsz + 2) < 0)
goto error;
}
- namebuf[GIT_SHA1_HEXSZ] = '\n';
+ namebuf[hexsz] = '\n';
for (i = 0; i < src->nr; i++) {
o = src->objects[i].item;
if (is_our_ref(o)) {
}
if (reachable && o->type == OBJ_COMMIT)
o->flags |= TMP_MARK;
- memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
- if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 1) < 0)
+ memcpy(namebuf, oid_to_hex(&o->oid), hexsz);
+ if (write_in_full(cmd->in, namebuf, hexsz + 1) < 0)
goto error;
}
close(cmd->in);
struct child_process cmd = CHILD_PROCESS_INIT;
int i;
struct object *o;
- char namebuf[42]; /* ^ + SHA-1 + LF */
+ char namebuf[GIT_MAX_HEXSZ + 2]; /* ^ + hash + LF */
+ const unsigned hexsz = the_hash_algo->hexsz;
if (do_reachable_revlist(&cmd, src, reachable) < 0)
return -1;
- while ((i = read_in_full(cmd.out, namebuf, 41)) == 41) {
+ while ((i = read_in_full(cmd.out, namebuf, hexsz + 1)) == hexsz + 1) {
struct object_id sha1;
+ const char *p;
- if (namebuf[40] != '\n' || get_oid_hex(namebuf, &sha1))
+ if (parse_oid_hex(namebuf, &sha1, &p) || *p != '\n')
break;
- o = lookup_object(sha1.hash);
+ o = lookup_object(the_repository, sha1.hash);
if (o && o->type == OBJ_COMMIT) {
o->flags &= ~TMP_MARK;
}
if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
packet_write_fmt(1, "shallow %s",
oid_to_hex(&object->oid));
- register_shallow(&object->oid);
+ register_shallow(the_repository, &object->oid);
shallow_nr++;
}
result = result->next;
add_object_array(object, NULL, &extra_edge_obj);
}
/* make sure commit traversal conforms to client */
- register_shallow(&object->oid);
+ register_shallow(the_repository, &object->oid);
}
}
static void deepen(int depth, int deepen_relative,
struct object_array *shallows)
{
- if (depth == INFINITE_DEPTH && !is_repository_shallow()) {
+ if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
int i;
for (i = 0; i < shallows->nr; i++) {
{
struct commit_list *result;
+ close_commit_graph(the_repository);
result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
send_shallow(result);
free_commit_list(result);
if (shallows->nr > 0) {
int i;
for (i = 0; i < shallows->nr; i++)
- register_shallow(&shallows->objects[i].item->oid);
+ register_shallow(the_repository,
+ &shallows->objects[i].item->oid);
}
}
struct object *object;
if (get_oid_hex(arg, &oid))
die("invalid shallow line: %s", line);
- object = parse_object(&oid);
+ object = parse_object(the_repository, &oid);
if (!object)
return 1;
if (object->type != OBJ_COMMIT)
}
if (!skip_prefix(line, "want ", &arg) ||
- get_oid_hex(arg, &oid_buf))
+ parse_oid_hex(arg, &oid_buf, &features))
die("git upload-pack: protocol error, "
- "expected to get sha, not '%s'", line);
-
- features = arg + 40;
+ "expected to get object ID, not '%s'", line);
if (parse_feature_request(features, "deepen-relative"))
deepen_relative = 1;
if (allow_filter && parse_feature_request(features, "filter"))
filter_capability_requested = 1;
- o = parse_object(&oid_buf);
+ o = parse_object(the_repository, &oid_buf);
if (!o) {
packet_write_fmt(1,
"ERR upload-pack: not our ref %s",
return git_config_string(&pack_objects_hook, var, value);
} else if (!strcmp("uploadpack.allowfilter", var)) {
allow_filter = git_config_bool(var, value);
+ } else if (!strcmp("uploadpack.allowrefinwant", var)) {
+ allow_ref_in_want = git_config_bool(var, value);
}
return parse_hide_refs_config(var, value, "uploadpack");
}
struct upload_pack_data {
struct object_array wants;
+ struct string_list wanted_refs;
struct oid_array haves;
struct object_array shallows;
static void upload_pack_data_init(struct upload_pack_data *data)
{
struct object_array wants = OBJECT_ARRAY_INIT;
+ struct string_list wanted_refs = STRING_LIST_INIT_DUP;
struct oid_array haves = OID_ARRAY_INIT;
struct object_array shallows = OBJECT_ARRAY_INIT;
struct string_list deepen_not = STRING_LIST_INIT_DUP;
memset(data, 0, sizeof(*data));
data->wants = wants;
+ data->wanted_refs = wanted_refs;
data->haves = haves;
data->shallows = shallows;
data->deepen_not = deepen_not;
static void upload_pack_data_clear(struct upload_pack_data *data)
{
object_array_clear(&data->wants);
+ string_list_clear(&data->wanted_refs, 1);
oid_array_clear(&data->haves);
object_array_clear(&data->shallows);
string_list_clear(&data->deepen_not, 0);
die("git upload-pack: protocol error, "
"expected to get oid, not '%s'", line);
- o = parse_object(&oid);
+ o = parse_object(the_repository, &oid);
if (!o) {
packet_write_fmt(1,
"ERR upload-pack: not our ref %s",
return 0;
}
+static int parse_want_ref(const char *line, struct string_list *wanted_refs)
+{
+ const char *arg;
+ if (skip_prefix(line, "want-ref ", &arg)) {
+ struct object_id oid;
+ struct string_list_item *item;
+ struct object *o;
+
+ if (read_ref(arg, &oid)) {
+ packet_write_fmt(1, "ERR unknown ref %s", arg);
+ die("unknown ref %s", arg);
+ }
+
+ item = string_list_append(wanted_refs, arg);
+ item->util = oiddup(&oid);
+
+ o = parse_object_or_die(&oid, arg);
+ 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;
{
while (packet_reader_read(request) != PACKET_READ_FLUSH) {
const char *arg = request->line;
+ const char *p;
/* process want */
if (parse_want(arg))
continue;
+ if (allow_ref_in_want && parse_want_ref(arg, &data->wanted_refs))
+ continue;
/* process have line */
if (parse_have(arg, &data->haves))
continue;
continue;
}
+ if (allow_filter && skip_prefix(arg, "filter ", &p)) {
+ parse_list_objects_filter(&filter_options, p);
+ continue;
+ }
+
/* ignore unknown lines maybe? */
- die("unexpect line: '%s'", arg);
+ die("unexpected line: '%s'", arg);
}
}
oid_array_append(common, oid);
- o = parse_object(oid);
+ o = parse_object(the_repository, oid);
if (!o)
die("oops (%s)", oid_to_hex(oid));
if (o->type == OBJ_COMMIT) {
return ret;
}
+static void send_wanted_ref_info(struct upload_pack_data *data)
+{
+ const struct string_list_item *item;
+
+ if (!data->wanted_refs.nr)
+ return;
+
+ packet_write_fmt(1, "wanted-refs\n");
+
+ for_each_string_list_item(item, &data->wanted_refs) {
+ packet_write_fmt(1, "%s %s\n",
+ oid_to_hex(item->util),
+ item->string);
+ }
+
+ packet_delim(1);
+}
+
static void send_shallow_info(struct upload_pack_data *data)
{
/* No shallow info needs to be sent */
if (!data->depth && !data->deepen_rev_list && !data->shallows.nr &&
- !is_repository_shallow())
+ !is_repository_shallow(the_repository))
return;
packet_write_fmt(1, "shallow-info\n");
if (!send_shallow_list(data->depth, data->deepen_rev_list,
data->deepen_since, &data->deepen_not,
- &data->shallows) && is_repository_shallow())
+ &data->shallows) &&
+ is_repository_shallow(the_repository))
deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows);
packet_delim(1);
enum fetch_state state = FETCH_PROCESS_ARGS;
struct upload_pack_data data;
+ git_config(upload_pack_config, NULL);
+
upload_pack_data_init(&data);
use_sideband = LARGE_PACKET_MAX;
state = FETCH_DONE;
break;
case FETCH_SEND_PACK:
+ send_wanted_ref_info(&data);
send_shallow_info(&data);
packet_write_fmt(1, "packfile\n");
int upload_pack_advertise(struct repository *r,
struct strbuf *value)
{
- if (value)
+ if (value) {
+ int allow_filter_value;
+ int allow_ref_in_want;
+
strbuf_addstr(value, "shallow");
+
+ if (!repo_config_get_bool(the_repository,
+ "uploadpack.allowfilter",
+ &allow_filter_value) &&
+ allow_filter_value)
+ strbuf_addstr(value, " filter");
+
+ if (!repo_config_get_bool(the_repository,
+ "uploadpack.allowrefinwant",
+ &allow_ref_in_want) &&
+ allow_ref_in_want)
+ strbuf_addstr(value, " ref-in-want");
+ }
+
return 1;
}