Commit f12aa9dc by Vicent Martí

Merge pull request #300 from carlosmn/gsoc2011/master

A bit of networking
parents 7d69f788 0ac2726f
......@@ -53,5 +53,13 @@
#include "git2/index.h"
#include "git2/config.h"
#include "git2/remote.h"
#include "git2/remote.h"
#include "git2/refspec.h"
#include "git2/net.h"
#include "git2/transport.h"
#include "git2/pkt.h"
#endif
#ifndef INCLUDE_branch_h__
#define INCLUDE_branch_h__
struct git_branch {
char *remote; /* TODO: Make this a git_remote */
char *merge;
};
#endif
......@@ -125,6 +125,12 @@ typedef enum {
/** Skip and passthrough the given ODB backend */
GIT_EPASSTHROUGH = -30,
/** The path pattern and string did not match */
GIT_ENOMATCH = -31,
/** The buffer is too short to satisfy the request */
GIT_ESHORTBUFFER = -32,
} git_error;
/**
......
#ifndef INCLUDE_net_h__
#define INCLUDE_net_h__
#include "common.h"
#include "oid.h"
#include "types.h"
#define GIT_DEFAULT_PORT "9418"
/*
* We need this because we need to know whether we should call
* git-upload-pack or git-receive-pack on the remote end when get_refs
* gets called.
*/
#define GIT_DIR_FETCH 0
#define GIT_DIR_PUSH 1
/*
* This is what we give out on ->ls()
*/
struct git_remote_head {
git_oid oid;
char *name;
};
struct git_headarray {
unsigned int len;
struct git_remote_head **heads;
};
#endif
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "git2/net.h"
enum git_pkt_type {
GIT_PKT_CMD,
GIT_PKT_FLUSH,
GIT_PKT_REF,
GIT_PKT_HAVE,
};
/* This would be a flush pkt */
struct git_pkt {
enum git_pkt_type type;
};
struct git_pkt_cmd {
enum git_pkt_type type;
char *cmd;
char *path;
char *host;
};
/* This is a pkt-line with some info in it */
struct git_pkt_ref {
enum git_pkt_type type;
git_remote_head head;
char *capabilities;
};
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_send_flush(int s);
void git_pkt_free(git_pkt *pkt);
#ifndef INCLUDE_git_refspec_h__
#define INCLUDE_git_refspec_h__
#include "git2/types.h"
/**
* Get the source specifier
*
* @param refspec the refspec
* @return the refspec's source specifier
*/
const char *git_refspec_src(const git_refspec *refspec);
/**
* Get the destination specifier
*
* @param refspec the refspec
* @return the refspec's destination specifier
*/
const char *git_refspec_dst(const git_refspec *refspec);
/**
* Match a refspec's source descriptor with a reference name
*
* @param refspec the refspec
* @param refname the name of the reference to check
* @return GIT_SUCCESS on successful match; GIT_ENOMACH on match
* failure or an error code on other failure
*/
int git_refspec_src_match(const git_refspec *refspec, const char *refname);
/**
* Transform a reference to its target following the refspec's rules
*
* @param out where to store the target name
* @param in the source reference
* @param spec the refspec
* @param len the length of the out buffer
* @preturn GIT_SUCCESS, GIT_ESHORTBUFFER or another error
*/
int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
#endif
#ifndef INCLUDE_git_remote_h__
#define INCLUDE_git_remote_h__
#include "git2/common.h"
#include "git2/repository.h"
#include "git2/refspec.h"
/*
* TODO: This functions still need to be implemented:
* - _listcb/_foreach
* - _add
* - _rename
* - _del (needs support from config)
*/
/**
* Get the information for a particular remote
*
* @param out pointer to the new remote object
* @param cfg the repository's configuration
* @param name the remote's name
* @return 0 on success; error value otherwise
*/
GIT_EXTERN(int) git_remote_get(struct git_remote **out, struct git_config *cfg, const char *name);
/**
* Get the remote's name
*
* @param remote the remote
* @return a pointer to the name
*/
GIT_EXTERN(const char *) git_remote_name(struct git_remote *remote);
/**
* Get the remote's url
*
* @param remote the remote
* @return a pointer to the url
*/
GIT_EXTERN(const char *) git_remote_url(struct git_remote *remote);
/**
* Get the fetch refspec
*
* @param remote the remote
* @return a pointer to the fetch refspec or NULL if it doesn't exist
*/
GIT_EXTERN(const git_refspec *) git_remote_fetchspec(struct git_remote *remote);
/**
* Get the push refspec
*
* @param remote the remote
* @return a pointer to the push refspec or NULL if it doesn't exist
*/
GIT_EXTERN(const git_refspec *) git_remote_fetchspec(struct git_remote *remote);
/**
* Open a connection to a remote
*
* The transport is selected based on the URL
*
* @param remote the remote to connect to
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_remote_connect(struct git_remote *remote, int direction);
/**
* Get a list of refs at the remote
*
* The remote (or more exactly its transport) must be connected.
*
* @param refs where to store the refs
* @param remote the remote
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headarray *refs);
/**
* Free the memory associated with a remote
*
* @param remote the remote to free
*/
GIT_EXTERN(void) git_remote_free(struct git_remote *remote);
#endif
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_git_transport_h__
#define INCLUDE_git_transport_h__
#include "common.h"
#include "types.h"
#include "net.h"
/**
* @file git2/transport.h
* @brief Git protocol transport abstraction
* @defgroup git_transport Git protocol transport abstraction
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Get the appropriate transport for an URL.
* @param tranport the transport for the url
* @param url the url of the repo
*/
GIT_EXTERN(int) git_transport_new(git_transport **transport, const char *url);
GIT_EXTERN(int) git_transport_connect(git_transport *transport, int direction);
GIT_EXTERN(int) git_transport_ls(git_transport *transport, git_headarray *array);
GIT_EXTERN(int) git_transport_close(git_transport *transport);
GIT_EXTERN(void) git_transport_free(git_transport *transport);
GIT_EXTERN(int) git_transport_add(git_transport *transport, const char *prefix);
/** @} */
GIT_END_DECL
#endif
......@@ -167,6 +167,27 @@ typedef enum {
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
} git_rtype;
typedef struct git_refspec git_refspec;
typedef struct git_remote git_remote;
/** A transport to use */
typedef struct git_transport git_transport;
/** Whether to push or pull */
typedef enum git_net_direction git_net_direction;
typedef int (*git_transport_cb)(git_transport **transport);
typedef struct git_remote_head git_remote_head;
typedef struct git_headarray git_headarray;
/* Several types of packets */
typedef enum git_pkt_type git_pkt_type;
typedef struct git_pkt git_pkt;
typedef struct git_pkt_cmd git_pkt_cmd;
typedef struct git_pkt_ref git_pkt_ref;
/** @} */
GIT_END_DECL
......
This diff is collapsed. Click to expand it.
/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef _FNMATCH_H
#define _FNMATCH_H 1
#ifdef __cplusplus
extern "C" {
#endif
#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
# if !defined __GLIBC__ || !defined __P
# undef __P
# define __P(protos) protos
# endif
#else /* Not C++ or ANSI C. */
# undef __P
# define __P(protos) ()
/* We can get away without defining `const' here only because in this file
it is used only inside the prototype for `fnmatch', which is elided in
non-ANSI C where `const' is problematical. */
#endif /* C++ or ANSI C. */
#ifndef const
# if (defined __STDC__ && __STDC__) || defined __cplusplus
# define __const const
# else
# define __const
# endif
#endif
/* We #undef these before defining them because some losing systems
(HP-UX A.08.07 for example) define these in <unistd.h>. */
#undef FNM_PATHNAME
#undef FNM_NOESCAPE
#undef FNM_PERIOD
/* Bits set in the FLAGS argument to `fnmatch'. */
#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
#endif
/* Value returned by `fnmatch' if STRING does not match PATTERN. */
#define FNM_NOMATCH 1
/* This value is returned if the implementation does not support
`fnmatch'. Since this is not the case here it will never be
returned but the conformance test suites still require the symbol
to be defined. */
#ifdef _XOPEN_SOURCE
# define FNM_NOSYS (-1)
#endif
/* Match NAME against the filename pattern PATTERN,
returning zero if it matches, FNM_NOMATCH if not. */
extern int fnmatch __P ((__const char *__pattern, __const char *__name,
int __flags));
#ifdef __cplusplus
}
#endif
#endif /* fnmatch.h */
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _MSC_VER
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
#else
# include <winsock2.h>
# include <Ws2tcpip.h>
# pragma comment(lib, "Ws2_32.lib")
#endif
#include "git2/errors.h"
#include "common.h"
#include "netops.h"
void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd)
{
memset(buf, 0x0, sizeof(gitno_buffer));
memset(data, 0x0, len);
buf->data = data;
buf->len = len - 1;
buf->offset = 0;
buf->fd = fd;
}
int gitno_recv(gitno_buffer *buf)
{
int ret;
ret = recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
if (ret < 0)
return git__throw(GIT_EOSERR, "Failed to receive data");
if (ret == 0) /* Orderly shutdown, so exit */
return GIT_SUCCESS;
buf->offset += ret;
return ret;
}
/* Consume up to ptr and move the rest of the buffer to the beginning */
void gitno_consume(gitno_buffer *buf, const char *ptr)
{
int consumed;
assert(ptr - buf->data <= (int) buf->len);
consumed = ptr - buf->data;
memmove(buf->data, ptr, buf->offset - consumed);
memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
buf->offset -= consumed;
}
/* Consume const bytes and move the rest of the buffer to the beginning */
void gitno_consume_n(gitno_buffer *buf, unsigned int cons)
{
memmove(buf->data, buf->data + cons, buf->len - buf->offset);
memset(buf->data + cons, 0x0, buf->len - buf->offset);
buf->offset -= cons;
}
int gitno_connect(const char *host, const char *port)
{
struct addrinfo *info, *p;
struct addrinfo hints;
int ret, error = GIT_SUCCESS;
int s;
memset(&hints, 0x0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
ret = getaddrinfo(host, port, &hints, &info);
if (ret != 0) {
error = GIT_EOSERR;
goto cleanup;
}
for (p = info; p != NULL; p = p->ai_next) {
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (s < 0) {
error = GIT_EOSERR;
goto cleanup;
}
ret = connect(s, p->ai_addr, p->ai_addrlen);
/* If we can't connect, try the next one */
if (ret < 0) {
continue;
}
/* Return the socket */
error = s;
goto cleanup;
}
/* Oops, we couldn't connect to any address */
error = GIT_EOSERR;
cleanup:
freeaddrinfo(info);
return error;
}
int gitno_send(int s, const char *msg, int len, int flags)
{
int ret, off = 0;
while (off < len) {
ret = send(s, msg + off, len - off, flags);
if (ret < 0)
return GIT_EOSERR;
off += ret;
}
return off;
}
/*
* netops.h - convencience functions for networking
*/
#ifndef INCLUDE_netops_h__
#define INCLUDE_netops_h__
typedef struct gitno_buffer {
char *data;
unsigned int len;
unsigned int offset;
int fd;
} gitno_buffer;
void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd);
int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, unsigned int cons);
int gitno_connect(const char *host, const char *port);
int gitno_send(int s, const char *msg, int len, int flags);
#endif
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "git2/pkt.h"
#include "git2/types.h"
#include "git2/errors.h"
#include "common.h"
#include "util.h"
#include "netops.h"
#include <ctype.h>
#define PKT_LEN_SIZE 4
static int flush_pkt(git_pkt **out)
{
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
if (pkt == NULL)
return GIT_ENOMEM;
pkt->type = GIT_PKT_FLUSH;
*out = pkt;
return GIT_SUCCESS;
}
/*
* Parse an other-ref line.
*/
int ref_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_ref *pkt;
int error, has_caps = 0;
pkt = git__malloc(sizeof(git_pkt_ref));
if (pkt == NULL)
return GIT_ENOMEM;
memset(pkt, 0x0, sizeof(git_pkt_ref));
pkt->type = GIT_PKT_REF;
error = git_oid_fromstr(&pkt->head.oid, line);
if (error < GIT_SUCCESS) {
error = git__throw(error, "Failed to parse reference ID");
goto out;
}
/* Check for a bit of consistency */
if (line[GIT_OID_HEXSZ] != ' ') {
error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ref. No SP");
goto out;
}
/* Jump from the name */
line += GIT_OID_HEXSZ + 1;
len -= (GIT_OID_HEXSZ + 1);
if (strlen(line) < len)
has_caps = 1;
if (line[len - 1] == '\n')
--len;
pkt->head.name = git__malloc(len + 1);
if (pkt->head.name == NULL) {
error = GIT_ENOMEM;
goto out;
}
memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0';
if (has_caps) {
pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
}
out:
if (error < GIT_SUCCESS)
free(pkt);
else
*out = (git_pkt *)pkt;
return error;
}
static ssize_t parse_len(const char *line)
{
char num[PKT_LEN_SIZE + 1];
int i, error;
long len;
const char *num_end;
memcpy(num, line, PKT_LEN_SIZE);
num[PKT_LEN_SIZE] = '\0';
for (i = 0; i < PKT_LEN_SIZE; ++i) {
if (!isxdigit(num[i]))
return GIT_ENOTNUM;
}
error = git__strtol32(&len, num, &num_end, 16);
if (error < GIT_SUCCESS) {
return error;
}
return (unsigned int) len;
}
/*
* As per the documentation, the syntax is:
*
* pkt-line = data-pkt / flush-pkt
* data-pkt = pkt-len pkt-payload
* pkt-len = 4*(HEXDIG)
* pkt-payload = (pkt-len -4)*(OCTET)
* flush-pkt = "0000"
*
* Which means that the first four bytes are the length of the line,
* in ASCII hexadecimal (including itself)
*/
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t bufflen)
{
int error = GIT_SUCCESS;
size_t len;
/* Not even enough for the length */
if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
return GIT_ESHORTBUFFER;
error = parse_len(line);
if (error < GIT_SUCCESS) {
return git__throw(error, "Failed to parse pkt length");
}
len = error;
/*
* If we were given a buffer length, then make sure there is
* enough in the buffer to satisfy this line
*/
if (bufflen > 0 && bufflen < len)
return GIT_ESHORTBUFFER;
line += PKT_LEN_SIZE;
/*
* TODO: How do we deal with empty lines? Try again? with the next
* line?
*/
if (len == PKT_LEN_SIZE) {
*out = line;
return GIT_SUCCESS;
}
if (len == 0) { /* Flush pkt */
*out = line;
return flush_pkt(head);
}
len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
/*
* For now, we're just going to assume we're parsing references
*/
error = ref_pkt(head, line, len);
*out = line + len;
return error;
}
void git_pkt_free(git_pkt *pkt)
{
if(pkt->type == GIT_PKT_REF) {
git_pkt_ref *p = (git_pkt_ref *) pkt;
free(p->head.name);
}
free(pkt);
}
int git_pkt_send_flush(int s)
{
char flush[] = "0000";
return gitno_send(s, flush, STRLEN(flush), 0);
}
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "git2/errors.h"
#include "common.h"
#include "refspec.h"
#include "util.h"
int git_refspec_parse(git_refspec *refspec, const char *str)
{
char *delim;
memset(refspec, 0x0, sizeof(git_refspec));
if (*str == '+') {
refspec->force = 1;
str++;
}
delim = strchr(str, ':');
if (delim == NULL)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'");
refspec->src = git__strndup(str, delim - str);
if (refspec->src == NULL)
return GIT_ENOMEM;
refspec->dst = git__strdup(delim + 1);
if (refspec->dst == NULL) {
free(refspec->src);
refspec->src = NULL;
return GIT_ENOMEM;
}
return GIT_SUCCESS;
}
const char *git_refspec_src(const git_refspec *refspec)
{
return refspec->src;
}
const char *git_refspec_dst(const git_refspec *refspec)
{
return refspec->dst;
}
int git_refspec_src_match(const git_refspec *refspec, const char *refname)
{
return git__fnmatch(refspec->src, refname, 0);
}
int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
{
size_t baselen, namelen;
baselen = strlen(spec->dst);
if (outlen <= baselen)
return git__throw(GIT_EINVALIDREFNAME, "Reference name too long");
/*
* No '*' at the end means that it's mapped to one specific local
* branch, so no actual transformation is needed.
*/
if (spec->dst[baselen - 1] != '*') {
memcpy(out, spec->dst, baselen + 1); /* include '\0' */
return GIT_SUCCESS;
}
/* There's a '*' at the end, so remove its length */
baselen--;
/* skip the prefix, -1 is for the '*' */
name += strlen(spec->src) - 1;
namelen = strlen(name);
if (outlen <= baselen + namelen)
return git__throw(GIT_EINVALIDREFNAME, "Reference name too long");
memcpy(out, spec->dst, baselen);
memcpy(out + baselen, name, namelen + 1);
return GIT_SUCCESS;
}
#ifndef INCLUDE_refspec_h__
#define INCLUDE_refspec_h__
#include "git2/refspec.h"
struct git_refspec {
int force;
char *src;
char *dst;
};
int git_refspec_parse(struct git_refspec *refspec, const char *str);
#endif
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "git2/remote.h"
#include "git2/config.h"
#include "git2/types.h"
#include "config.h"
#include "repository.h"
#include "remote.h"
static int refspec_parse(git_refspec *refspec, const char *str)
{
char *delim;
memset(refspec, 0x0, sizeof(git_refspec));
if (*str == '+') {
refspec->force = 1;
str++;
}
delim = strchr(str, ':');
if (delim == NULL)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'");
refspec->src = git__strndup(str, delim - str);
if (refspec->src == NULL)
return GIT_ENOMEM;
refspec->dst = git__strdup(delim + 1);
if (refspec->dst == NULL) {
free(refspec->src);
refspec->src = NULL;
return GIT_ENOMEM;
}
return GIT_SUCCESS;
}
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)
{
const char *val;
int error;
error = git_config_get_string(cfg, var, &val);
if (error < GIT_SUCCESS)
return error;
return refspec_parse(refspec, val);
}
int git_remote_get(git_remote **out, git_config *cfg, const char *name)
{
git_remote *remote;
char *buf = NULL;
const char *val;
int ret, error, buf_len;
remote = git__malloc(sizeof(git_remote));
if (remote == NULL)
return GIT_ENOMEM;
memset(remote, 0x0, sizeof(git_remote));
remote->name = git__strdup(name);
if (remote->name == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
/* "fetch" is the longest var name we're interested in */
buf_len = STRLEN("remote.") + STRLEN(".fetch") + strlen(name) + 1;
buf = git__malloc(buf_len);
if (buf == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
ret = snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "url");
if (ret < 0) {
error = git__throw(GIT_EOSERR, "Failed to build config var name");
goto cleanup;
}
error = git_config_get_string(cfg, buf, &val);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Remote's url doesn't exist");
goto cleanup;
}
remote->url = git__strdup(val);
if (remote->url == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
ret = snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "fetch");
if (ret < 0) {
error = git__throw(GIT_EOSERR, "Failed to build config var name");
goto cleanup;
}
error = parse_remote_refspec(cfg, &remote->fetch, buf);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Failed to get fetch refspec");
goto cleanup;
}
ret = snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "push");
if (ret < 0) {
error = git__throw(GIT_EOSERR, "Failed to build config var name");
goto cleanup;
}
error = parse_remote_refspec(cfg, &remote->push, buf);
/* Not finding push is fine */
if (error == GIT_ENOTFOUND)
error = GIT_SUCCESS;
if (error < GIT_SUCCESS)
goto cleanup;
*out = remote;
cleanup:
free(buf);
if (error < GIT_SUCCESS)
git_remote_free(remote);
return error;
}
const char *git_remote_name(struct git_remote *remote)
{
return remote->name;
}
const char *git_remote_url(struct git_remote *remote)
{
return remote->url;
}
const git_refspec *git_remote_fetchspec(struct git_remote *remote)
{
return &remote->fetch;
}
const git_refspec *git_remote_pushspec(struct git_remote *remote)
{
return &remote->push;
}
int git_remote_connect(git_remote *remote, int direction)
{
int error;
git_transport *t;
error = git_transport_new(&t, remote->url);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create transport");
error = git_transport_connect(t, direction);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Failed to connect the transport");
goto cleanup;
}
remote->transport = t;
cleanup:
if (error < GIT_SUCCESS)
git_transport_free(t);
return error;
}
int git_remote_ls(git_remote *remote, git_headarray *refs)
{
return git_transport_ls(remote->transport, refs);
}
void git_remote_free(git_remote *remote)
{
free(remote->fetch.src);
free(remote->fetch.dst);
free(remote->push.src);
free(remote->push.dst);
free(remote->url);
free(remote->name);
if (remote->transport != NULL) {
if (remote->transport->connected)
git_transport_close(remote->transport);
git_transport_free(remote->transport);
}
free(remote);
}
#ifndef INCLUDE_remote_h__
#define INCLUDE_remote_h__
#include "remote.h"
#include "refspec.h"
#include "transport.h"
struct git_remote {
char *name;
char *url;
struct git_refspec fetch;
struct git_refspec push;
git_transport *transport;
};
#endif
#include "common.h"
#include "git2/types.h"
#include "git2/transport.h"
#include "git2/net.h"
#include "transport.h"
struct {
char *prefix;
git_transport_cb fn;
} transports[] = {
{"git://", git_transport_git},
{"http://", git_transport_dummy},
{"https://", git_transport_dummy},
{"file://", git_transport_local},
{"git+ssh://", git_transport_dummy},
{"ssh+git://", git_transport_dummy},
{NULL, 0}
};
static git_transport_cb transport_new_fn(const char *url)
{
int i = 0;
while (1) {
if (transports[i].prefix == NULL)
break;
if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix)))
return transports[i].fn;
++i;
}
/*
* If we still haven't found the transport, we assume we mean a
* local file.
* TODO: Parse "example.com:project.git" as an SSH URL
*/
return git_transport_local;
}
/**************
* Public API *
**************/
int git_transport_dummy(git_transport **GIT_UNUSED(transport))
{
GIT_UNUSED_ARG(transport);
return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry");
}
int git_transport_new(git_transport **out, const char *url)
{
git_transport_cb fn;
git_transport *transport;
int error;
fn = transport_new_fn(url);
error = fn(&transport);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create new transport");
transport->url = git__strdup(url);
if (transport->url == NULL)
return GIT_ENOMEM;
*out = transport;
return GIT_SUCCESS;
}
int git_transport_connect(git_transport *transport, int direction)
{
return transport->connect(transport, direction);
}
int git_transport_ls(git_transport *transport, git_headarray *array)
{
return transport->ls(transport, array);
}
int git_transport_close(git_transport *transport)
{
return transport->close(transport);
}
void git_transport_free(git_transport *transport)
{
transport->free(transport);
}
#ifndef INCLUDE_transport_h__
#define INCLUDE_transport_h__
#include "git2/transport.h"
#include "git2/net.h"
#include "vector.h"
/*
* 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.
*
* If we just want to execute ls-remote, ->ls() gets
* called. Otherwise, the have/want/need list needs to be built via
* ->wanthaveneed(). We can then ->push() or ->pull(). 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 */
int connected : 1;
/**
* Connect and store the remote heads
*/
int (*connect)(struct git_transport *transport, int dir);
/**
* Give a list of references, useful for ls-remote
*/
int (*ls)(struct git_transport *transport, git_headarray *headarray);
/**
* Calculate want/have/need. May not even be needed.
*/
int (*wanthaveneed)(struct git_transport *transport, void *something);
/**
* Build the pack
*/
int (*build_pack)(struct git_transport *transport);
/**
* Push the changes over
*/
int (*push)(struct git_transport *transport);
/**
* Fetch the changes
*/
int (*fetch)(struct git_transport *transport);
/**
* Close the connection
*/
int (*close)(struct git_transport *transport);
/**
* Free the associated resources
*/
void (*free)(struct git_transport *transport);
};
int git_transport_local(struct git_transport **transport);
int git_transport_git(struct git_transport **transport);
int git_transport_dummy(struct git_transport **transport);
#endif
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _MSC_VER
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
#else
# include <winsock2.h>
# include <Ws2tcpip.h>
# pragma comment(lib, "Ws2_32.lib")
#endif
#include "git2/net.h"
#include "git2/pkt.h"
#include "git2/common.h"
#include "git2/types.h"
#include "git2/errors.h"
#include "vector.h"
#include "transport.h"
#include "common.h"
#include "netops.h"
typedef struct {
git_transport parent;
int socket;
git_vector refs;
git_remote_head **heads;
} transport_git;
/*
* Create a git procol request.
*
* For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
*/
static int gen_proto(char **out, int *outlen, const char *cmd, const char *url)
{
char *delim, *repo, *ptr;
char default_command[] = "git-upload-pack";
char host[] = "host=";
int len;
delim = strchr(url, '/');
if (delim == NULL)
return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL");
repo = delim;
delim = strchr(url, ':');
if (delim == NULL)
delim = strchr(url, '/');
if (cmd == NULL)
cmd = default_command;
len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + STRLEN(host) + (delim - url) + 2;
*out = git__malloc(len);
if (*out == NULL)
return GIT_ENOMEM;
*outlen = len - 1;
ptr = *out;
memset(ptr, 0x0, len);
/* We expect the return value to be > len - 1 so don't bother checking it */
snprintf(ptr, len -1, "%04x%s %s%c%s%s", len - 1, cmd, repo, 0, host, url);
return GIT_SUCCESS;
}
static int send_request(int s, const char *cmd, const char *url)
{
int error, len;
char *msg = NULL;
error = gen_proto(&msg, &len, cmd, url);
if (error < GIT_SUCCESS)
goto cleanup;
error = gitno_send(s, msg, len, 0);
cleanup:
free(msg);
return error;
}
/* The URL should already have been stripped of the protocol */
static int extract_host_and_port(char **host, char **port, const char *url)
{
char *colon, *slash, *delim;
int error = GIT_SUCCESS;
colon = strchr(url, ':');
slash = strchr(url, '/');
if (slash == NULL)
return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /");
if (colon == NULL) {
*port = git__strdup(GIT_DEFAULT_PORT);
} else {
*port = git__strndup(colon + 1, slash - colon - 1);
}
if (*port == NULL)
return GIT_ENOMEM;;
delim = colon == NULL ? slash : colon;
*host = git__strndup(url, delim - url);
if (*host == NULL) {
free(*port);
error = GIT_ENOMEM;
}
return error;
}
/*
* Parse the URL and connect to a server, storing the socket in
* out. For convenience this also takes care of asking for the remote
* refs
*/
static int do_connect(transport_git *t, const char *url)
{
int s = -1;
char *host, *port;
const char prefix[] = "git://";
int error, connected = 0;
if (!git__prefixcmp(url, prefix))
url += STRLEN(prefix);
error = extract_host_and_port(&host, &port, url);
s = gitno_connect(host, port);
connected = 1;
error = send_request(s, NULL, url);
t->socket = s;
free(host);
free(port);
if (error < GIT_SUCCESS && s > 0)
close(s);
if (!connected)
error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses");
return error;
}
/*
* Read from the socket and store the references in the vector
*/
static int store_refs(transport_git *t)
{
gitno_buffer buf;
int s = t->socket;
git_vector *refs = &t->refs;
int error = GIT_SUCCESS;
char buffer[1024];
const char *line_end, *ptr;
git_pkt *pkt;
gitno_buffer_setup(&buf, buffer, sizeof(buffer), s);
while (1) {
error = gitno_recv(&buf);
if (error < GIT_SUCCESS)
return git__rethrow(GIT_EOSERR, "Failed to receive data");
if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */
return GIT_SUCCESS;
ptr = buf.data;
while (1) {
if (buf.offset == 0)
break;
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset);
/*
* If the error is GIT_ESHORTBUFFER, it means the buffer
* isn't long enough to satisfy the request. Break out and
* wait for more input.
* On any other error, fail.
*/
if (error == GIT_ESHORTBUFFER) {
break;
}
if (error < GIT_SUCCESS) {
return error;
}
/* Get rid of the part we've used already */
gitno_consume(&buf, line_end);
error = git_vector_insert(refs, pkt);
if (error < GIT_SUCCESS)
return error;
if (pkt->type == GIT_PKT_FLUSH)
return GIT_SUCCESS;
}
}
return error;
}
/*
* Since this is a network connection, we need to parse and store the
* pkt-lines at this stage and keep them there.
*/
static int git_connect(git_transport *transport, int direction)
{
transport_git *t = (transport_git *) transport;
int error = GIT_SUCCESS;
if (direction == GIT_DIR_PUSH)
return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol");
t->parent.direction = direction;
error = git_vector_init(&t->refs, 16, NULL);
if (error < GIT_SUCCESS)
goto cleanup;
/* Connect and ask for the refs */
error = do_connect(t, transport->url);
if (error < GIT_SUCCESS)
return error;
t->parent.connected = 1;
error = store_refs(t);
cleanup:
if (error < GIT_SUCCESS) {
git_vector_free(&t->refs);
}
return error;
}
static int git_ls(git_transport *transport, git_headarray *array)
{
transport_git *t = (transport_git *) transport;
git_vector *refs = &t->refs;
int len = 0;
unsigned int i;
array->heads = git__calloc(refs->length, sizeof(git_remote_head *));
if (array->heads == NULL)
return GIT_ENOMEM;
for (i = 0; i < refs->length; ++i) {
git_pkt *p = git_vector_get(refs, i);
if (p->type != GIT_PKT_REF)
continue;
++len;
array->heads[i] = &(((git_pkt_ref *) p)->head);
}
array->len = len;
t->heads = array->heads;
return GIT_SUCCESS;
}
static int git_close(git_transport *transport)
{
transport_git *t = (transport_git*) transport;
int s = t->socket;
int error;
/* Can't do anything if there's an error, so don't bother checking */
git_pkt_send_flush(s);
error = close(s);
if (error < 0)
error = git__throw(GIT_EOSERR, "Failed to close socket");
return error;
}
static void git_free(git_transport *transport)
{
transport_git *t = (transport_git *) transport;
git_vector *refs = &t->refs;
unsigned int i;
for (i = 0; i < refs->length; ++i) {
git_pkt *p = git_vector_get(refs, i);
git_pkt_free(p);
}
git_vector_free(refs);
free(t->heads);
free(t->parent.url);
free(t);
}
int git_transport_git(git_transport **out)
{
transport_git *t;
t = git__malloc(sizeof(transport_git));
if (t == NULL)
return GIT_ENOMEM;
memset(t, 0x0, sizeof(transport_git));
t->parent.connect = git_connect;
t->parent.ls = git_ls;
t->parent.close = git_close;
t->parent.free = git_free;
*out = (git_transport *) t;
return GIT_SUCCESS;
}
#include "common.h"
#include "git2/types.h"
#include "git2/transport.h"
#include "git2/net.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "git2/tag.h"
#include "refs.h"
#include "transport.h"
typedef struct {
git_transport parent;
git_repository *repo;
git_vector *refs;
} transport_local;
static int cmp_refs(const void *a, const void *b)
{
const char *stra = *(const char **) a;
const char *strb = *(const char **) b;
return strcmp(stra, strb);
}
/*
* Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves.
*/
static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
{
git_repository *repo;
int error;
transport_local *t = (transport_local *) transport;
const char *path;
const char file_prefix[] = "file://";
GIT_UNUSED_ARG(dir);
/* The repo layer doesn't want the prefix */
if (!git__prefixcmp(transport->url, file_prefix))
path = transport->url + STRLEN(file_prefix);
else
path = transport->url;
error = git_repository_open(&repo, path);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to open remote");
t->repo = repo;
t->parent.connected = 1;
return GIT_SUCCESS;
}
static int add_ref(const char *name, git_repository *repo, git_vector *vec)
{
const char peeled[] = "^{}";
git_remote_head *head;
git_reference *ref;
git_object *obj = NULL;
int error = GIT_SUCCESS, peel_len, ret;
head = git__malloc(sizeof(git_remote_head));
if (head == NULL)
return GIT_ENOMEM;
head->name = git__strdup(name);
if (head->name == NULL) {
error = GIT_ENOMEM;
goto out;
}
error = git_reference_lookup(&ref, repo, name);
if (error < GIT_SUCCESS)
goto out;
error = git_reference_resolve(&ref, ref);
if (error < GIT_SUCCESS)
goto out;
git_oid_cpy(&head->oid, git_reference_oid(ref));
error = git_vector_insert(vec, head);
if (error < GIT_SUCCESS)
goto out;
/* If it's not a tag, we don't need to try to peel it */
if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
goto out;
error = git_object_lookup(&obj, repo, &head->oid, GIT_OBJ_ANY);
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to lookup object");
}
/* If it's not an annotated tag, just get out */
if (git_object_type(obj) != GIT_OBJ_TAG)
goto out;
/* And if it's a tag, peel it, and add it to the list */
head = git__malloc(sizeof(git_remote_head));
peel_len = strlen(name) + STRLEN(peeled);
head->name = git__malloc(peel_len + 1);
ret = snprintf(head->name, peel_len + 1, "%s%s", name, peeled);
if (ret >= peel_len + 1) {
error = git__throw(GIT_ERROR, "The string is magically to long");
}
git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj));
error = git_vector_insert(vec, head);
if (error < GIT_SUCCESS)
goto out;
out:
git_object_close(obj);
if (error < GIT_SUCCESS) {
free(head->name);
free(head);
}
return error;
}
static int local_ls(git_transport *transport, git_headarray *array)
{
int error;
unsigned int i;
git_repository *repo;
git_vector *vec;
git_strarray refs;
transport_local *t = (transport_local *) transport;
assert(transport && transport->connected);
repo = t->repo;
error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to list remote heads");
vec = git__malloc(sizeof(git_vector));
if (vec == NULL) {
error = GIT_ENOMEM;
goto out;
}
error = git_vector_init(vec, refs.count, NULL);
if (error < GIT_SUCCESS)
return error;
/* Sort the references first */
qsort(refs.strings, refs.count, sizeof(char *), cmp_refs);
/* Add HEAD */
error = add_ref(GIT_HEAD_FILE, repo, vec);
if (error < GIT_SUCCESS)
goto out;
for (i = 0; i < refs.count; ++i) {
error = add_ref(refs.strings[i], repo, vec);
if (error < GIT_SUCCESS)
goto out;
}
array->len = vec->length;
array->heads = (git_remote_head **)vec->contents;
t->refs = vec;
out:
git_strarray_free(&refs);
return error;
}
static int local_close(git_transport *GIT_UNUSED(transport))
{
/* Nothing to do */
GIT_UNUSED_ARG(transport);
return GIT_SUCCESS;
}
static void local_free(git_transport *transport)
{
unsigned int i;
transport_local *t = (transport_local *) transport;
git_vector *vec = t->refs;
assert(transport);
for (i = 0; i < vec->length; ++i) {
git_remote_head *h = git_vector_get(vec, i);
free(h->name);
free(h);
}
git_vector_free(vec);
free(vec);
git_repository_free(t->repo);
free(t->parent.url);
free(t);
}
/**************
* Public API *
**************/
int git_transport_local(git_transport **out)
{
transport_local *t;
t = git__malloc(sizeof(transport_local));
if (t == NULL)
return GIT_ENOMEM;
t->parent.connect = local_connect;
t->parent.ls = local_ls;
t->parent.close = local_close;
t->parent.free = local_free;
*out = (git_transport *) t;
return GIT_SUCCESS;
}
#include <git2.h>
#include "common.h"
#include "fnmatch.h"
#include <stdarg.h>
#include <stdio.h>
#include <ctype.h>
#ifdef _MSV_VER
# include <Shlwapi.h>
#else
# include <fnmatch.h>
#endif
void git_libgit2_version(int *major, int *minor, int *rev)
{
*major = LIBGIT2_VER_MAJOR;
......@@ -20,6 +27,21 @@ void git_strarray_free(git_strarray *array)
free(array->strings);
}
int git__fnmatch(const char *pattern, const char *name, int flags)
{
int ret;
ret = fnmatch(pattern, name, flags);
switch (ret) {
case 0:
return GIT_SUCCESS;
case FNM_NOMATCH:
return GIT_ENOMATCH;
default:
return git__throw(GIT_EOSERR, "Error trying to match path");
}
}
int git__strtol32(long *result, const char *nptr, const char **endptr, int base)
{
const char *p;
......
......@@ -4,6 +4,9 @@
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
#define bitsizeof(x) (CHAR_BIT * sizeof(x))
#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))
#ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
#endif
/*
* Custom memory allocation wrappers
......@@ -94,6 +97,8 @@ extern void git__strtolower(char *str);
#define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1)
extern int git__fnmatch(const char *pattern, const char *name, int flags);
/*
* Realloc the buffer pointed at by variable 'x' so that it can hold
* at least 'nr' entries; the number of entries currently allocated
......
......@@ -3,3 +3,6 @@
filemode = true
bare = true
logallrefupdates = true
[remote "test"]
url = git://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/test/*
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "test_lib.h"
#include "test_helpers.h"
#include <git2.h>
BEGIN_TEST(remotes0, "remote parsing works")
git_remote *remote;
git_repository *repo;
git_config *cfg;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_config(&cfg, repo, NULL, NULL));
must_pass(git_remote_get(&remote, cfg, "test"));
must_be_true(!strcmp(git_remote_name(remote), "test"));
must_be_true(!strcmp(git_remote_url(remote), "git://github.com/libgit2/libgit2"));
git_remote_free(remote);
git_config_free(cfg);
git_repository_free(repo);
END_TEST
BEGIN_TEST(refspec0, "remote with refspec works")
git_remote *remote;
git_repository *repo;
git_config *cfg;
const git_refspec *refspec = NULL;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_config(&cfg, repo, NULL, NULL));
must_pass(git_remote_get(&remote, cfg, "test"));
refspec = git_remote_fetchspec(remote);
must_be_true(refspec != NULL);
must_be_true(!strcmp(git_refspec_src(refspec), "refs/heads/*"));
must_be_true(!strcmp(git_refspec_dst(refspec), "refs/remotes/test/*"));
git_remote_free(remote);
git_config_free(cfg);
git_repository_free(repo);
END_TEST
BEGIN_TEST(refspec1, "remote fnmatch works as expected")
git_remote *remote;
git_repository *repo;
git_config *cfg;
const git_refspec *refspec = NULL;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_config(&cfg, repo, NULL, NULL));
must_pass(git_remote_get(&remote, cfg, "test"));
refspec = git_remote_fetchspec(remote);
must_be_true(refspec != NULL);
must_pass(git_refspec_src_match(refspec, "refs/heads/master"));
must_pass(git_refspec_src_match(refspec, "refs/heads/multi/level/branch"));
git_remote_free(remote);
git_config_free(cfg);
git_repository_free(repo);
END_TEST
BEGIN_TEST(refspec2, "refspec transform")
git_remote *remote;
git_repository *repo;
git_config *cfg;
const git_refspec *refspec = NULL;
char ref[1024] = {0};
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_repository_config(&cfg, repo, NULL, NULL));
must_pass(git_remote_get(&remote, cfg, "test"));
refspec = git_remote_fetchspec(remote);
must_be_true(refspec != NULL);
must_pass(git_refspec_transform(ref, sizeof(ref), refspec, "refs/heads/master"));
must_be_true(!strcmp(ref, "refs/remotes/test/master"));
git_remote_free(remote);
git_config_free(cfg);
git_repository_free(repo);
END_TEST
BEGIN_SUITE(remotes)
ADD_TEST(remotes0)
ADD_TEST(refspec0)
ADD_TEST(refspec1)
ADD_TEST(refspec2)
END_SUITE
......@@ -43,6 +43,7 @@ DECLARE_SUITE(refs);
DECLARE_SUITE(repository);
DECLARE_SUITE(threads);
DECLARE_SUITE(config);
DECLARE_SUITE(remotes);
static libgit2_suite suite_methods[]= {
SUITE_NAME(core),
......@@ -59,6 +60,7 @@ static libgit2_suite suite_methods[]= {
SUITE_NAME(repository),
SUITE_NAME(threads),
SUITE_NAME(config),
SUITE_NAME(remotes),
};
#define GIT_SUITE_COUNT (ARRAY_SIZE(suite_methods))
......
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