t7610: run 'git reset --hard' after each test to clean up
[gitweb.git] / transport.c
index 94d6dc3725a2bb091df66ce6fe266ddd5317ed02..3e8799a6111b3c8672e4068450c9d225f3fcccc5 100644 (file)
@@ -151,6 +151,15 @@ static int set_git_option(struct git_transport_options *opts,
                                die(_("transport: invalid depth option '%s'"), value);
                }
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) {
+               opts->deepen_since = value;
+               return 0;
+       } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {
+               opts->deepen_not = (const struct string_list *)value;
+               return 0;
+       } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
+               opts->deepen_relative = !!value;
+               return 0;
        }
        return 1;
 }
@@ -211,6 +220,9 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.quiet = (transport->verbose < 0);
        args.no_progress = !transport->progress;
        args.depth = data->options.depth;
+       args.deepen_since = data->options.deepen_since;
+       args.deepen_not = data->options.deepen_not;
+       args.deepen_relative = data->options.deepen_relative;
        args.check_self_contained_and_connected =
                data->options.check_self_contained_and_connected;
        args.cloning = transport->cloning;
@@ -295,7 +307,9 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v
        }
 }
 
-static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain)
+static void print_ref_status(char flag, const char *summary,
+                            struct ref *to, struct ref *from, const char *msg,
+                            int porcelain, int summary_width)
 {
        if (porcelain) {
                if (from)
@@ -307,7 +321,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
                else
                        fprintf(stdout, "%s\n", summary);
        } else {
-               fprintf(stderr, " %c %-*s ", flag, TRANSPORT_SUMMARY_WIDTH, summary);
+               fprintf(stderr, " %c %-*s ", flag, summary_width, summary);
                if (from)
                        fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
                else
@@ -321,15 +335,16 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str
        }
 }
 
-static void print_ok_ref_status(struct ref *ref, int porcelain)
+static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_width)
 {
        if (ref->deletion)
-               print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
+               print_ref_status('-', "[deleted]", ref, NULL, NULL,
+                                porcelain, summary_width);
        else if (is_null_oid(&ref->old_oid))
                print_ref_status('*',
                        (starts_with(ref->name, "refs/tags/") ? "[new tag]" :
                        "[new branch]"),
-                       ref, ref->peer_ref, NULL, porcelain);
+                       ref, ref->peer_ref, NULL, porcelain, summary_width);
        else {
                struct strbuf quickref = STRBUF_INIT;
                char type;
@@ -349,12 +364,14 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
                strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash,
                                         DEFAULT_ABBREV);
 
-               print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);
+               print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg,
+                                porcelain, summary_width);
                strbuf_release(&quickref);
        }
 }
 
-static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
+static int print_one_push_status(struct ref *ref, const char *dest, int count,
+                                int porcelain, int summary_width)
 {
        if (!count) {
                char *url = transport_anonymize_url(dest);
@@ -364,62 +381,87 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
 
        switch(ref->status) {
        case REF_STATUS_NONE:
-               print_ref_status('X', "[no match]", ref, NULL, NULL, porcelain);
+               print_ref_status('X', "[no match]", ref, NULL, NULL,
+                                porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_NODELETE:
                print_ref_status('!', "[rejected]", ref, NULL,
-                                                "remote does not support deleting refs", porcelain);
+                                "remote does not support deleting refs",
+                                porcelain, summary_width);
                break;
        case REF_STATUS_UPTODATE:
                print_ref_status('=', "[up to date]", ref,
-                                                ref->peer_ref, NULL, porcelain);
+                                ref->peer_ref, NULL, porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "non-fast-forward", porcelain);
+                                "non-fast-forward", porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_ALREADY_EXISTS:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "already exists", porcelain);
+                                "already exists", porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_FETCH_FIRST:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "fetch first", porcelain);
+                                "fetch first", porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_NEEDS_FORCE:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "needs force", porcelain);
+                                "needs force", porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_STALE:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "stale info", porcelain);
+                                "stale info", porcelain, summary_width);
                break;
        case REF_STATUS_REJECT_SHALLOW:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "new shallow roots not allowed", porcelain);
+                                "new shallow roots not allowed",
+                                porcelain, summary_width);
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
-                                                ref->deletion ? NULL : ref->peer_ref,
-                                                ref->remote_status, porcelain);
+                                ref->deletion ? NULL : ref->peer_ref,
+                                ref->remote_status, porcelain, summary_width);
                break;
        case REF_STATUS_EXPECTING_REPORT:
                print_ref_status('!', "[remote failure]", ref,
-                                                ref->deletion ? NULL : ref->peer_ref,
-                                                "remote failed to report status", porcelain);
+                                ref->deletion ? NULL : ref->peer_ref,
+                                "remote failed to report status",
+                                porcelain, summary_width);
                break;
        case REF_STATUS_ATOMIC_PUSH_FAILED:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "atomic push failed", porcelain);
+                                "atomic push failed", porcelain, summary_width);
                break;
        case REF_STATUS_OK:
-               print_ok_ref_status(ref, porcelain);
+               print_ok_ref_status(ref, porcelain, summary_width);
                break;
        }
 
        return 1;
 }
 
+static int measure_abbrev(const struct object_id *oid, int sofar)
+{
+       char hex[GIT_SHA1_HEXSZ + 1];
+       int w = find_unique_abbrev_r(hex, oid->hash, DEFAULT_ABBREV);
+
+       return (w < sofar) ? sofar : w;
+}
+
+int transport_summary_width(const struct ref *refs)
+{
+       int maxw = -1;
+
+       for (; refs; refs = refs->next) {
+               maxw = measure_abbrev(&refs->old_oid, maxw);
+               maxw = measure_abbrev(&refs->new_oid, maxw);
+       }
+       if (maxw < 0)
+               maxw = FALLBACK_DEFAULT_ABBREV;
+       return (2 * maxw + 3);
+}
+
 void transport_print_push_status(const char *dest, struct ref *refs,
                                  int verbose, int porcelain, unsigned int *reject_reasons)
 {
@@ -427,25 +469,29 @@ void transport_print_push_status(const char *dest, struct ref *refs,
        int n = 0;
        unsigned char head_sha1[20];
        char *head;
+       int summary_width = transport_summary_width(refs);
 
        head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
 
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
                        if (ref->status == REF_STATUS_UPTODATE)
-                               n += print_one_push_status(ref, dest, n, porcelain);
+                               n += print_one_push_status(ref, dest, n,
+                                                          porcelain, summary_width);
        }
 
        for (ref = refs; ref; ref = ref->next)
                if (ref->status == REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n, porcelain);
+                       n += print_one_push_status(ref, dest, n,
+                                                  porcelain, summary_width);
 
        *reject_reasons = 0;
        for (ref = refs; ref; ref = ref->next) {
                if (ref->status != REF_STATUS_NONE &&
                    ref->status != REF_STATUS_UPTODATE &&
                    ref->status != REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n, porcelain);
+                       n += print_one_push_status(ref, dest, n,
+                                                  porcelain, summary_width);
                if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
                        if (head != NULL && !strcmp(head, ref->name))
                                *reject_reasons |= REJECT_NON_FF_HEAD;
@@ -618,21 +664,89 @@ static const struct string_list *protocol_whitelist(void)
        return enabled ? &allowed : NULL;
 }
 
-int is_transport_allowed(const char *type)
+enum protocol_allow_config {
+       PROTOCOL_ALLOW_NEVER = 0,
+       PROTOCOL_ALLOW_USER_ONLY,
+       PROTOCOL_ALLOW_ALWAYS
+};
+
+static enum protocol_allow_config parse_protocol_config(const char *key,
+                                                       const char *value)
 {
-       const struct string_list *allowed = protocol_whitelist();
-       return !allowed || string_list_has_string(allowed, type);
+       if (!strcasecmp(value, "always"))
+               return PROTOCOL_ALLOW_ALWAYS;
+       else if (!strcasecmp(value, "never"))
+               return PROTOCOL_ALLOW_NEVER;
+       else if (!strcasecmp(value, "user"))
+               return PROTOCOL_ALLOW_USER_ONLY;
+
+       die("unknown value for config '%s': %s", key, value);
 }
 
-void transport_check_allowed(const char *type)
+static enum protocol_allow_config get_protocol_config(const char *type)
 {
-       if (!is_transport_allowed(type))
-               die("transport '%s' not allowed", type);
+       char *key = xstrfmt("protocol.%s.allow", type);
+       char *value;
+
+       /* first check the per-protocol config */
+       if (!git_config_get_string(key, &value)) {
+               enum protocol_allow_config ret =
+                       parse_protocol_config(key, value);
+               free(key);
+               free(value);
+               return ret;
+       }
+       free(key);
+
+       /* if defined, fallback to user-defined default for unknown protocols */
+       if (!git_config_get_string("protocol.allow", &value)) {
+               enum protocol_allow_config ret =
+                       parse_protocol_config("protocol.allow", value);
+               free(value);
+               return ret;
+       }
+
+       /* fallback to built-in defaults */
+       /* known safe */
+       if (!strcmp(type, "http") ||
+           !strcmp(type, "https") ||
+           !strcmp(type, "git") ||
+           !strcmp(type, "ssh") ||
+           !strcmp(type, "file"))
+               return PROTOCOL_ALLOW_ALWAYS;
+
+       /* known scary; err on the side of caution */
+       if (!strcmp(type, "ext"))
+               return PROTOCOL_ALLOW_NEVER;
+
+       /* unknown; by default let them be used only directly by the user */
+       return PROTOCOL_ALLOW_USER_ONLY;
 }
 
-int transport_restrict_protocols(void)
+int is_transport_allowed(const char *type, int from_user)
 {
-       return !!protocol_whitelist();
+       const struct string_list *whitelist = protocol_whitelist();
+       if (whitelist)
+               return string_list_has_string(whitelist, type);
+
+       switch (get_protocol_config(type)) {
+       case PROTOCOL_ALLOW_ALWAYS:
+               return 1;
+       case PROTOCOL_ALLOW_NEVER:
+               return 0;
+       case PROTOCOL_ALLOW_USER_ONLY:
+               if (from_user < 0)
+                       from_user = git_env_bool("GIT_PROTOCOL_FROM_USER", 1);
+               return from_user;
+       }
+
+       die("BUG: invalid protocol_allow_config type");
+}
+
+void transport_check_allowed(const char *type)
+{
+       if (!is_transport_allowed(type, -1))
+               die("transport '%s' not allowed", type);
 }
 
 struct transport *transport_get(struct remote *remote, const char *url)
@@ -903,23 +1017,39 @@ int transport_push(struct transport *transport,
 
                if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
+                       struct sha1_array commits = SHA1_ARRAY_INIT;
+
                        for (; ref; ref = ref->next)
-                               if (!is_null_oid(&ref->new_oid) &&
-                                   !push_unpushed_submodules(ref->new_oid.hash,
-                                           transport->remote->name))
-                                   die ("Failed to push all needed submodules!");
+                               if (!is_null_oid(&ref->new_oid))
+                                       sha1_array_append(&commits, ref->new_oid.hash);
+
+                       if (!push_unpushed_submodules(&commits,
+                                                     transport->remote->name,
+                                                     pretend)) {
+                               sha1_array_clear(&commits);
+                               die("Failed to push all needed submodules!");
+                       }
+                       sha1_array_clear(&commits);
                }
 
-               if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
-                             TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
+               if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||
+                    ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) &&
+                     !pretend)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
+                       struct sha1_array commits = SHA1_ARRAY_INIT;
 
                        for (; ref; ref = ref->next)
-                               if (!is_null_oid(&ref->new_oid) &&
-                                   find_unpushed_submodules(ref->new_oid.hash,
-                                           transport->remote->name, &needs_pushing))
-                                       die_with_unpushed_submodules(&needs_pushing);
+                               if (!is_null_oid(&ref->new_oid))
+                                       sha1_array_append(&commits, ref->new_oid.hash);
+
+                       if (find_unpushed_submodules(&commits, transport->remote->name,
+                                               &needs_pushing)) {
+                               sha1_array_clear(&commits);
+                               die_with_unpushed_submodules(&needs_pushing);
+                       }
+                       string_list_clear(&needs_pushing, 0);
+                       sha1_array_clear(&commits);
                }
 
                push_ret = transport->push_refs(transport, remote_refs, flags);
@@ -1084,9 +1214,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
        const struct ref *extra;
        struct alternate_refs_data *cb = data;
 
-       e->name[-1] = '\0';
-       other = xstrdup(real_path(e->base));
-       e->name[-1] = '/';
+       other = xstrdup(real_path(e->path));
        len = strlen(other);
 
        while (other[len-1] == '/')