Code

Merge branch 'maint'
[git.git] / transport.c
index 97c59dce60eb01382bdd10e4d20cc476fd489cf0..3ff851935ff8d018a926084b89cb31bbb3d30a07 100644 (file)
@@ -203,7 +203,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport)
 }
 
 static int fetch_objs_via_rsync(struct transport *transport,
-                                int nr_objs, struct ref **to_fetch)
+                               int nr_objs, const struct ref **to_fetch)
 {
        struct strbuf buf = STRBUF_INIT;
        struct child_process rsync;
@@ -350,7 +350,7 @@ static int rsync_transport_push(struct transport *transport,
 
 #ifndef NO_CURL /* http fetch is the only user */
 static int fetch_objs_via_walker(struct transport *transport,
-                                int nr_objs, struct ref **to_fetch)
+                                int nr_objs, const struct ref **to_fetch)
 {
        char *dest = xstrdup(transport->url);
        struct walker *walker = transport->data;
@@ -441,10 +441,14 @@ static struct ref *get_refs_via_curl(struct transport *transport)
        struct ref *ref = NULL;
        struct ref *last_ref = NULL;
 
+       struct walker *walker;
+
        if (!transport->data)
                transport->data = get_http_walker(transport->url,
                                                transport->remote);
 
+       walker = transport->data;
+
        refs_url = xmalloc(strlen(transport->url) + 11);
        sprintf(refs_url, "%s/info/refs", transport->url);
 
@@ -500,11 +504,20 @@ static struct ref *get_refs_via_curl(struct transport *transport)
 
        strbuf_release(&buffer);
 
+       ref = alloc_ref_from_str("HEAD");
+       if (!walker->fetch_ref(walker, ref) &&
+           !resolve_remote_symref(ref, refs)) {
+               ref->next = refs;
+               refs = ref;
+       } else {
+               free(ref);
+       }
+
        return refs;
 }
 
 static int fetch_objs_via_curl(struct transport *transport,
-                                int nr_objs, struct ref **to_fetch)
+                                int nr_objs, const struct ref **to_fetch)
 {
        if (!transport->data)
                transport->data = get_http_walker(transport->url,
@@ -532,9 +545,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport)
                die ("Could not read bundle '%s'.", transport->url);
        for (i = 0; i < data->header.references.nr; i++) {
                struct ref_list_entry *e = data->header.references.list + i;
-               struct ref *ref = alloc_ref(strlen(e->name) + 1);
+               struct ref *ref = alloc_ref_from_str(e->name);
                hashcpy(ref->old_sha1, e->sha1);
-               strcpy(ref->name, e->name);
                ref->next = result;
                result = ref;
        }
@@ -542,7 +554,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport)
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
-                              int nr_heads, struct ref **to_fetch)
+                              int nr_heads, const struct ref **to_fetch)
 {
        struct bundle_transport_data *data = transport->data;
        return unbundle(&data->header, data->fd);
@@ -560,7 +572,10 @@ static int close_bundle(struct transport *transport)
 struct git_transport_data {
        unsigned thin : 1;
        unsigned keep : 1;
+       unsigned followtags : 1;
        int depth;
+       struct child_process *conn;
+       int fd[2];
        const char *uploadpack;
        const char *receivepack;
 };
@@ -578,6 +593,9 @@ static int set_git_option(struct transport *connection,
        } else if (!strcmp(name, TRANS_OPT_THIN)) {
                data->thin = !!value;
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
+               data->followtags = !!value;
+               return 0;
        } else if (!strcmp(name, TRANS_OPT_KEEP)) {
                data->keep = !!value;
                return 0;
@@ -591,52 +609,68 @@ static int set_git_option(struct transport *connection,
        return 1;
 }
 
+static int connect_setup(struct transport *transport)
+{
+       struct git_transport_data *data = transport->data;
+       data->conn = git_connect(data->fd, transport->url, data->uploadpack, 0);
+       return 0;
+}
+
 static struct ref *get_refs_via_connect(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        struct ref *refs;
-       int fd[2];
-       char *dest = xstrdup(transport->url);
-       struct child_process *conn = git_connect(fd, dest, data->uploadpack, 0);
-
-       get_remote_heads(fd[0], &refs, 0, NULL, 0);
-       packet_flush(fd[1]);
 
-       finish_connect(conn);
-
-       free(dest);
+       connect_setup(transport);
+       get_remote_heads(data->fd[0], &refs, 0, NULL, 0);
 
        return refs;
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
-                              int nr_heads, struct ref **to_fetch)
+                              int nr_heads, const struct ref **to_fetch)
 {
        struct git_transport_data *data = transport->data;
        char **heads = xmalloc(nr_heads * sizeof(*heads));
        char **origh = xmalloc(nr_heads * sizeof(*origh));
-       struct ref *refs;
+       const struct ref *refs;
        char *dest = xstrdup(transport->url);
        struct fetch_pack_args args;
        int i;
+       struct ref *refs_tmp = NULL;
 
        memset(&args, 0, sizeof(args));
        args.uploadpack = data->uploadpack;
        args.keep_pack = data->keep;
        args.lock_pack = 1;
        args.use_thin_pack = data->thin;
+       args.include_tag = data->followtags;
        args.verbose = transport->verbose > 0;
        args.depth = data->depth;
 
        for (i = 0; i < nr_heads; i++)
                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
-       refs = fetch_pack(&args, dest, nr_heads, heads, &transport->pack_lockfile);
+
+       if (!data->conn) {
+               connect_setup(transport);
+               get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0);
+       }
+
+       refs = fetch_pack(&args, data->fd, data->conn,
+                         refs_tmp ? refs_tmp : transport->remote_refs,
+                         dest, nr_heads, heads, &transport->pack_lockfile);
+       close(data->fd[0]);
+       close(data->fd[1]);
+       if (finish_connect(data->conn))
+               refs = NULL;
+       data->conn = NULL;
+
+       free_refs(refs_tmp);
 
        for (i = 0; i < nr_heads; i++)
                free(origh[i]);
        free(origh);
        free(heads);
-       free_refs(refs);
        free(dest);
        return (refs ? 0 : -1);
 }
@@ -659,7 +693,15 @@ static int git_transport_push(struct transport *transport, int refspec_nr, const
 
 static int disconnect_git(struct transport *transport)
 {
-       free(transport->data);
+       struct git_transport_data *data = transport->data;
+       if (data->conn) {
+               packet_flush(data->fd[1]);
+               close(data->fd[0]);
+               close(data->fd[1]);
+               finish_connect(data->conn);
+       }
+
+       free(data);
        return 0;
 }
 
@@ -719,6 +761,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                ret->disconnect = disconnect_git;
 
                data->thin = 1;
+               data->conn = NULL;
                data->uploadpack = "git-upload-pack";
                if (remote && remote->uploadpack)
                        data->uploadpack = remote->uploadpack;
@@ -753,12 +796,12 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
        return transport->remote_refs;
 }
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs)
+int transport_fetch_refs(struct transport *transport, const struct ref *refs)
 {
        int rc;
        int nr_heads = 0, nr_alloc = 0;
-       struct ref **heads = NULL;
-       struct ref *rm;
+       const struct ref **heads = NULL;
+       const struct ref *rm;
 
        for (rm = refs; rm; rm = rm->next) {
                if (rm->peer_ref &&