Merge branch 'tq/branch-style-fix'
[gitweb.git] / upload-pack.c
index 87b4d32a6e23aed2416a9bb752dfa56b916b141c..5dc317c3b437952963f974b0faaaffdb0176fbca 100644 (file)
@@ -3,6 +3,8 @@
 #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)
@@ -64,6 +67,7 @@ static const char *pack_objects_hook;
 
 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)
@@ -310,7 +314,7 @@ static int got_oid(const char *hex, struct object_id *oid)
        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) {
@@ -334,64 +338,16 @@ static int got_oid(const char *hex, struct object_id *oid)
        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)
@@ -444,7 +400,7 @@ 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)
@@ -486,8 +442,9 @@ static int do_reachable_revlist(struct child_process *cmd,
                "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;
@@ -506,7 +463,7 @@ static int do_reachable_revlist(struct child_process *cmd,
                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)
@@ -515,11 +472,11 @@ static int do_reachable_revlist(struct child_process *cmd,
                        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)) {
@@ -529,8 +486,8 @@ static int do_reachable_revlist(struct child_process *cmd,
                }
                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);
@@ -555,18 +512,20 @@ static int get_reachable_list(struct object_array *src,
        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;
                }
@@ -656,7 +615,7 @@ static void send_shallow(struct commit_list *result)
                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;
@@ -693,14 +652,14 @@ static void send_unshallow(const struct object_array *shallows)
                        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++) {
@@ -735,6 +694,7 @@ static void deepen_by_rev_list(int ac, const char **av,
 {
        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);
@@ -780,7 +740,8 @@ static int send_shallow_list(int depth, int deepen_rev_list,
                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);
                }
        }
 
@@ -796,7 +757,7 @@ static int process_shallow(const char *line, struct object_array *shallows)
                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)
@@ -895,11 +856,9 @@ static void receive_needs(void)
                }
 
                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;
@@ -924,7 +883,7 @@ static void receive_needs(void)
                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",
@@ -1075,6 +1034,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
                        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");
 }
@@ -1114,6 +1075,7 @@ void upload_pack(struct upload_pack_options *options)
 
 struct upload_pack_data {
        struct object_array wants;
+       struct string_list wanted_refs;
        struct oid_array haves;
 
        struct object_array shallows;
@@ -1135,12 +1097,14 @@ struct upload_pack_data {
 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;
@@ -1149,6 +1113,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
 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);
@@ -1165,7 +1130,7 @@ static int parse_want(const char *line)
                        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",
@@ -1185,6 +1150,34 @@ static int parse_want(const char *line)
        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;
@@ -1205,10 +1198,13 @@ static void process_args(struct packet_reader *request,
 {
        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;
@@ -1251,8 +1247,13 @@ static void process_args(struct packet_reader *request,
                        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);
        }
 }
 
@@ -1271,7 +1272,7 @@ static int process_haves(struct oid_array *haves, struct oid_array *common)
 
                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) {
@@ -1346,18 +1347,37 @@ static int process_haves_and_send_acks(struct upload_pack_data *data)
        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);
@@ -1376,6 +1396,8 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
        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;
 
@@ -1410,6 +1432,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
                                state = FETCH_DONE;
                        break;
                case FETCH_SEND_PACK:
+                       send_wanted_ref_info(&data);
                        send_shallow_info(&data);
 
                        packet_write_fmt(1, "packfile\n");
@@ -1428,7 +1451,24 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
 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;
 }