Commit 41fb1ca0 by Philip Kelley

Reorganize transport architecture (squashed 3)

parent a0ce87c5
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "git2/index.h" #include "git2/index.h"
#include "git2/config.h" #include "git2/config.h"
#include "git2/transport.h"
#include "git2/remote.h" #include "git2/remote.h"
#include "git2/clone.h" #include "git2/clone.h"
#include "git2/checkout.h" #include "git2/checkout.h"
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "net.h" #include "net.h"
#include "indexer.h" #include "indexer.h"
#include "strarray.h" #include "strarray.h"
#include "transport.h"
/** /**
* @file git2/remote.h * @file git2/remote.h
...@@ -283,10 +284,22 @@ GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const cha ...@@ -283,10 +284,22 @@ GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const cha
* @param remote the remote to configure * @param remote the remote to configure
* @param check whether to check the server's certificate (defaults to yes) * @param check whether to check the server's certificate (defaults to yes)
*/ */
GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check); GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
/** /**
* Sets a custom transport for the remote. The caller can use this function
* to bypass the automatic discovery of a transport by URL scheme (i.e.
* http://, https://, git://) and supply their own transport to be used
* instead. After providing the transport to a remote using this function,
* the transport object belongs exclusively to that remote, and the remote will
* free it when it is freed with git_remote_free.
*
* @param remote the remote to configure
* @param transport the transport object for the remote to use
*/
GIT_EXTERN(int) git_remote_set_transport(git_remote *remote, git_transport *transport);
/**
* Argument to the completion callback which tells it which operation * Argument to the completion callback which tells it which operation
* finished. * finished.
*/ */
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_transport_h__
#define INCLUDE_git_transport_h__
#include "indexer.h"
#include "net.h"
/**
* @file git2/transport.h
* @brief Git transport interfaces and functions
* @defgroup git_transport interfaces and functions
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/*
*** Begin base transport interface ***
*/
typedef enum {
GIT_TRANSPORTFLAGS_NONE = 0,
/* If the connection is secured with SSL/TLS, the authenticity
* of the server certificate should not be verified. */
GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
} git_transport_flags_t;
typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
typedef struct git_transport {
/* Set progress and error callbacks */
int (*set_callbacks)(struct git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
void *payload);
/* Connect the transport to the remote repository, using the given
* direction. */
int (*connect)(struct git_transport *transport,
const char *url,
int direction,
int flags);
/* This function may be called after a successful call to connect(). The
* provided callback is invoked for each ref discovered on the remote
* end. */
int (*ls)(struct git_transport *transport,
git_headlist_cb list_cb,
void *payload);
/* Reserved until push is implemented. */
int (*push)(struct git_transport *transport);
/* This function may be called after a successful call to connect(), when
* the direction is FETCH. The function performs a negotiation to calculate
* the wants list for the fetch. */
int (*negotiate_fetch)(struct git_transport *transport,
git_repository *repo,
const git_remote_head * const *refs,
size_t count);
/* This function may be called after a successful call to negotiate_fetch(),
* when the direction is FETCH. This function retrieves the pack file for
* the fetch from the remote end. */
int (*download_pack)(struct git_transport *transport,
git_repository *repo,
git_transfer_progress *stats,
git_transfer_progress_callback progress_cb,
void *progress_payload);
/* Checks to see if the transport is connected */
int (*is_connected)(struct git_transport *transport, int *connected);
/* Reads the flags value previously passed into connect() */
int (*read_flags)(struct git_transport *transport, int *flags);
/* Cancels any outstanding transport operation */
void (*cancel)(struct git_transport *transport);
/* This function is the reverse of connect() -- it terminates the
* connection to the remote end. */
int (*close)(struct git_transport *transport);
/* Frees/destructs the git_transport object. */
void (*free)(struct git_transport *transport);
} git_transport;
/**
* Function to use to create a transport from a URL. The transport database
* is scanned to find a transport that implements the scheme of the URI (i.e.
* git:// or http://) and a transport object is returned to the caller.
*
* @param transport The newly created transport (out)
* @param url The URL to connect to
* @return 0 or an error code
*/
GIT_EXTERN(int) git_transport_new(git_transport **transport, const char *url);
/**
* Function which checks to see if a transport could be created for the
* given URL (i.e. checks to see if libgit2 has a transport that supports
* the given URL's scheme)
*
* @param url The URL to check
* @return Zero if the URL is not valid; nonzero otherwise
*/
GIT_EXTERN(int) git_transport_valid_url(const char *url);
/* Signature of a function which creates a transport */
typedef int (*git_transport_cb)(git_transport **transport, void *param);
/* Transports which come with libgit2 (match git_transport_cb). The expected
* value for "param" is listed in-line below. */
/**
* Create an instance of the dummy transport.
*
* @param transport The newly created transport (out)
* @param param You must pass NULL for this parameter.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_transport_dummy(
git_transport **transport,
/* NULL */ void *param);
/**
* Create an instance of the local transport.
*
* @param transport The newly created transport (out)
* @param param You must pass NULL for this parameter.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_transport_local(
git_transport **transport,
/* NULL */ void *param);
/**
* Create an instance of the smart transport.
*
* @param transport The newly created transport (out)
* @param param A pointer to a git_smart_subtransport_definition
* @return 0 or an error code
*/
GIT_EXTERN(int) git_transport_smart(
git_transport **transport,
/* (git_smart_subtransport_definition *) */ void *param);
/*
*** End of base transport interface ***
*** Begin interface for subtransports for the smart transport ***
*/
/* The smart transport knows how to speak the git protocol, but it has no
* knowledge of how to establish a connection between it and another endpoint,
* or how to move data back and forth. For this, a subtransport interface is
* declared, and the smart transport delegates this work to the subtransports.
* Three subtransports are implemented: git, http, and winhttp. (The http and
* winhttp transports each implement both http and https.) */
/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1
* (request/response). The smart transport handles the differences in its own
* logic. The git subtransport is RPC = 0, while http and winhttp are both
* RPC = 1. */
/* Actions that the smart transport can ask
* a subtransport to perform */
typedef enum {
GIT_SERVICE_UPLOADPACK_LS = 1,
GIT_SERVICE_UPLOADPACK = 2,
} git_smart_service_t;
struct git_smart_subtransport;
/* A stream used by the smart transport to read and write data
* from a subtransport */
typedef struct git_smart_subtransport_stream {
/* The owning subtransport */
struct git_smart_subtransport *subtransport;
int (*read)(
struct git_smart_subtransport_stream *stream,
char *buffer,
size_t buf_size,
size_t *bytes_read);
int (*write)(
struct git_smart_subtransport_stream *stream,
const char *buffer,
size_t len);
void (*free)(
struct git_smart_subtransport_stream *stream);
} git_smart_subtransport_stream;
/* An implementation of a subtransport which carries data for the
* smart transport */
typedef struct git_smart_subtransport {
int (* action)(
git_smart_subtransport_stream **out,
struct git_smart_subtransport *transport,
const char *url,
git_smart_service_t action);
void (* free)(struct git_smart_subtransport *transport);
} git_smart_subtransport;
/* A function which creates a new subtransport for the smart transport */
typedef int (*git_smart_subtransport_cb)(
git_smart_subtransport **out,
git_transport* owner);
typedef struct git_smart_subtransport_definition {
/* The function to use to create the git_smart_subtransport */
git_smart_subtransport_cb callback;
/* True if the protocol is stateless; false otherwise. For example,
* http:// is stateless, but git:// is not. */
unsigned rpc : 1;
} git_smart_subtransport_definition;
/* Smart transport subtransports that come with libgit2 */
/**
* Create an instance of the http subtransport. This subtransport
* also supports https. On Win32, this subtransport may be implemented
* using the WinHTTP library.
*
* @param out The newly created subtransport
* @param owner The smart transport to own this subtransport
* @return 0 or an error code
*/
GIT_EXTERN(int) git_smart_subtransport_http(
git_smart_subtransport **out,
git_transport* owner);
/**
* Create an instance of the git subtransport.
*
* @param out The newly created subtransport
* @param owner The smart transport to own this subtransport
* @return 0 or an error code
*/
GIT_EXTERN(int) git_smart_subtransport_git(
git_smart_subtransport **out,
git_transport* owner);
/*
*** End interface for subtransports for the smart transport ***
*/
/** @} */
GIT_END_DECL
#endif
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "common.h" #include "common.h"
#include "remote.h" #include "remote.h"
#include "pkt.h"
#include "fileops.h" #include "fileops.h"
#include "refs.h" #include "refs.h"
#include "path.h" #include "path.h"
...@@ -171,11 +170,19 @@ static int update_head_to_new_branch( ...@@ -171,11 +170,19 @@ static int update_head_to_new_branch(
return error; return error;
} }
static int get_head_callback(git_remote_head *head, void *payload)
{
git_remote_head **destination = (git_remote_head **)payload;
/* Save the first entry, and terminate the enumeration */
*destination = head;
return 1;
}
static int update_head_to_remote(git_repository *repo, git_remote *remote) static int update_head_to_remote(git_repository *repo, git_remote *remote)
{ {
int retcode = -1; int retcode = -1;
git_remote_head *remote_head; git_remote_head *remote_head;
git_pkt_ref *pkt;
struct head_info head_info; struct head_info head_info;
git_buf remote_master_name = GIT_BUF_INIT; git_buf remote_master_name = GIT_BUF_INIT;
...@@ -189,8 +196,13 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) ...@@ -189,8 +196,13 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
} }
/* Get the remote's HEAD. This is always the first ref in remote->refs. */ /* Get the remote's HEAD. This is always the first ref in remote->refs. */
pkt = remote->transport->refs.contents[0]; remote_head = NULL;
remote_head = &pkt->head;
if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
return -1;
assert(remote_head);
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
git_buf_init(&head_info.branchname, 16); git_buf_init(&head_info.branchname, 16);
head_info.repo = repo; head_info.repo = repo;
......
...@@ -69,7 +69,4 @@ void giterr_set_regex(const regex_t *regex, int error_code); ...@@ -69,7 +69,4 @@ void giterr_set_regex(const regex_t *regex, int error_code);
#include "util.h" #include "util.h"
typedef struct git_transport git_transport;
typedef struct gitno_buffer gitno_buffer;
#endif /* INCLUDE_common_h__ */ #endif /* INCLUDE_common_h__ */
...@@ -10,33 +10,60 @@ ...@@ -10,33 +10,60 @@
#include "posix.h" #include "posix.h"
#include "common.h" #include "common.h"
#ifdef GIT_SSL
# include <openssl/ssl.h>
#endif
struct gitno_ssl {
#ifdef GIT_SSL
SSL_CTX *ctx;
SSL *ssl;
#else
size_t dummy;
#endif
};
typedef struct gitno_ssl gitno_ssl;
/* Represents a socket that may or may not be using SSL */
struct gitno_socket {
GIT_SOCKET socket;
gitno_ssl ssl;
};
typedef struct gitno_socket gitno_socket;
struct gitno_buffer { struct gitno_buffer {
char *data; char *data;
size_t len; size_t len;
size_t offset; size_t offset;
GIT_SOCKET fd; gitno_socket *socket;
#ifdef GIT_SSL int (*recv)(struct gitno_buffer *buffer);
struct gitno_ssl *ssl;
#endif
int (*recv)(gitno_buffer *buffer);
void *cb_data; void *cb_data;
void (*packetsize_cb)(int received, void *payload);
void *packetsize_payload;
}; };
void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, size_t len); typedef struct gitno_buffer gitno_buffer;
void gitno_buffer_setup_callback(git_transport *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
/* Flags to gitno_connect */
enum {
/* Attempt to create an SSL connection. */
GITNO_CONNECT_SSL = 1,
/* Valid only when GITNO_CONNECT_SSL is also specified.
* Indicates that the server certificate should not be validated. */
GITNO_CONNECT_SSL_NO_CHECK_CERT = 2,
};
void gitno_buffer_setup(gitno_socket *t, gitno_buffer *buf, char *data, size_t len);
void gitno_buffer_setup_callback(gitno_socket *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
int gitno_recv(gitno_buffer *buf); int gitno_recv(gitno_buffer *buf);
int gitno__recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons); void gitno_consume_n(gitno_buffer *buf, size_t cons);
int gitno_connect(git_transport *t, const char *host, const char *port); int gitno_connect(gitno_socket *socket, const char *host, const char *port, int flags);
int gitno_send(git_transport *t, const char *msg, size_t len, int flags); int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags);
int gitno_close(GIT_SOCKET s); int gitno_close(gitno_socket *s);
int gitno_ssl_teardown(git_transport *t);
int gitno_send_chunk_size(int s, size_t len);
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port); int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port);
......
...@@ -604,8 +604,8 @@ on_error: ...@@ -604,8 +604,8 @@ on_error:
static int send_pack_file(void *buf, size_t size, void *data) static int send_pack_file(void *buf, size_t size, void *data)
{ {
git_transport *t = (git_transport *)data; gitno_socket *s = (gitno_socket *)data;
return gitno_send(t, buf, size, 0); return gitno_send(s, buf, size, 0);
} }
static int write_pack_buf(void *buf, size_t size, void *data) static int write_pack_buf(void *buf, size_t size, void *data)
...@@ -1231,10 +1231,10 @@ static int prepare_pack(git_packbuilder *pb) ...@@ -1231,10 +1231,10 @@ static int prepare_pack(git_packbuilder *pb)
#define PREPARE_PACK if (prepare_pack(pb) < 0) { return -1; } #define PREPARE_PACK if (prepare_pack(pb) < 0) { return -1; }
int git_packbuilder_send(git_packbuilder *pb, git_transport *t) int git_packbuilder_send(git_packbuilder *pb, gitno_socket *s)
{ {
PREPARE_PACK; PREPARE_PACK;
return write_pack(pb, &send_pack_file, t); return write_pack(pb, &send_pack_file, s);
} }
int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb) int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "buffer.h" #include "buffer.h"
#include "hash.h" #include "hash.h"
#include "oidmap.h" #include "oidmap.h"
#include "netops.h"
#include "git2/oid.h" #include "git2/oid.h"
...@@ -81,7 +82,7 @@ struct git_packbuilder { ...@@ -81,7 +82,7 @@ struct git_packbuilder {
bool done; bool done;
}; };
int git_packbuilder_send(git_packbuilder *pb, git_transport *t); int git_packbuilder_send(git_packbuilder *pb, gitno_socket *s);
int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb); int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
#endif /* INCLUDE_pack_objects_h__ */ #endif /* INCLUDE_pack_objects_h__ */
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "protocol.h"
#include "pkt.h"
#include "buffer.h"
int git_protocol_store_refs(git_transport *t, int flushes)
{
gitno_buffer *buf = &t->buffer;
git_vector *refs = &t->refs;
int error, flush = 0, recvd;
const char *line_end;
git_pkt *pkt;
do {
if (buf->offset > 0)
error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset);
else
error = GIT_EBUFS;
if (error < 0 && error != GIT_EBUFS)
return -1;
if (error == GIT_EBUFS) {
if ((recvd = gitno_recv(buf)) < 0)
return -1;
if (recvd == 0 && !flush) {
giterr_set(GITERR_NET, "Early EOF");
return -1;
}
continue;
}
gitno_consume(buf, line_end);
if (pkt->type == GIT_PKT_ERR) {
giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
git__free(pkt);
return -1;
}
if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
return -1;
if (pkt->type == GIT_PKT_FLUSH) {
flush++;
git_pkt_free(pkt);
}
} while (flush < flushes);
return flush;
}
int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps)
{
const char *ptr;
/* No refs or capabilites, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL)
return 0;
ptr = pkt->capabilities;
while (ptr != NULL && *ptr != '\0') {
if (*ptr == ' ')
ptr++;
if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
caps->common = caps->ofs_delta = 1;
ptr += strlen(GIT_CAP_OFS_DELTA);
continue;
}
if(!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
caps->common = caps->multi_ack = 1;
ptr += strlen(GIT_CAP_MULTI_ACK);
continue;
}
if(!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
caps->common = caps->include_tag = 1;
ptr += strlen(GIT_CAP_INCLUDE_TAG);
continue;
}
/* Keep side-band check after side-band-64k */
if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
caps->common = caps->side_band_64k = 1;
ptr += strlen(GIT_CAP_SIDE_BAND_64K);
continue;
}
if(!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
caps->common = caps->side_band = 1;
ptr += strlen(GIT_CAP_SIDE_BAND);
continue;
}
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
return 0;
}
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_protocol_h__
#define INCLUDE_protocol_h__
#include "transport.h"
#include "buffer.h"
#include "pkt.h"
int git_protocol_store_refs(git_transport *t, int flushes);
int git_protocol_detect_caps(git_pkt_ref *pkt, git_transport_caps *caps);
#define GIT_SIDE_BAND_DATA 1
#define GIT_SIDE_BAND_PROGRESS 2
#define GIT_SIDE_BAND_ERROR 3
#endif
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "remote.h" #include "remote.h"
#include "fetch.h" #include "fetch.h"
#include "refs.h" #include "refs.h"
#include "pkt.h"
#include <regex.h> #include <regex.h>
...@@ -464,23 +463,30 @@ int git_remote_connect(git_remote *remote, int direction) ...@@ -464,23 +463,30 @@ int git_remote_connect(git_remote *remote, int direction)
{ {
git_transport *t; git_transport *t;
const char *url; const char *url;
int flags = GIT_TRANSPORTFLAGS_NONE;
assert(remote); assert(remote);
t = remote->transport;
url = git_remote__urlfordirection(remote, direction); url = git_remote__urlfordirection(remote, direction);
if (url == NULL ) if (url == NULL )
return -1; return -1;
if (git_transport_new(&t, url) < 0) /* A transport could have been supplied in advance with
* git_remote_set_transport */
if (!t && git_transport_new(&t, url) < 0)
return -1; return -1;
t->progress_cb = remote->callbacks.progress; if (t->set_callbacks &&
t->cb_data = remote->callbacks.data; t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.data) < 0)
goto on_error;
if (!remote->check_cert)
flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
t->check_cert = remote->check_cert; if (t->connect(t, url, direction, flags) < 0)
if (t->connect(t, direction) < 0) {
goto on_error; goto on_error;
}
remote->transport = t; remote->transport = t;
...@@ -493,30 +499,14 @@ on_error: ...@@ -493,30 +499,14 @@ on_error:
int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
{ {
git_vector *refs = &remote->transport->refs;
unsigned int i;
git_pkt *p = NULL;
assert(remote); assert(remote);
if (!remote->transport || !remote->transport->connected) { if (!remote->transport) {
giterr_set(GITERR_NET, "The remote is not connected"); giterr_set(GITERR_NET, "The remote is not connected");
return -1; return -1;
} }
git_vector_foreach(refs, i, p) { return remote->transport->ls(remote->transport, list_cb, payload);
git_pkt_ref *pkt = NULL;
if (p->type != GIT_PKT_REF)
continue;
pkt = (git_pkt_ref *)p;
if (list_cb(&pkt->head, payload))
return GIT_EUSER;
}
return 0;
} }
int git_remote_download( int git_remote_download(
...@@ -534,54 +524,61 @@ int git_remote_download( ...@@ -534,54 +524,61 @@ int git_remote_download(
return git_fetch_download_pack(remote, progress_cb, progress_payload); return git_fetch_download_pack(remote, progress_cb, progress_payload);
} }
static int update_tips_callback(git_remote_head *head, void *payload)
{
git_vector *refs = (git_vector *)payload;
git_vector_insert(refs, head);
return 0;
}
int git_remote_update_tips(git_remote *remote) int git_remote_update_tips(git_remote *remote)
{ {
int error = 0, autotag; int error = 0, autotag;
unsigned int i = 0; unsigned int i = 0;
git_buf refname = GIT_BUF_INIT; git_buf refname = GIT_BUF_INIT;
git_oid old; git_oid old;
git_pkt *pkt;
git_odb *odb; git_odb *odb;
git_vector *refs;
git_remote_head *head; git_remote_head *head;
git_reference *ref; git_reference *ref;
struct git_refspec *spec; struct git_refspec *spec;
git_refspec tagspec; git_refspec tagspec;
git_vector refs;
assert(remote); assert(remote);
refs = &remote->transport->refs;
spec = &remote->fetch; spec = &remote->fetch;
if (refs->length == 0)
return 0;
if (git_repository_odb__weakptr(&odb, remote->repo) < 0) if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
return -1; return -1;
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1; return -1;
/* HEAD is only allowed to be the first in the list */ /* Make a copy of the transport's refs */
pkt = refs->contents[0]; if (git_vector_init(&refs, 16, NULL) < 0)
head = &((git_pkt_ref *)pkt)->head; return -1;
if (!strcmp(head->name, GIT_HEAD_FILE)) {
if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
return -1;
i = 1; if (remote->transport->ls(remote->transport, update_tips_callback, &refs) < 0)
git_reference_free(ref); goto on_error;
/* Let's go find HEAD, if it exists. Check only the first ref in the vector. */
if (refs.length > 0) {
head = (git_remote_head *)refs.contents[0];
if (!strcmp(head->name, GIT_HEAD_FILE)) {
if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
goto on_error;
i = 1;
git_reference_free(ref);
}
} }
for (; i < refs->length; ++i) { for (; i < refs.length; ++i) {
git_pkt *pkt = refs->contents[i]; head = (git_remote_head *)refs.contents[i];
autotag = 0; autotag = 0;
if (pkt->type == GIT_PKT_REF)
head = &((git_pkt_ref *)pkt)->head;
else
continue;
/* Ignore malformed ref names (which also saves us from tag^{} */ /* Ignore malformed ref names (which also saves us from tag^{} */
if (!git_reference_is_valid_name(head->name)) if (!git_reference_is_valid_name(head->name))
continue; continue;
...@@ -630,11 +627,13 @@ int git_remote_update_tips(git_remote *remote) ...@@ -630,11 +627,13 @@ int git_remote_update_tips(git_remote *remote)
} }
} }
git_vector_free(&refs);
git_refspec__free(&tagspec); git_refspec__free(&tagspec);
git_buf_free(&refname); git_buf_free(&refname);
return 0; return 0;
on_error: on_error:
git_vector_free(&refs);
git_refspec__free(&tagspec); git_refspec__free(&tagspec);
git_buf_free(&refname); git_buf_free(&refname);
return -1; return -1;
...@@ -643,21 +642,31 @@ on_error: ...@@ -643,21 +642,31 @@ on_error:
int git_remote_connected(git_remote *remote) int git_remote_connected(git_remote *remote)
{ {
int connected;
assert(remote); assert(remote);
return remote->transport == NULL ? 0 : remote->transport->connected;
if (!remote->transport || !remote->transport->is_connected)
return 0;
/* Ask the transport if it's connected. */
remote->transport->is_connected(remote->transport, &connected);
return connected;
} }
void git_remote_stop(git_remote *remote) void git_remote_stop(git_remote *remote)
{ {
git_atomic_set(&remote->transport->cancel, 1); if (remote->transport->cancel)
remote->transport->cancel(remote->transport);
} }
void git_remote_disconnect(git_remote *remote) void git_remote_disconnect(git_remote *remote)
{ {
assert(remote); assert(remote);
if (remote->transport != NULL && remote->transport->connected) if (git_remote_connected(remote))
remote->transport->close(remote->transport); remote->transport->close(remote->transport);
} }
void git_remote_free(git_remote *remote) void git_remote_free(git_remote *remote)
...@@ -786,10 +795,24 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback ...@@ -786,10 +795,24 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback
memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks)); memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
if (remote->transport && remote->transport->set_callbacks)
remote->transport->set_callbacks(remote->transport,
remote->callbacks.progress,
NULL,
remote->callbacks.data);
}
int git_remote_set_transport(git_remote *remote, git_transport *transport)
{
assert(remote && transport);
if (remote->transport) { if (remote->transport) {
remote->transport->progress_cb = remote->callbacks.progress; giterr_set(GITERR_NET, "A transport is already bound to this remote");
remote->transport->cb_data = remote->callbacks.data; return -1;
} }
remote->transport = transport;
return 0;
} }
const git_transfer_progress* git_remote_stats(git_remote *remote) const git_transfer_progress* git_remote_stats(git_remote *remote)
......
...@@ -8,9 +8,9 @@ ...@@ -8,9 +8,9 @@
#define INCLUDE_remote_h__ #define INCLUDE_remote_h__
#include "git2/remote.h" #include "git2/remote.h"
#include "git2/transport.h"
#include "refspec.h" #include "refspec.h"
#include "transport.h"
#include "repository.h" #include "repository.h"
#define GIT_REMOTE_ORIGIN "origin" #define GIT_REMOTE_ORIGIN "origin"
......
...@@ -8,52 +8,78 @@ ...@@ -8,52 +8,78 @@
#include "git2/types.h" #include "git2/types.h"
#include "git2/remote.h" #include "git2/remote.h"
#include "git2/net.h" #include "git2/net.h"
#include "transport.h" #include "git2/transport.h"
#include "path.h" #include "path.h"
static struct { typedef struct transport_definition {
char *prefix; char *prefix;
unsigned priority;
git_transport_cb fn; git_transport_cb fn;
} transports[] = { void *param;
{"git://", git_transport_git}, } transport_definition;
{"http://", git_transport_http},
{"https://", git_transport_https}, static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
{"file://", git_transport_local}, static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
{"git+ssh://", git_transport_dummy},
{"ssh+git://", git_transport_dummy}, static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 };
{NULL, 0} static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 };
static transport_definition transports[] = {
{"git://", 1, git_transport_smart, &git_subtransport_definition},
{"http://", 1, git_transport_smart, &http_subtransport_definition},
{"https://", 1, git_transport_smart, &http_subtransport_definition},
{"file://", 1, git_transport_local, NULL},
{"git+ssh://", 1, git_transport_dummy, NULL},
{"ssh+git://", 1, git_transport_dummy, NULL},
{NULL, 0, 0}
}; };
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1 #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
static git_transport_cb transport_find_fn(const char *url) static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
{ {
size_t i = 0; size_t i = 0;
unsigned priority = 0;
transport_definition *definition = NULL, *definition_iter;
// First, check to see if it's an obvious URL, which a URL scheme // First, check to see if it's an obvious URL, which a URL scheme
for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix))) definition_iter = &transports[i];
return transports[i].fn;
if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
continue;
if (definition_iter->priority > priority)
definition = definition_iter;
} }
/* still here? Check to see if the path points to a file on the local file system */ if (!definition) {
if ((git_path_exists(url) == 0) && git_path_isdir(url)) /* still here? Check to see if the path points to a file on the local file system */
return &git_transport_local; if ((git_path_exists(url) == 0) && git_path_isdir(url))
definition = &local_transport_definition;
/* It could be a SSH remote path. Check to see if there's a : */
if (strrchr(url, ':'))
definition = &dummy_transport_definition; /* SSH is an unsupported transport mechanism in this version of libgit2 */
}
/* It could be a SSH remote path. Check to see if there's a : */ if (!definition)
if (strrchr(url, ':')) return -1;
return &git_transport_dummy; /* SSH is an unsupported transport mechanism in this version of libgit2 */
return NULL; *callback = definition->fn;
*param = definition->param;
return 0;
} }
/************** /**************
* Public API * * Public API *
**************/ **************/
int git_transport_dummy(git_transport **transport) int git_transport_dummy(git_transport **transport, void *param)
{ {
GIT_UNUSED(transport); GIT_UNUSED(transport);
GIT_UNUSED(param);
giterr_set(GITERR_NET, "This transport isn't implemented. Sorry"); giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
return -1; return -1;
} }
...@@ -62,22 +88,18 @@ int git_transport_new(git_transport **out, const char *url) ...@@ -62,22 +88,18 @@ int git_transport_new(git_transport **out, const char *url)
{ {
git_transport_cb fn; git_transport_cb fn;
git_transport *transport; git_transport *transport;
void *param;
int error; int error;
fn = transport_find_fn(url); if (transport_find_fn(url, &fn, &param) < 0) {
if (fn == NULL) {
giterr_set(GITERR_NET, "Unsupported URL protocol"); giterr_set(GITERR_NET, "Unsupported URL protocol");
return -1; return -1;
} }
error = fn(&transport); error = fn(&transport, param);
if (error < 0) if (error < 0)
return error; return error;
transport->url = git__strdup(url);
GITERR_CHECK_ALLOC(transport->url);
*out = transport; *out = transport;
return 0; return 0;
...@@ -86,12 +108,19 @@ int git_transport_new(git_transport **out, const char *url) ...@@ -86,12 +108,19 @@ int git_transport_new(git_transport **out, const char *url)
/* from remote.h */ /* from remote.h */
int git_remote_valid_url(const char *url) int git_remote_valid_url(const char *url)
{ {
return transport_find_fn(url) != NULL; git_transport_cb fn;
void *param;
return !transport_find_fn(url, &fn, &param);
} }
int git_remote_supported_url(const char* url) int git_remote_supported_url(const char* url)
{ {
git_transport_cb transport_fn = transport_find_fn(url); git_transport_cb fn;
void *param;
if (transport_find_fn(url, &fn, &param) < 0)
return 0;
return ((transport_fn != NULL) && (transport_fn != &git_transport_dummy)); return fn != &git_transport_dummy;
} }
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_transport_h__
#define INCLUDE_transport_h__
#include "git2/net.h"
#include "git2/indexer.h"
#include "vector.h"
#include "posix.h"
#include "common.h"
#include "netops.h"
#ifdef GIT_SSL
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif
#define GIT_CAP_OFS_DELTA "ofs-delta"
#define GIT_CAP_MULTI_ACK "multi_ack"
#define GIT_CAP_SIDE_BAND "side-band"
#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
#define GIT_CAP_INCLUDE_TAG "include-tag"
typedef struct git_transport_caps {
int common:1,
ofs_delta:1,
multi_ack: 1,
side_band:1,
side_band_64k:1,
include_tag:1;
} git_transport_caps;
#ifdef GIT_SSL
typedef struct gitno_ssl {
SSL_CTX *ctx;
SSL *ssl;
} gitno_ssl;
#endif
/*
* A day in the life of a network operation
* ========================================
*
* The library gets told to ls-remote/push/fetch on/to/from some
* remote. We look at the URL of the remote and fill the function
* table with whatever is appropriate (the remote may be git over git,
* ssh or http(s). It may even be an hg or svn repository, the library
* at this level doesn't care, it just calls the helpers.
*
* The first call is to ->connect() which connects to the remote,
* making use of the direction if necessary. This function must also
* store the remote heads and any other information it needs.
*
* The next useful step is to call ->ls() to get the list of
* references available to the remote. These references may have been
* collected on connect, or we may build them now. For ls-remote,
* nothing else is needed other than closing the connection.
* Otherwise, the higher leves decide which objects we want to
* have. ->send_have() is used to tell the other end what we have. If
* we do need to download a pack, ->download_pack() is called.
*
* When we're done, we call ->close() to close the
* connection. ->free() takes care of freeing all the resources.
*/
struct git_transport {
/**
* Where the repo lives
*/
char *url;
/**
* Whether we want to push or fetch
*/
int direction : 1, /* 0 fetch, 1 push */
connected : 1,
check_cert: 1,
use_ssl : 1,
own_logic: 1, /* transitional */
rpc: 1; /* git-speak for the HTTP transport */
#ifdef GIT_SSL
struct gitno_ssl ssl;
#endif
git_vector refs;
git_vector common;
gitno_buffer buffer;
GIT_SOCKET socket;
git_transport_caps caps;
void *cb_data;
git_atomic cancel;
/**
* Connect and store the remote heads
*/
int (*connect)(struct git_transport *transport, int dir);
/**
* Send our side of a negotiation
*/
int (*negotiation_step)(struct git_transport *transport, void *data, size_t len);
/**
* Push the changes over
*/
int (*push)(struct git_transport *transport);
/**
* Negotiate the minimal amount of objects that need to be
* retrieved
*/
int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants);
/**
* Download the packfile
*/
int (*download_pack)(struct git_transport *transport, git_repository *repo, git_transfer_progress *stats);
/**
* Close the connection
*/
int (*close)(struct git_transport *transport);
/**
* Free the associated resources
*/
void (*free)(struct git_transport *transport);
/**
* Callbacks for the progress and error output
*/
void (*progress_cb)(const char *str, int len, void *data);
void (*error_cb)(const char *str, int len, void *data);
};
int git_transport_new(struct git_transport **transport, const char *url);
int git_transport_local(struct git_transport **transport);
int git_transport_git(struct git_transport **transport);
int git_transport_http(struct git_transport **transport);
int git_transport_https(struct git_transport **transport);
int git_transport_dummy(struct git_transport **transport);
/**
Returns true if the passed URL is valid (a URL with a Git supported scheme,
or pointing to an existing path)
*/
int git_transport_valid_url(const char *url);
typedef int (*git_transport_cb)(git_transport **transport);
#endif
...@@ -5,40 +5,37 @@ ...@@ -5,40 +5,37 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include "git2/net.h" #include "git2.h"
#include "git2/common.h" #include "buffer.h"
#include "git2/types.h"
#include "git2/errors.h"
#include "git2/net.h"
#include "git2/revwalk.h"
#include "vector.h"
#include "transport.h"
#include "pkt.h"
#include "common.h"
#include "netops.h" #include "netops.h"
#include "filebuf.h"
#include "repository.h" #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
#include "fetch.h"
#include "protocol.h" static const char prefix_git[] = "git://";
static const char cmd_uploadpack[] = "git-upload-pack";
typedef struct { typedef struct {
git_transport parent; git_smart_subtransport_stream parent;
char buff[65536]; gitno_socket socket;
#ifdef GIT_WIN32 const char *cmd;
WSADATA wsd; char *url;
#endif unsigned sent_command : 1;
} transport_git; } git_stream;
typedef struct {
git_smart_subtransport parent;
git_transport *owner;
git_stream *current_stream;
} git_subtransport;
/* /*
* Create a git procol request. * Create a git protocol request.
* *
* For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
*/ */
static int gen_proto(git_buf *request, const char *cmd, const char *url) static int gen_proto(git_buf *request, const char *cmd, const char *url)
{ {
char *delim, *repo; char *delim, *repo;
char default_command[] = "git-upload-pack";
char host[] = "host="; char host[] = "host=";
size_t len; size_t len;
...@@ -54,9 +51,6 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) ...@@ -54,9 +51,6 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
if (delim == NULL) if (delim == NULL)
delim = strchr(url, '/'); delim = strchr(url, '/');
if (cmd == NULL)
cmd = default_command;
len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1; len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
git_buf_grow(request, len); git_buf_grow(request, len);
...@@ -71,175 +65,211 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) ...@@ -71,175 +65,211 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
return 0; return 0;
} }
static int send_request(git_transport *t, const char *cmd, const char *url) static int send_command(git_stream *s)
{ {
int error; int error;
git_buf request = GIT_BUF_INIT; git_buf request = GIT_BUF_INIT;
error = gen_proto(&request, cmd, url); error = gen_proto(&request, s->cmd, s->url);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
error = gitno_send(t, request.ptr, request.size, 0); /* It looks like negative values are errors here, and positive values
* are the number of bytes sent. */
error = gitno_send(&s->socket, request.ptr, request.size, 0);
if (error >= 0)
s->sent_command = 1;
cleanup: cleanup:
git_buf_free(&request); git_buf_free(&request);
return error; return error;
} }
/* static int git_stream_read(
* Parse the URL and connect to a server, storing the socket in git_smart_subtransport_stream *stream,
* out. For convenience this also takes care of asking for the remote char *buffer,
* refs size_t buf_size,
*/ size_t *bytes_read)
static int do_connect(transport_git *t, const char *url)
{ {
char *host, *port; git_stream *s = (git_stream *)stream;
const char prefix[] = "git://"; gitno_buffer buf;
if (!git__prefixcmp(url, prefix)) *bytes_read = 0;
url += strlen(prefix);
if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0) if (!s->sent_command && send_command(s) < 0)
return -1; return -1;
if (gitno_connect((git_transport *)t, host, port) < 0) gitno_buffer_setup(&s->socket, &buf, buffer, buf_size);
goto on_error;
if (send_request((git_transport *)t, NULL, url) < 0) if (gitno_recv(&buf) < 0)
goto on_error; return -1;
git__free(host); *bytes_read = buf.offset;
git__free(port);
return 0; return 0;
on_error:
git__free(host);
git__free(port);
gitno_close(t->parent.socket);
return -1;
} }
/* static int git_stream_write(
* Since this is a network connection, we need to parse and store the git_smart_subtransport_stream *stream,
* pkt-lines at this stage and keep them there. const char *buffer,
*/ size_t len)
static int git_connect(git_transport *transport, int direction)
{ {
transport_git *t = (transport_git *) transport; git_stream *s = (git_stream *)stream;
if (direction == GIT_DIR_PUSH) { if (!s->sent_command && send_command(s) < 0)
giterr_set(GITERR_NET, "Pushing over git:// is not supported");
return -1; return -1;
return gitno_send(&s->socket, buffer, len, 0);
}
static void git_stream_free(git_smart_subtransport_stream *stream)
{
git_stream *s = (git_stream *)stream;
git_subtransport *t = OWNING_SUBTRANSPORT(s);
int ret;
GIT_UNUSED(ret);
t->current_stream = NULL;
if (s->socket.socket) {
ret = gitno_close(&s->socket);
assert(!ret);
} }
t->parent.direction = direction; git__free(s->url);
git__free(s);
}
/* Connect and ask for the refs */ static int git_stream_alloc(
if (do_connect(t, transport->url) < 0) git_subtransport *t,
const char *url,
const char *cmd,
git_smart_subtransport_stream **stream)
{
git_stream *s;
if (!stream)
return -1; return -1;
gitno_buffer_setup(transport, &transport->buffer, t->buff, sizeof(t->buff)); s = (git_stream *)git__calloc(sizeof(git_stream), 1);
GITERR_CHECK_ALLOC(s);
t->parent.connected = 1; s->parent.subtransport = &t->parent;
if (git_protocol_store_refs(transport, 1) < 0) s->parent.read = git_stream_read;
return -1; s->parent.write = git_stream_write;
s->parent.free = git_stream_free;
if (git_protocol_detect_caps(git_vector_get(&transport->refs, 0), &transport->caps) < 0) s->cmd = cmd;
s->url = git__strdup(url);
if (!s->url) {
git__free(s);
return -1; return -1;
}
*stream = &s->parent;
return 0; return 0;
} }
static int git_negotiation_step(struct git_transport *transport, void *data, size_t len) static int git_git_uploadpack_ls(
git_subtransport *t,
const char *url,
git_smart_subtransport_stream **stream)
{ {
return gitno_send(transport, data, len, 0); char *host, *port;
} git_stream *s;
static int git_close(git_transport *t) *stream = NULL;
{
git_buf buf = GIT_BUF_INIT;
if (git_pkt_buffer_flush(&buf) < 0) if (!git__prefixcmp(url, prefix_git))
return -1; url += strlen(prefix_git);
/* Can't do anything if there's an error, so don't bother checking */
gitno_send(t, buf.ptr, buf.size, 0);
git_buf_free(&buf);
if (gitno_close(t->socket) < 0) { if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0)
giterr_set(GITERR_NET, "Failed to close socket");
return -1; return -1;
}
t->connected = 0; s = (git_stream *)*stream;
if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
goto on_error;
#ifdef GIT_WIN32 if (gitno_connect(&s->socket, host, port, 0) < 0)
WSACleanup(); goto on_error;
#endif
t->current_stream = s;
git__free(host);
git__free(port);
return 0; return 0;
on_error:
if (*stream)
git_stream_free(*stream);
git__free(host);
git__free(port);
return -1;
} }
static void git_free(git_transport *transport) static int git_git_uploadpack(
git_subtransport *t,
const char *url,
git_smart_subtransport_stream **stream)
{ {
transport_git *t = (transport_git *) transport; GIT_UNUSED(url);
git_vector *refs = &transport->refs;
unsigned int i;
for (i = 0; i < refs->length; ++i) { if (t->current_stream) {
git_pkt *p = git_vector_get(refs, i); *stream = &t->current_stream->parent;
git_pkt_free(p); return 0;
} }
git_vector_free(refs);
refs = &transport->common; giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
for (i = 0; i < refs->length; ++i) { return -1;
git_pkt *p = git_vector_get(refs, i); }
git_pkt_free(p);
static int _git_action(
git_smart_subtransport_stream **stream,
git_smart_subtransport *smart_transport,
const char *url,
git_smart_service_t action)
{
git_subtransport *t = (git_subtransport *) smart_transport;
switch (action) {
case GIT_SERVICE_UPLOADPACK_LS:
return git_git_uploadpack_ls(t, url, stream);
case GIT_SERVICE_UPLOADPACK:
return git_git_uploadpack(t, url, stream);
} }
git_vector_free(refs);
git__free(t->parent.url); *stream = NULL;
git__free(t); return -1;
} }
int git_transport_git(git_transport **out) static void _git_free(git_smart_subtransport *smart_transport)
{ {
transport_git *t; git_subtransport *t = (git_subtransport *) smart_transport;
#ifdef GIT_WIN32
int ret;
#endif
t = git__malloc(sizeof(transport_git)); assert(!t->current_stream);
GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_git)); git__free(t);
if (git_vector_init(&t->parent.common, 8, NULL)) }
goto on_error;
if (git_vector_init(&t->parent.refs, 16, NULL) < 0) int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner)
goto on_error; {
git_subtransport *t;
t->parent.connect = git_connect; if (!out)
t->parent.negotiation_step = git_negotiation_step; return -1;
t->parent.close = git_close;
t->parent.free = git_free;
*out = (git_transport *) t; t = (git_subtransport *)git__calloc(sizeof(git_subtransport), 1);
GITERR_CHECK_ALLOC(t);
#ifdef GIT_WIN32 t->owner = owner;
ret = WSAStartup(MAKEWORD(2,2), &t->wsd); t->parent.action = _git_action;
if (ret != 0) { t->parent.free = _git_free;
git_free(*out);
giterr_set(GITERR_NET, "Winsock init failed");
return -1;
}
#endif
*out = (git_smart_subtransport *) t;
return 0; return 0;
on_error:
git__free(t);
return -1;
} }
...@@ -11,15 +11,20 @@ ...@@ -11,15 +11,20 @@
#include "git2/object.h" #include "git2/object.h"
#include "git2/tag.h" #include "git2/tag.h"
#include "refs.h" #include "refs.h"
#include "transport.h" #include "git2/transport.h"
#include "posix.h" #include "posix.h"
#include "path.h" #include "path.h"
#include "buffer.h" #include "buffer.h"
#include "pkt.h"
typedef struct { typedef struct {
git_transport parent; git_transport parent;
char *url;
int direction;
int flags;
git_atomic cancelled;
git_repository *repo; git_repository *repo;
git_vector refs;
unsigned connected : 1;
} transport_local; } transport_local;
static int add_ref(transport_local *t, const char *name) static int add_ref(transport_local *t, const char *name)
...@@ -27,32 +32,24 @@ static int add_ref(transport_local *t, const char *name) ...@@ -27,32 +32,24 @@ static int add_ref(transport_local *t, const char *name)
const char peeled[] = "^{}"; const char peeled[] = "^{}";
git_remote_head *head; git_remote_head *head;
git_object *obj = NULL, *target = NULL; git_object *obj = NULL, *target = NULL;
git_transport *transport = (git_transport *) t;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
git_pkt_ref *pkt;
head = git__malloc(sizeof(git_remote_head)); head = (git_remote_head *)git__malloc(sizeof(git_remote_head));
GITERR_CHECK_ALLOC(head); GITERR_CHECK_ALLOC(head);
pkt = git__malloc(sizeof(git_pkt_ref));
GITERR_CHECK_ALLOC(pkt);
head->name = git__strdup(name); head->name = git__strdup(name);
GITERR_CHECK_ALLOC(head->name); GITERR_CHECK_ALLOC(head->name);
if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0) { if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0) {
git__free(head->name);
git__free(head); git__free(head);
git__free(pkt->head.name); return -1;
git__free(pkt);
} }
pkt->type = GIT_PKT_REF; if (git_vector_insert(&t->refs, head) < 0)
memcpy(&pkt->head, head, sizeof(git_remote_head));
git__free(head);
if (git_vector_insert(&transport->refs, pkt) < 0)
{ {
git__free(pkt->head.name); git__free(head->name);
git__free(pkt); git__free(head);
return -1; return -1;
} }
...@@ -60,7 +57,7 @@ static int add_ref(transport_local *t, const char *name) ...@@ -60,7 +57,7 @@ static int add_ref(transport_local *t, const char *name)
if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
return 0; return 0;
if (git_object_lookup(&obj, t->repo, &pkt->head.oid, GIT_OBJ_ANY) < 0) if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0)
return -1; return -1;
head = NULL; head = NULL;
...@@ -72,27 +69,21 @@ static int add_ref(transport_local *t, const char *name) ...@@ -72,27 +69,21 @@ static int add_ref(transport_local *t, const char *name)
} }
/* And if it's a tag, peel it, and add it to the list */ /* And if it's a tag, peel it, and add it to the list */
head = git__malloc(sizeof(git_remote_head)); head = (git_remote_head *)git__malloc(sizeof(git_remote_head));
GITERR_CHECK_ALLOC(head); GITERR_CHECK_ALLOC(head);
if (git_buf_join(&buf, 0, name, peeled) < 0) if (git_buf_join(&buf, 0, name, peeled) < 0)
return -1; return -1;
head->name = git_buf_detach(&buf); head->name = git_buf_detach(&buf);
pkt = git__malloc(sizeof(git_pkt_ref));
GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_REF;
if (git_tag_peel(&target, (git_tag *) obj) < 0) if (git_tag_peel(&target, (git_tag *) obj) < 0)
goto on_error; goto on_error;
git_oid_cpy(&head->oid, git_object_id(target)); git_oid_cpy(&head->oid, git_object_id(target));
git_object_free(obj); git_object_free(obj);
git_object_free(target); git_object_free(target);
memcpy(&pkt->head, head, sizeof(git_remote_head));
git__free(head);
if (git_vector_insert(&transport->refs, pkt) < 0) if (git_vector_insert(&t->refs, head) < 0)
return -1; return -1;
return 0; return 0;
...@@ -107,12 +98,11 @@ static int store_refs(transport_local *t) ...@@ -107,12 +98,11 @@ static int store_refs(transport_local *t)
{ {
unsigned int i; unsigned int i;
git_strarray ref_names = {0}; git_strarray ref_names = {0};
git_transport *transport = (git_transport *) t;
assert(t); assert(t);
if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 || if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 ||
git_vector_init(&transport->refs, (unsigned int)ref_names.count, NULL) < 0) git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0)
goto on_error; goto on_error;
/* Sort the references first */ /* Sort the references first */
...@@ -131,7 +121,7 @@ static int store_refs(transport_local *t) ...@@ -131,7 +121,7 @@ static int store_refs(transport_local *t)
return 0; return 0;
on_error: on_error:
git_vector_free(&transport->refs); git_vector_free(&t->refs);
git_strarray_free(&ref_names); git_strarray_free(&ref_names);
return -1; return -1;
} }
...@@ -140,7 +130,7 @@ on_error: ...@@ -140,7 +130,7 @@ on_error:
* Try to open the url as a git directory. The direction doesn't * Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves. * matter in this case because we're calulating the heads ourselves.
*/ */
static int local_connect(git_transport *transport, int direction) static int local_connect(git_transport *transport, const char *url, int direction, int flags)
{ {
git_repository *repo; git_repository *repo;
int error; int error;
...@@ -148,18 +138,21 @@ static int local_connect(git_transport *transport, int direction) ...@@ -148,18 +138,21 @@ static int local_connect(git_transport *transport, int direction)
const char *path; const char *path;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
GIT_UNUSED(direction); t->url = git__strdup(url);
GITERR_CHECK_ALLOC(t->url);
t->direction = direction;
t->flags = flags;
/* The repo layer doesn't want the prefix */ /* The repo layer doesn't want the prefix */
if (!git__prefixcmp(transport->url, "file://")) { if (!git__prefixcmp(t->url, "file://")) {
if (git_path_fromurl(&buf, transport->url) < 0) { if (git_path_fromurl(&buf, t->url) < 0) {
git_buf_free(&buf); git_buf_free(&buf);
return -1; return -1;
} }
path = git_buf_cstr(&buf); path = git_buf_cstr(&buf);
} else { /* We assume transport->url is already a path */ } else { /* We assume transport->url is already a path */
path = transport->url; path = t->url;
} }
error = git_repository_open(&repo, path); error = git_repository_open(&repo, path);
...@@ -174,26 +167,74 @@ static int local_connect(git_transport *transport, int direction) ...@@ -174,26 +167,74 @@ static int local_connect(git_transport *transport, int direction)
if (store_refs(t) < 0) if (store_refs(t) < 0)
return -1; return -1;
t->parent.connected = 1; t->connected = 1;
return 0;
}
static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
{
transport_local *t = (transport_local *)transport;
unsigned int i;
git_remote_head *head = NULL;
if (!t->connected) {
giterr_set(GITERR_NET, "The transport is not connected");
return -1;
}
git_vector_foreach(&t->refs, i, head) {
if (list_cb(head, payload))
return GIT_EUSER;
}
return 0; return 0;
} }
static int local_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) static int local_negotiate_fetch(
git_transport *transport,
git_repository *repo,
const git_remote_head * const *refs, size_t count)
{ {
GIT_UNUSED(transport); GIT_UNUSED(transport);
GIT_UNUSED(repo); GIT_UNUSED(repo);
GIT_UNUSED(wants); GIT_UNUSED(refs);
GIT_UNUSED(count);
giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry"); giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry");
return -1; return -1;
} }
static int local_is_connected(git_transport *transport, int *connected)
{
transport_local *t = (transport_local *)transport;
*connected = t->connected;
return 0;
}
static int local_read_flags(git_transport *transport, int *flags)
{
transport_local *t = (transport_local *)transport;
*flags = t->flags;
return 0;
}
static void local_cancel(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
git_atomic_set(&t->cancelled, 1);
}
static int local_close(git_transport *transport) static int local_close(git_transport *transport)
{ {
transport_local *t = (transport_local *)transport; transport_local *t = (transport_local *)transport;
t->parent.connected = 0; t->connected = 0;
git_repository_free(t->repo); git_repository_free(t->repo);
t->repo = NULL; t->repo = NULL;
...@@ -204,18 +245,18 @@ static void local_free(git_transport *transport) ...@@ -204,18 +245,18 @@ static void local_free(git_transport *transport)
{ {
unsigned int i; unsigned int i;
transport_local *t = (transport_local *) transport; transport_local *t = (transport_local *) transport;
git_vector *vec = &transport->refs; git_vector *vec = &t->refs;
git_pkt_ref *pkt; git_remote_head *head;
assert(transport); assert(transport);
git_vector_foreach (vec, i, pkt) { git_vector_foreach (vec, i, head) {
git__free(pkt->head.name); git__free(head->name);
git__free(pkt); git__free(head);
} }
git_vector_free(vec); git_vector_free(vec);
git__free(t->parent.url); git__free(t->url);
git__free(t); git__free(t);
} }
...@@ -223,20 +264,25 @@ static void local_free(git_transport *transport) ...@@ -223,20 +264,25 @@ static void local_free(git_transport *transport)
* Public API * * Public API *
**************/ **************/
int git_transport_local(git_transport **out) int git_transport_local(git_transport **out, void *param)
{ {
transport_local *t; transport_local *t;
GIT_UNUSED(param);
t = git__malloc(sizeof(transport_local)); t = git__malloc(sizeof(transport_local));
GITERR_CHECK_ALLOC(t); GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_local)); memset(t, 0x0, sizeof(transport_local));
t->parent.own_logic = 1;
t->parent.connect = local_connect; t->parent.connect = local_connect;
t->parent.negotiate_fetch = local_negotiate_fetch; t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.close = local_close; t->parent.close = local_close;
t->parent.free = local_free; t->parent.free = local_free;
t->parent.ls = local_ls;
t->parent.is_connected = local_is_connected;
t->parent.read_flags = local_read_flags;
t->parent.cancel = local_cancel;
*out = (git_transport *) t; *out = (git_transport *) t;
......
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "git2.h"
#include "smart.h"
#include "refs.h"
static int git_smart__recv_cb(gitno_buffer *buf)
{
transport_smart *t = (transport_smart *) buf->cb_data;
size_t old_len, bytes_read;
int error;
assert(t->current_stream);
old_len = buf->offset;
if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0)
return error;
buf->offset += bytes_read;
if (t->packetsize_cb)
t->packetsize_cb(bytes_read, t->packetsize_payload);
return (int)(buf->offset - old_len);
}
GIT_INLINE(void) git_smart__reset_stream(transport_smart *t)
{
if (t->current_stream) {
t->current_stream->free(t->current_stream);
t->current_stream = NULL;
}
}
static int git_smart__set_callbacks(
git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
void *message_cb_payload)
{
transport_smart *t = (transport_smart *)transport;
t->progress_cb = progress_cb;
t->error_cb = error_cb;
t->message_cb_payload = message_cb_payload;
return 0;
}
static int git_smart__connect(git_transport *transport, const char *url, int direction, int flags)
{
transport_smart *t = (transport_smart *)transport;
git_smart_subtransport_stream *stream;
int error;
git_pkt *pkt;
git_smart__reset_stream(t);
t->url = git__strdup(url);
GITERR_CHECK_ALLOC(t->url);
t->direction = direction;
t->flags = flags;
if (GIT_DIR_FETCH == direction)
{
if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK_LS)) < 0)
return error;
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = stream;
gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
/* 2 flushes for RPC; 1 for stateful */
if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
return error;
/* Strip the comment packet for RPC */
if (t->rpc) {
pkt = (git_pkt *)git_vector_get(&t->refs, 0);
if (!pkt || GIT_PKT_COMMENT != pkt->type) {
giterr_set(GITERR_NET, "Invalid response");
return -1;
} else {
/* Remove the comment pkt from the list */
git_vector_remove(&t->refs, 0);
git__free(pkt);
}
}
/* We now have loaded the refs. */
t->have_refs = 1;
if (git_smart__detect_caps((git_pkt_ref *)git_vector_get(&t->refs, 0), &t->caps) < 0)
return -1;
if (t->rpc)
git_smart__reset_stream(t);
/* We're now logically connected. */
t->connected = 1;
return 0;
}
else
{
giterr_set(GITERR_NET, "Push not implemented");
return -1;
}
return -1;
}
static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
{
transport_smart *t = (transport_smart *)transport;
unsigned int i;
git_pkt *p = NULL;
if (!t->have_refs) {
giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
return -1;
}
git_vector_foreach(&t->refs, i, p) {
git_pkt_ref *pkt = NULL;
if (p->type != GIT_PKT_REF)
continue;
pkt = (git_pkt_ref *)p;
if (list_cb(&pkt->head, payload))
return GIT_EUSER;
}
return 0;
}
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len)
{
transport_smart *t = (transport_smart *)transport;
git_smart_subtransport_stream *stream;
int error;
if (t->rpc)
git_smart__reset_stream(t);
if (GIT_DIR_FETCH == t->direction) {
if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0)
return error;
/* If this is a stateful implementation, the stream we get back should be the same */
assert(t->rpc || t->current_stream == stream);
/* Save off the current stream (i.e. socket) that we are working with */
t->current_stream = stream;
if ((error = stream->write(stream, (const char *)data, len)) < 0)
return error;
gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
return 0;
}
giterr_set(GITERR_NET, "Push not implemented");
return -1;
}
static void git_smart__cancel(git_transport *transport)
{
transport_smart *t = (transport_smart *)transport;
git_atomic_set(&t->cancelled, 1);
}
static int git_smart__is_connected(git_transport *transport, int *connected)
{
transport_smart *t = (transport_smart *)transport;
*connected = t->connected;
return 0;
}
static int git_smart__read_flags(git_transport *transport, int *flags)
{
transport_smart *t = (transport_smart *)transport;
*flags = t->flags;
return 0;
}
static int git_smart__close(git_transport *transport)
{
transport_smart *t = (transport_smart *)transport;
git_smart__reset_stream(t);
t->connected = 0;
return 0;
}
static void git_smart__free(git_transport *transport)
{
transport_smart *t = (transport_smart *)transport;
git_vector *refs = &t->refs;
git_vector *common = &t->common;
unsigned int i;
git_pkt *p;
/* Make sure that the current stream is closed, if we have one. */
git_smart__close(transport);
/* Free the subtransport */
t->wrapped->free(t->wrapped);
git_vector_foreach(refs, i, p) {
git_pkt_free(p);
}
git_vector_free(refs);
git_vector_foreach(common, i, p) {
git_pkt_free(p);
}
git_vector_free(common);
git__free(t->url);
git__free(t);
}
int git_transport_smart(git_transport **out, void *param)
{
transport_smart *t;
git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param;
if (!param)
return -1;
t = (transport_smart *)git__calloc(sizeof(transport_smart), 1);
GITERR_CHECK_ALLOC(t);
t->parent.set_callbacks = git_smart__set_callbacks;
t->parent.connect = git_smart__connect;
t->parent.close = git_smart__close;
t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch;
t->parent.download_pack = git_smart__download_pack;
t->parent.ls = git_smart__ls;
t->parent.is_connected = git_smart__is_connected;
t->parent.read_flags = git_smart__read_flags;
t->parent.cancel = git_smart__cancel;
t->rpc = definition->rpc;
if (git_vector_init(&t->refs, 16, NULL) < 0) {
git__free(t);
return -1;
}
if (definition->callback(&t->wrapped, &t->parent) < 0) {
git__free(t);
return -1;
}
*out = (git_transport *) t;
return 0;
}
...@@ -4,15 +4,20 @@ ...@@ -4,15 +4,20 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with * This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include "git2.h"
#include "vector.h"
#include "netops.h"
#include "buffer.h"
#ifndef INCLUDE_pkt_h__ #define GIT_SIDE_BAND_DATA 1
#define INCLUDE_pkt_h__ #define GIT_SIDE_BAND_PROGRESS 2
#define GIT_SIDE_BAND_ERROR 3
#include "common.h" #define GIT_CAP_OFS_DELTA "ofs-delta"
#include "transport.h" #define GIT_CAP_MULTI_ACK "multi_ack"
#include "buffer.h" #define GIT_CAP_SIDE_BAND "side-band"
#include "posix.h" #define GIT_CAP_SIDE_BAND_64K "side-band-64k"
#include "git2/net.h" #define GIT_CAP_INCLUDE_TAG "include-tag"
enum git_pkt_type { enum git_pkt_type {
GIT_PKT_CMD, GIT_PKT_CMD,
...@@ -80,12 +85,65 @@ typedef struct { ...@@ -80,12 +85,65 @@ typedef struct {
char error[GIT_FLEX_ARRAY]; char error[GIT_FLEX_ARRAY];
} git_pkt_err; } git_pkt_err;
typedef struct transport_smart_caps {
int common:1,
ofs_delta:1,
multi_ack: 1,
side_band:1,
side_band_64k:1,
include_tag:1;
} transport_smart_caps;
typedef void (*packetsize_cb)(int received, void *payload);
typedef struct {
git_transport parent;
char *url;
int direction;
int flags;
git_transport_message_cb progress_cb;
git_transport_message_cb error_cb;
void *message_cb_payload;
git_smart_subtransport *wrapped;
git_smart_subtransport_stream *current_stream;
transport_smart_caps caps;
git_vector refs;
git_vector common;
git_atomic cancelled;
packetsize_cb packetsize_cb;
void *packetsize_payload;
unsigned rpc : 1,
have_refs : 1,
connected : 1;
gitno_buffer buffer;
char buffer_data[65536];
} transport_smart;
/* smart_protocol.c */
int git_smart__store_refs(transport_smart *t, int flushes);
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps);
int git_smart__negotiate_fetch(
git_transport *transport,
git_repository *repo,
const git_remote_head * const *refs,
size_t count);
int git_smart__download_pack(
git_transport *transport,
git_repository *repo,
git_transfer_progress *stats,
git_transfer_progress_callback progress_cb,
void *progress_payload);
/* smart.c */
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
/* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_buffer_flush(git_buf *buf); int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf); int git_pkt_buffer_done(git_buf *buf);
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf); int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf);
int git_pkt_buffer_have(git_oid *oid, git_buf *buf); int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
void git_pkt_free(git_pkt *pkt); void git_pkt_free(git_pkt *pkt);
#endif
...@@ -12,12 +12,11 @@ ...@@ -12,12 +12,11 @@
#include "git2/refs.h" #include "git2/refs.h"
#include "git2/revwalk.h" #include "git2/revwalk.h"
#include "pkt.h" #include "smart.h"
#include "util.h" #include "util.h"
#include "netops.h" #include "netops.h"
#include "posix.h" #include "posix.h"
#include "buffer.h" #include "buffer.h"
#include "protocol.h"
#include <ctype.h> #include <ctype.h>
...@@ -335,7 +334,7 @@ int git_pkt_buffer_flush(git_buf *buf) ...@@ -335,7 +334,7 @@ int git_pkt_buffer_flush(git_buf *buf)
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str)); return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
} }
static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf) static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_buf *buf)
{ {
git_buf str = GIT_BUF_INIT; git_buf str = GIT_BUF_INIT;
char oid[GIT_OID_HEXSZ +1] = {0}; char oid[GIT_OID_HEXSZ +1] = {0};
...@@ -376,28 +375,32 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps ...@@ -376,28 +375,32 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
* is overwrite the OID each time. * is overwrite the OID each time.
*/ */
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf) int git_pkt_buffer_wants(
const git_remote_head * const *refs,
size_t count,
transport_smart_caps *caps,
git_buf *buf)
{ {
unsigned int i = 0; size_t i = 0;
git_remote_head *head; const git_remote_head *head;
if (caps->common) { if (caps->common) {
for (; i < refs->length; ++i) { for (; i < count; ++i) {
head = refs->contents[i]; head = refs[i];
if (!head->local) if (!head->local)
break; break;
} }
if (buffer_want_with_caps(refs->contents[i], caps, buf) < 0) if (buffer_want_with_caps(refs[i], caps, buf) < 0)
return -1; return -1;
i++; i++;
} }
for (; i < refs->length; ++i) { for (; i < count; ++i) {
char oid[GIT_OID_HEXSZ]; char oid[GIT_OID_HEXSZ];
head = refs->contents[i]; head = refs[i];
if (head->local) if (head->local)
continue; continue;
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "transport.h"
#include "buffer.h" #include "buffer.h"
#include "path.h" #include "path.h"
#include "posix.h" #include "posix.h"
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "buffer.h" #include "buffer.h"
#include "refspec.h" #include "refspec.h"
#include "transport.h"
#include "remote.h" #include "remote.h"
static git_remote *_remote; static git_remote *_remote;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment