mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-08 19:51:31 +00:00
Merge pull request #300 from carlosmn/gsoc2011/master
A bit of networking
This commit is contained in:
commit
f12aa9dc5e
@ -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
|
||||
|
9
include/git2/branch.h
Normal file
9
include/git2/branch.h
Normal file
@ -0,0 +1,9 @@
|
||||
#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;
|
||||
|
||||
/**
|
||||
|
33
include/git2/net.h
Normal file
33
include/git2/net.h
Normal file
@ -0,0 +1,33 @@
|
||||
#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
|
56
include/git2/pkt.h
Normal file
56
include/git2/pkt.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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);
|
42
include/git2/refspec.h
Normal file
42
include/git2/refspec.h
Normal file
@ -0,0 +1,42 @@
|
||||
#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
|
87
include/git2/remote.h
Normal file
87
include/git2/remote.h
Normal file
@ -0,0 +1,87 @@
|
||||
#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
|
58
include/git2/transport.h
Normal file
58
include/git2/transport.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
489
src/fnmatch.c
Normal file
489
src/fnmatch.c
Normal file
@ -0,0 +1,489 @@
|
||||
/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
This 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.
|
||||
|
||||
This 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 this 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. */
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
/* Enable GNU extensions in fnmatch.h. */
|
||||
#ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fnmatch.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if defined _MSC_VER
|
||||
# define HAVE_STRING_H 1
|
||||
#endif
|
||||
|
||||
#if HAVE_STRING_H || defined _LIBC
|
||||
# include <string.h>
|
||||
#else
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#if defined STDC_HEADERS || defined _LIBC
|
||||
# include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/* For platforms which support the ISO C amendment 1 functionality we
|
||||
support user defined character classes. */
|
||||
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
|
||||
# include <wchar.h>
|
||||
# include <wctype.h>
|
||||
#endif
|
||||
|
||||
/* Comment out all this code if we are using the GNU C Library, and are not
|
||||
actually compiling the library itself. This code is part of the GNU C
|
||||
Library, but also included in many other GNU distributions. Compiling
|
||||
and linking in this code is a waste when using the GNU C library
|
||||
(especially if it is a shared library). Rather than having every GNU
|
||||
program understand `configure --with-gnu-libc' and omit the object files,
|
||||
it is simpler to just do this in the source for each such file. */
|
||||
|
||||
#if defined _LIBC || !defined __GNU_LIBRARY__
|
||||
|
||||
|
||||
# if defined STDC_HEADERS || !defined isascii
|
||||
# define ISASCII(c) 1
|
||||
# else
|
||||
# define ISASCII(c) isascii(c)
|
||||
# endif
|
||||
|
||||
# ifdef isblank
|
||||
# define ISBLANK(c) (ISASCII (c) && isblank (c))
|
||||
# else
|
||||
# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
|
||||
# endif
|
||||
# ifdef isgraph
|
||||
# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
|
||||
# else
|
||||
# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
|
||||
# endif
|
||||
|
||||
# define ISPRINT(c) (ISASCII (c) && isprint (c))
|
||||
# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
|
||||
# define ISALNUM(c) (ISASCII (c) && isalnum (c))
|
||||
# define ISALPHA(c) (ISASCII (c) && isalpha (c))
|
||||
# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
|
||||
# define ISLOWER(c) (ISASCII (c) && islower (c))
|
||||
# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
|
||||
# define ISSPACE(c) (ISASCII (c) && isspace (c))
|
||||
# define ISUPPER(c) (ISASCII (c) && isupper (c))
|
||||
# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
|
||||
|
||||
# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
|
||||
|
||||
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
/* The GNU C library provides support for user-defined character classes
|
||||
and the functions from ISO C amendment 1. */
|
||||
# ifdef CHARCLASS_NAME_MAX
|
||||
# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
|
||||
# else
|
||||
/* This shouldn't happen but some implementation might still have this
|
||||
problem. Use a reasonable default value. */
|
||||
# define CHAR_CLASS_MAX_LENGTH 256
|
||||
# endif
|
||||
|
||||
# ifdef _LIBC
|
||||
# define IS_CHAR_CLASS(string) __wctype (string)
|
||||
# else
|
||||
# define IS_CHAR_CLASS(string) wctype (string)
|
||||
# endif
|
||||
# else
|
||||
# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
|
||||
|
||||
# define IS_CHAR_CLASS(string) \
|
||||
(STREQ (string, "alpha") || STREQ (string, "upper") \
|
||||
|| STREQ (string, "lower") || STREQ (string, "digit") \
|
||||
|| STREQ (string, "alnum") || STREQ (string, "xdigit") \
|
||||
|| STREQ (string, "space") || STREQ (string, "print") \
|
||||
|| STREQ (string, "punct") || STREQ (string, "graph") \
|
||||
|| STREQ (string, "cntrl") || STREQ (string, "blank"))
|
||||
# endif
|
||||
|
||||
/* Avoid depending on library functions or files
|
||||
whose names are inconsistent. */
|
||||
|
||||
# if !defined _LIBC && !defined getenv
|
||||
extern char *getenv ();
|
||||
# endif
|
||||
|
||||
# ifndef errno
|
||||
extern int errno;
|
||||
# endif
|
||||
|
||||
# ifndef NULL
|
||||
# define NULL 0
|
||||
# endif
|
||||
|
||||
/* This function doesn't exist on most systems. */
|
||||
|
||||
# if !defined HAVE___STRCHRNUL && !defined _LIBC
|
||||
static char *
|
||||
__strchrnul (const char *s, int c)
|
||||
{
|
||||
char *result = strchr (s, c);
|
||||
if (result == NULL)
|
||||
result = strchr (s, '\0');
|
||||
return result;
|
||||
}
|
||||
# endif
|
||||
|
||||
# ifndef internal_function
|
||||
/* Inside GNU libc we mark some function in a special way. In other
|
||||
environments simply ignore the marking. */
|
||||
# define internal_function
|
||||
# endif
|
||||
|
||||
/* Match STRING against the filename pattern PATTERN, returning zero if
|
||||
it matches, nonzero if not. */
|
||||
static int internal_fnmatch __P ((const char *pattern, const char *string,
|
||||
int no_leading_period, int flags))
|
||||
internal_function;
|
||||
static int
|
||||
internal_function
|
||||
internal_fnmatch(const char *pattern, const char *string,
|
||||
int no_leading_period ,int flags)
|
||||
{
|
||||
register const char *p = pattern, *n = string;
|
||||
register unsigned char c;
|
||||
|
||||
/* Note that this evaluates C many times. */
|
||||
# ifdef _LIBC
|
||||
# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
|
||||
# else
|
||||
# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
|
||||
# endif
|
||||
|
||||
while ((c = *p++) != '\0')
|
||||
{
|
||||
c = (unsigned char) FOLD (c);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '?':
|
||||
if (*n == '\0')
|
||||
return FNM_NOMATCH;
|
||||
else if (*n == '/' && (flags & FNM_FILE_NAME))
|
||||
return FNM_NOMATCH;
|
||||
else if (*n == '.' && no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if (!(flags & FNM_NOESCAPE))
|
||||
{
|
||||
c = *p++;
|
||||
if (c == '\0')
|
||||
/* Trailing \ loses. */
|
||||
return FNM_NOMATCH;
|
||||
c = (unsigned char) FOLD (c);
|
||||
}
|
||||
if (FOLD ((unsigned char) *n) != c)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (*n == '.' && no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/' && (flags & FNM_FILE_NAME))))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
for (c = *p++; c == '?' || c == '*'; c = *p++)
|
||||
{
|
||||
if (*n == '/' && (flags & FNM_FILE_NAME))
|
||||
/* A slash does not match a wildcard under FNM_FILE_NAME. */
|
||||
return FNM_NOMATCH;
|
||||
else if (c == '?')
|
||||
{
|
||||
/* A ? needs to match one character. */
|
||||
if (*n == '\0')
|
||||
/* There isn't another character; no match. */
|
||||
return FNM_NOMATCH;
|
||||
else
|
||||
/* One character of the string is consumed in matching
|
||||
this ? wildcard, so *??? won't match if there are
|
||||
less than three characters. */
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
/* The wildcard(s) is/are the last element of the pattern.
|
||||
If the name is a file name and contains another slash
|
||||
this does mean it cannot match. */
|
||||
return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
|
||||
? FNM_NOMATCH : 0);
|
||||
else
|
||||
{
|
||||
const char *endp;
|
||||
|
||||
endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
|
||||
|
||||
if (c == '[')
|
||||
{
|
||||
int flags2 = ((flags & FNM_FILE_NAME)
|
||||
? flags : (flags & ~FNM_PERIOD));
|
||||
|
||||
for (--p; n < endp; ++n)
|
||||
if (internal_fnmatch (p, n,
|
||||
(no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/'
|
||||
&& (flags
|
||||
& FNM_FILE_NAME)))),
|
||||
flags2)
|
||||
== 0)
|
||||
return 0;
|
||||
}
|
||||
else if (c == '/' && (flags & FNM_FILE_NAME))
|
||||
{
|
||||
while (*n != '\0' && *n != '/')
|
||||
++n;
|
||||
if (*n == '/'
|
||||
&& (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
|
||||
flags) == 0))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int flags2 = ((flags & FNM_FILE_NAME)
|
||||
? flags : (flags & ~FNM_PERIOD));
|
||||
|
||||
if (c == '\\' && !(flags & FNM_NOESCAPE))
|
||||
c = *p;
|
||||
c = (unsigned char) FOLD (c);
|
||||
for (--p; n < endp; ++n)
|
||||
if (FOLD ((unsigned char) *n) == c
|
||||
&& (internal_fnmatch (p, n,
|
||||
(no_leading_period
|
||||
&& (n == string
|
||||
|| (n[-1] == '/'
|
||||
&& (flags
|
||||
& FNM_FILE_NAME)))),
|
||||
flags2) == 0))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we come here no match is possible with the wildcard. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
case '[':
|
||||
{
|
||||
/* Nonzero if the sense of the character class is inverted. */
|
||||
static int posixly_correct;
|
||||
register int not;
|
||||
char cold;
|
||||
|
||||
if (posixly_correct == 0)
|
||||
posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
|
||||
|
||||
if (*n == '\0')
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (*n == '.' && no_leading_period && (n == string
|
||||
|| (n[-1] == '/'
|
||||
&& (flags
|
||||
& FNM_FILE_NAME))))
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (*n == '/' && (flags & FNM_FILE_NAME))
|
||||
/* `/' cannot be matched. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
|
||||
if (not)
|
||||
++p;
|
||||
|
||||
c = *p++;
|
||||
for (;;)
|
||||
{
|
||||
unsigned char fn;
|
||||
fn = (unsigned char) FOLD ((unsigned char) *n);
|
||||
|
||||
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||
{
|
||||
if (*p == '\0')
|
||||
return FNM_NOMATCH;
|
||||
c = (unsigned char) FOLD ((unsigned char) *p);
|
||||
++p;
|
||||
|
||||
if (c == fn)
|
||||
goto matched;
|
||||
}
|
||||
else if (c == '[' && *p == ':')
|
||||
{
|
||||
/* Leave room for the null. */
|
||||
char str[CHAR_CLASS_MAX_LENGTH + 1];
|
||||
size_t c1 = 0;
|
||||
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
wctype_t wt;
|
||||
# endif
|
||||
const char *startp = p;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (c1 == CHAR_CLASS_MAX_LENGTH)
|
||||
/* The name is too long and therefore the pattern
|
||||
is ill-formed. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
c = *++p;
|
||||
if (c == ':' && p[1] == ']')
|
||||
{
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (c < 'a' || c >= 'z')
|
||||
{
|
||||
/* This cannot possibly be a character class name.
|
||||
Match it as a normal range. */
|
||||
p = startp;
|
||||
c = '[';
|
||||
goto normal_bracket;
|
||||
}
|
||||
str[c1++] = c;
|
||||
}
|
||||
str[c1] = '\0';
|
||||
|
||||
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
|
||||
wt = IS_CHAR_CLASS (str);
|
||||
if (wt == 0)
|
||||
/* Invalid character class name. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (__iswctype (__btowc ((unsigned char) *n), wt))
|
||||
goto matched;
|
||||
# else
|
||||
if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
|
||||
|| (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
|
||||
|| (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
|
||||
|| (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
|
||||
|| (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
|
||||
|| (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
|
||||
|| (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
|
||||
|| (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
|
||||
|| (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
|
||||
|| (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
|
||||
|| (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
|
||||
|| (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
|
||||
goto matched;
|
||||
# endif
|
||||
}
|
||||
else if (c == '\0')
|
||||
/* [ (unterminated) loses. */
|
||||
return FNM_NOMATCH;
|
||||
else
|
||||
{
|
||||
normal_bracket:
|
||||
if (FOLD (c) == fn)
|
||||
goto matched;
|
||||
|
||||
cold = c;
|
||||
c = *p++;
|
||||
|
||||
if (c == '-' && *p != ']')
|
||||
{
|
||||
/* It is a range. */
|
||||
unsigned char cend = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && cend == '\\')
|
||||
cend = *p++;
|
||||
if (cend == '\0')
|
||||
return FNM_NOMATCH;
|
||||
|
||||
if (cold <= fn && fn <= FOLD (cend))
|
||||
goto matched;
|
||||
|
||||
c = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == ']')
|
||||
break;
|
||||
}
|
||||
|
||||
if (!not)
|
||||
return FNM_NOMATCH;
|
||||
break;
|
||||
|
||||
matched:
|
||||
/* Skip the rest of the [...] that already matched. */
|
||||
while (c != ']')
|
||||
{
|
||||
if (c == '\0')
|
||||
/* [... (unterminated) loses. */
|
||||
return FNM_NOMATCH;
|
||||
|
||||
c = *p++;
|
||||
if (!(flags & FNM_NOESCAPE) && c == '\\')
|
||||
{
|
||||
if (*p == '\0')
|
||||
return FNM_NOMATCH;
|
||||
/* XXX 1003.2d11 is unclear if this is right. */
|
||||
++p;
|
||||
}
|
||||
else if (c == '[' && *p == ':')
|
||||
{
|
||||
do
|
||||
if (*++p == '\0')
|
||||
return FNM_NOMATCH;
|
||||
while (*p != ':' || p[1] == ']');
|
||||
p += 2;
|
||||
c = *p;
|
||||
}
|
||||
}
|
||||
if (not)
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c != FOLD ((unsigned char) *n))
|
||||
return FNM_NOMATCH;
|
||||
}
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
if (*n == '\0')
|
||||
return 0;
|
||||
|
||||
if ((flags & FNM_LEADING_DIR) && *n == '/')
|
||||
/* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
|
||||
return 0;
|
||||
|
||||
return FNM_NOMATCH;
|
||||
|
||||
# undef FOLD
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
fnmatch(const char *pattern, const char *string, int flags)
|
||||
{
|
||||
return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
|
||||
}
|
||||
|
||||
#endif /* _LIBC or not __GNU_LIBRARY__. */
|
84
src/fnmatch.h
Normal file
84
src/fnmatch.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* 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 */
|
144
src/netops.c
Normal file
144
src/netops.c
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
22
src/netops.h
Normal file
22
src/netops.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
210
src/pkt.c
Normal file
210
src/pkt.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
108
src/refspec.c
Normal file
108
src/refspec.c
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
14
src/refspec.h
Normal file
14
src/refspec.h
Normal file
@ -0,0 +1,14 @@
|
||||
#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
|
218
src/remote.c
Normal file
218
src/remote.c
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
16
src/remote.h
Normal file
16
src/remote.h
Normal file
@ -0,0 +1,16 @@
|
||||
#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
|
91
src/transport.c
Normal file
91
src/transport.c
Normal file
@ -0,0 +1,91 @@
|
||||
#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);
|
||||
}
|
77
src/transport.h
Normal file
77
src/transport.h
Normal file
@ -0,0 +1,77 @@
|
||||
#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
|
337
src/transport_git.c
Normal file
337
src/transport_git.c
Normal file
@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
223
src/transport_local.c
Normal file
223
src/transport_local.c
Normal file
@ -0,0 +1,223 @@
|
||||
#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;
|
||||
}
|
22
src/util.c
22
src/util.c
@ -1,9 +1,16 @@
|
||||
#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
|
||||
|
Binary file not shown.
106
tests/t16-remotes.c
Normal file
106
tests/t16-remotes.c
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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))
|
||||
|
Loading…
Reference in New Issue
Block a user