+static void send_shallow(struct upload_pack_data *data,
+ struct commit_list *result)
+{
+ while (result) {
+ struct object *object = &result->item->object;
+ if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
+ packet_writer_write(&data->writer, "shallow %s",
+ oid_to_hex(&object->oid));
+ register_shallow(the_repository, &object->oid);
+ data->shallow_nr++;
+ }
+ result = result->next;
+ }
+}
+
+static void send_unshallow(struct upload_pack_data *data)
+{
+ int i;
+
+ for (i = 0; i < data->shallows.nr; i++) {
+ struct object *object = data->shallows.objects[i].item;
+ if (object->flags & NOT_SHALLOW) {
+ struct commit_list *parents;
+ packet_writer_write(&data->writer, "unshallow %s",
+ oid_to_hex(&object->oid));
+ object->flags &= ~CLIENT_SHALLOW;
+ /*
+ * We want to _register_ "object" as shallow, but we
+ * also need to traverse object's parents to deepen a
+ * shallow clone. Unregister it for now so we can
+ * parse and add the parents to the want list, then
+ * re-register it.
+ */
+ unregister_shallow(&object->oid);
+ object->parsed = 0;
+ parse_commit_or_die((struct commit *)object);
+ parents = ((struct commit *)object)->parents;
+ while (parents) {
+ add_object_array(&parents->item->object,
+ NULL, &data->want_obj);
+ parents = parents->next;
+ }
+ add_object_array(object, NULL, &data->extra_edge_obj);
+ }
+ /* make sure commit traversal conforms to client */
+ register_shallow(the_repository, &object->oid);
+ }
+}
+
+static int check_ref(const char *refname_full, const struct object_id *oid,
+ int flag, void *cb_data);
+static void deepen(struct upload_pack_data *data, int depth)
+{
+ if (depth == INFINITE_DEPTH && !is_repository_shallow(the_repository)) {
+ int i;
+
+ for (i = 0; i < data->shallows.nr; i++) {
+ 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.
+ */
+ head_ref_namespaced(check_ref, NULL);
+ for_each_namespaced_ref(check_ref, NULL);
+
+ 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,
+ SHALLOW, NOT_SHALLOW);
+ send_shallow(data, result);
+ free_commit_list(result);
+ }
+
+ send_unshallow(data);
+}
+
+static void deepen_by_rev_list(struct upload_pack_data *data,
+ int ac,
+ const char **av)
+{
+ struct commit_list *result;
+
+ disable_commit_graph(the_repository);
+ result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
+ send_shallow(data, result);
+ free_commit_list(result);
+ send_unshallow(data);
+}
+
+/* Returns 1 if a shallow list is sent or 0 otherwise */
+static int send_shallow_list(struct upload_pack_data *data)
+{
+ int ret = 0;
+
+ if (data->depth > 0 && data->deepen_rev_list)
+ die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
+ if (data->depth > 0) {
+ deepen(data, data->depth);
+ ret = 1;
+ } else if (data->deepen_rev_list) {
+ struct strvec av = STRVEC_INIT;
+ int i;
+
+ strvec_push(&av, "rev-list");
+ if (data->deepen_since)
+ strvec_pushf(&av, "--max-age=%"PRItime, data->deepen_since);
+ if (data->deepen_not.nr) {
+ strvec_push(&av, "--not");
+ for (i = 0; i < data->deepen_not.nr; i++) {
+ struct string_list_item *s = data->deepen_not.items + i;
+ strvec_push(&av, s->string);
+ }
+ strvec_push(&av, "--not");
+ }
+ for (i = 0; i < data->want_obj.nr; i++) {
+ struct object *o = data->want_obj.objects[i].item;
+ strvec_push(&av, oid_to_hex(&o->oid));
+ }
+ deepen_by_rev_list(data, av.nr, av.v);
+ strvec_clear(&av);
+ ret = 1;
+ } else {
+ if (data->shallows.nr > 0) {
+ int i;
+ for (i = 0; i < data->shallows.nr; i++)
+ register_shallow(the_repository,
+ &data->shallows.objects[i].item->oid);
+ }
+ }
+
+ data->shallow_nr += data->shallows.nr;
+ return ret;
+}
+
+static int process_shallow(const char *line, struct object_array *shallows)
+{
+ const char *arg;
+ if (skip_prefix(line, "shallow ", &arg)) {
+ struct object_id oid;
+ struct object *object;
+ if (get_oid_hex(arg, &oid))
+ die("invalid shallow line: %s", line);
+ object = parse_object(the_repository, &oid);
+ if (!object)
+ return 1;
+ if (object->type != OBJ_COMMIT)
+ die("invalid shallow object %s", oid_to_hex(&oid));
+ if (!(object->flags & CLIENT_SHALLOW)) {
+ object->flags |= CLIENT_SHALLOW;
+ add_object_array(object, NULL, shallows);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static int process_deepen(const char *line, int *depth)
+{
+ const char *arg;
+ if (skip_prefix(line, "deepen ", &arg)) {
+ char *end = NULL;
+ *depth = (int)strtol(arg, &end, 0);
+ if (!end || *end || *depth <= 0)
+ die("Invalid deepen: %s", line);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int process_deepen_since(const char *line, timestamp_t *deepen_since, int *deepen_rev_list)
+{
+ const char *arg;
+ if (skip_prefix(line, "deepen-since ", &arg)) {
+ char *end = NULL;
+ *deepen_since = parse_timestamp(arg, &end, 0);
+ if (!end || *end || !deepen_since ||
+ /* revisions.c's max_age -1 is special */
+ *deepen_since == -1)
+ die("Invalid deepen-since: %s", line);
+ *deepen_rev_list = 1;
+ return 1;
+ }
+ return 0;
+}
+
+static int process_deepen_not(const char *line, struct string_list *deepen_not, int *deepen_rev_list)
+{
+ const char *arg;
+ if (skip_prefix(line, "deepen-not ", &arg)) {
+ char *ref = NULL;
+ struct object_id oid;
+ if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1)
+ die("git upload-pack: ambiguous deepen-not: %s", line);
+ string_list_append(deepen_not, ref);
+ free(ref);
+ *deepen_rev_list = 1;
+ return 1;
+ }
+ return 0;
+}
+
+NORETURN __attribute__((format(printf,2,3)))
+static void send_err_and_die(struct upload_pack_data *data,
+ const char *fmt, ...)
+{
+ struct strbuf buf = STRBUF_INIT;
+ va_list ap;
+
+ va_start(ap, fmt);
+ strbuf_vaddf(&buf, fmt, ap);
+ va_end(ap);
+
+ packet_writer_error(&data->writer, "%s", buf.buf);
+ die("%s", buf.buf);
+}
+
+static void check_one_filter(struct upload_pack_data *data,
+ struct list_objects_filter_options *opts)
+{
+ const char *key = list_object_filter_config_name(opts->choice);
+ struct string_list_item *item = string_list_lookup(&data->allowed_filters,
+ key);
+ int allowed;
+
+ if (item)
+ allowed = (intptr_t)item->util;
+ else
+ allowed = data->allow_filter_fallback;
+
+ if (!allowed)
+ send_err_and_die(data, "filter '%s' not supported", key);
+
+ if (opts->choice == LOFC_TREE_DEPTH &&
+ opts->tree_exclude_depth > data->tree_filter_max_depth)
+ send_err_and_die(data,
+ "tree filter allows max depth %lu, but got %lu",
+ data->tree_filter_max_depth,
+ opts->tree_exclude_depth);
+}
+
+static void check_filter_recurse(struct upload_pack_data *data,
+ struct list_objects_filter_options *opts)
+{
+ size_t i;
+
+ check_one_filter(data, opts);
+ if (opts->choice != LOFC_COMBINE)
+ return;
+
+ for (i = 0; i < opts->sub_nr; i++)
+ check_filter_recurse(data, &opts->sub[i]);
+}
+
+static void die_if_using_banned_filter(struct upload_pack_data *data)
+{
+ check_filter_recurse(data, &data->filter_options);
+}
+
+static void receive_needs(struct upload_pack_data *data,
+ struct packet_reader *reader)