mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 12:12:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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"
 | 
						|
#include "fetch.h"
 | 
						|
#include "refs.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_new(git_remote **out, git_repository *repo, const char *url)
 | 
						|
{
 | 
						|
	git_remote *remote;
 | 
						|
 | 
						|
	remote = git__malloc(sizeof(git_remote));
 | 
						|
	if (remote == NULL)
 | 
						|
		return GIT_ENOMEM;
 | 
						|
 | 
						|
	memset(remote, 0x0, sizeof(git_remote));
 | 
						|
	remote->repo = repo;
 | 
						|
	remote->url = git__strdup(url);
 | 
						|
	if (remote->url == NULL) {
 | 
						|
		free(remote);
 | 
						|
		return GIT_ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	*out = remote;
 | 
						|
	return GIT_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
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 = p_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->repo = cfg->repo;
 | 
						|
	remote->url = git__strdup(val);
 | 
						|
	if (remote->url == NULL) {
 | 
						|
		error = GIT_ENOMEM;
 | 
						|
		goto cleanup;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = p_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 = p_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 = t->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)
 | 
						|
		t->free(t);
 | 
						|
 | 
						|
	return error;
 | 
						|
}
 | 
						|
 | 
						|
int git_remote_ls(git_remote *remote, git_headarray *refs)
 | 
						|
{
 | 
						|
	return remote->transport->ls(remote->transport, refs);
 | 
						|
}
 | 
						|
 | 
						|
int git_remote_negotiate(git_remote *remote)
 | 
						|
{
 | 
						|
	return git_fetch_negotiate(remote);
 | 
						|
}
 | 
						|
 | 
						|
int git_remote_download(char **filename, git_remote *remote)
 | 
						|
{
 | 
						|
	return git_fetch_download_pack(filename, remote);
 | 
						|
}
 | 
						|
 | 
						|
git_headarray *git_remote_tips(git_remote *remote)
 | 
						|
{
 | 
						|
	return &remote->refs;
 | 
						|
}
 | 
						|
 | 
						|
int git_remote_update_tips(struct git_remote *remote)
 | 
						|
{
 | 
						|
	int error = GIT_SUCCESS;
 | 
						|
	unsigned int i;
 | 
						|
	char refname[GIT_PATH_MAX];
 | 
						|
	git_headarray *refs = &remote->refs;
 | 
						|
	git_remote_head *head;
 | 
						|
	git_reference *ref;
 | 
						|
	struct git_refspec *spec = &remote->fetch;
 | 
						|
 | 
						|
	memset(refname, 0x0, sizeof(refname));
 | 
						|
 | 
						|
	for (i = 0; i < refs->len; ++i) {
 | 
						|
		head = refs->heads[i];
 | 
						|
		error = git_refspec_transform(refname, sizeof(refname), spec, head->name);
 | 
						|
		if (error < GIT_SUCCESS)
 | 
						|
			return error;
 | 
						|
 | 
						|
		error = git_reference_create_oid(&ref, remote->repo, refname, &head->oid, 1);
 | 
						|
		if (error < GIT_SUCCESS)
 | 
						|
			return error;
 | 
						|
	}
 | 
						|
 | 
						|
	return GIT_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
void git_remote_free(git_remote *remote)
 | 
						|
{
 | 
						|
	if (remote == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	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)
 | 
						|
			remote->transport->close(remote->transport);
 | 
						|
 | 
						|
		remote->transport->free(remote->transport);
 | 
						|
	}
 | 
						|
	free(remote);
 | 
						|
}
 |