Merge branch 'mm/fetch-show-error-message-on-unadvertised-object' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 20:52:22 +0000 (13:52 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 20:52:22 +0000 (13:52 -0700)
"git fetch" that requests a commit by object name, when the other
side does not allow such an request, failed without much
explanation.

* mm/fetch-show-error-message-on-unadvertised-object:
fetch-pack: add specific error for fetching an unadvertised object
fetch_refs_via_pack: call report_unmatched_refs
fetch-pack: move code to report unmatched refs to a function

1  2 
remote.h
t/t5516-fetch-push.sh
transport.c
diff --combined remote.h
index a5bbbe0ef965c5e62be50e8ee0c03794e393cc8c,0b9d8c45895262a751f3f7f01a192f75eb007503..dd8c5175776baabbac2bd735e21bc859fb62a111
+++ b/remote.h
@@@ -15,7 -15,7 +15,7 @@@ struct remote 
        struct hashmap_entry ent;  /* must be first */
  
        const char *name;
 -      int origin;
 +      int origin, configured_in_repo;
  
        const char *foreign_vcs;
  
@@@ -60,7 -60,7 +60,7 @@@
  
  struct remote *remote_get(const char *name);
  struct remote *pushremote_get(const char *name);
 -int remote_is_configured(struct remote *remote);
 +int remote_is_configured(struct remote *remote, int in_repo);
  
  typedef int each_remote_fn(struct remote *remote, void *priv);
  int for_each_remote(each_remote_fn fn, void *priv);
@@@ -89,8 -89,13 +89,13 @@@ struct ref 
                force:1,
                forced_update:1,
                expect_old_sha1:1,
-               deletion:1,
-               matched:1;
+               deletion:1;
+       enum {
+               REF_NOT_MATCHED = 0, /* initial value */
+               REF_MATCHED,
+               REF_UNADVERTISED_NOT_ALLOWED
+       } match_status;
  
        /*
         * Order is important here, as we write to FETCH_HEAD
diff --combined t/t5516-fetch-push.sh
index 0fc5a7c596b5d5e01ecb43f81d3d6eaafe611b7b,78f3b8ef22f919e98bb668686aa265c3f2688f74..177897ea0b1e00cc4ec0f0e43510411ab19f1681
@@@ -1004,7 -1004,7 +1004,7 @@@ test_expect_success 'push --porcelain' 
  test_expect_success 'push --porcelain bad url' '
        mk_empty testrepo &&
        test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
 -      test_must_fail grep -q Done .git/bar
 +      ! grep -q Done .git/bar
  '
  
  test_expect_success 'push --porcelain rejected' '
@@@ -1098,7 -1098,8 +1098,8 @@@ test_expect_success 'fetch exact SHA1' 
                test_must_fail git cat-file -t $the_commit &&
  
                # fetching the hidden object should fail by default
-               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
+               test_i18ngrep "Server does not allow request for unadvertised object" err &&
                test_must_fail git rev-parse --verify refs/heads/copy &&
  
                # the server side can allow it to succeed
diff --combined transport.c
index b6c5652d6cb5eb9b0c1d3ed80ad5034ce7f9b5dc,c377907d4fdb99709c8164ee8708b34f7e16fd61..b58f5c8cdc784bffd6de57c476f6e825411ee589
@@@ -204,6 -204,7 +204,7 @@@ static struct ref *get_refs_via_connect
  static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
  {
+       int ret = 0;
        struct git_transport_data *data = transport->data;
        struct ref *refs;
        char *dest = xstrdup(transport->url);
                          &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
-       if (finish_connect(data->conn)) {
-               free_refs(refs);
-               refs = NULL;
-       }
+       if (finish_connect(data->conn))
+               ret = -1;
        data->conn = NULL;
        data->got_remote_heads = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
  
+       if (refs == NULL)
+               ret = -1;
+       if (report_unmatched_refs(to_fetch, nr_heads))
+               ret = -1;
        free_refs(refs_tmp);
        free_refs(refs);
        free(dest);
-       return (refs ? 0 : -1);
+       return ret;
  }
  
  static int push_had_errors(struct ref *ref)
@@@ -664,89 -668,21 +668,89 @@@ static const struct string_list *protoc
        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)
@@@ -1015,9 -951,7 +1019,9 @@@ int transport_push(struct transport *tr
                        if (run_pre_push_hook(transport, remote_refs))
                                return -1;
  
 -              if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
 +              if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
 +                            TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&
 +                  !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        struct sha1_array commits = SHA1_ARRAY_INIT;
  
                }
  
                if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||
 -                   ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) &&
 +                   ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
 +                              TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&
                      !pretend)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
                        sha1_array_clear(&commits);
                }
  
 -              push_ret = transport->push_refs(transport, remote_refs, flags);
 +              if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY))
 +                      push_ret = transport->push_refs(transport, remote_refs, flags);
 +              else
 +                      push_ret = 0;
                err = push_had_errors(remote_refs);
                ret = push_ret | err;
  
                if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
                        set_upstreams(transport, remote_refs, pretend);
  
 -              if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
 +              if (!(flags & (TRANSPORT_PUSH_DRY_RUN |
 +                             TRANSPORT_RECURSE_SUBMODULES_ONLY))) {
                        struct ref *ref;
                        for (ref = remote_refs; ref; ref = ref->next)
                                transport_update_tracking_ref(transport->remote, ref, verbose);
@@@ -1221,7 -1150,7 +1225,7 @@@ static int refs_from_alternate_cb(struc
        const struct ref *extra;
        struct alternate_refs_data *cb = data;
  
 -      other = xstrdup(real_path(e->path));
 +      other = real_pathdup(e->path, 1);
        len = strlen(other);
  
        while (other[len-1] == '/')