Commit fc1f7d4f by Ben Straub

Merge branch 'development' into blame

Conflicts:
	include/git2.h
parents de8fe729 ab136876
......@@ -56,6 +56,8 @@ FUNCTION(TARGET_OS_LIBRARIES target)
TARGET_LINK_LIBRARIES(${target} ws2_32)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
TARGET_LINK_LIBRARIES(${target} socket nsl)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
TARGET_LINK_LIBRARIES(${target} rt)
ENDIF ()
IF(THREADSAFE)
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
......
......@@ -74,9 +74,9 @@ int do_clone(git_repository *repo, int argc, char **argv)
checkout_opts.progress_cb = checkout_progress;
checkout_opts.progress_payload = &pd;
clone_opts.checkout_opts = checkout_opts;
clone_opts.fetch_progress_cb = &fetch_progress;
clone_opts.fetch_progress_payload = &pd;
clone_opts.cred_acquire_cb = cred_acquire_cb;
clone_opts.remote_callbacks.transfer_progress = &fetch_progress;
clone_opts.remote_callbacks.credentials = cred_acquire_cb;
clone_opts.remote_callbacks.payload = &pd;
// Do the clone
error = git_clone(&cloned_repo, url, path, &clone_opts);
......
......@@ -35,7 +35,7 @@ static void *download(void *ptr)
// Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you
// inform the user about progress.
if (git_remote_download(data->remote, NULL, NULL) < 0) {
if (git_remote_download(data->remote) < 0) {
data->ret = -1;
goto exit;
}
......@@ -91,8 +91,8 @@ int fetch(git_repository *repo, int argc, char **argv)
// Set up the callbacks (only update_tips for now)
callbacks.update_tips = &update_cb;
callbacks.progress = &progress_cb;
callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks);
git_remote_set_cred_acquire_cb(remote, &cred_acquire_cb, NULL);
// Set up the information for the background worker thread
data.remote = remote;
......
......@@ -18,6 +18,7 @@ static int use_remote(git_repository *repo, char *name)
{
git_remote *remote = NULL;
int error;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
// Find the remote by name
error = git_remote_load(&remote, repo, name);
......@@ -27,7 +28,8 @@ static int use_remote(git_repository *repo, char *name)
goto cleanup;
}
git_remote_set_cred_acquire_cb(remote, &cred_acquire_cb, NULL);
callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks);
error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
if (error < 0)
......
......@@ -8,58 +8,51 @@
#ifndef INCLUDE_git_git_h__
#define INCLUDE_git_git_h__
#include "git2/version.h"
#include "git2/common.h"
#include "git2/threads.h"
#include "git2/errors.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/signature.h"
#include "git2/odb.h"
#include "git2/repository.h"
#include "git2/revwalk.h"
#include "git2/merge.h"
#include "git2/graph.h"
#include "git2/refs.h"
#include "git2/reflog.h"
#include "git2/revparse.h"
#include "git2/object.h"
#include "git2/attr.h"
#include "git2/blob.h"
#include "git2/blame.h"
#include "git2/branch.h"
#include "git2/buffer.h"
#include "git2/checkout.h"
#include "git2/clone.h"
#include "git2/commit.h"
#include "git2/tag.h"
#include "git2/tree.h"
#include "git2/diff.h"
#include "git2/index.h"
#include "git2/common.h"
#include "git2/config.h"
#include "git2/transport.h"
#include "git2/remote.h"
#include "git2/clone.h"
#include "git2/checkout.h"
#include "git2/push.h"
#include "git2/attr.h"
#include "git2/diff.h"
#include "git2/errors.h"
#include "git2/filter.h"
#include "git2/graph.h"
#include "git2/ignore.h"
#include "git2/branch.h"
#include "git2/refspec.h"
#include "git2/net.h"
#include "git2/status.h"
#include "git2/index.h"
#include "git2/indexer.h"
#include "git2/submodule.h"
#include "git2/notes.h"
#include "git2/reset.h"
#include "git2/merge.h"
#include "git2/message.h"
#include "git2/net.h"
#include "git2/notes.h"
#include "git2/object.h"
#include "git2/odb.h"
#include "git2/oid.h"
#include "git2/pack.h"
#include "git2/stash.h"
#include "git2/pathspec.h"
#include "git2/blame.h"
#include "git2/buffer.h"
#include "git2/filter.h"
#include "git2/push.h"
#include "git2/refdb.h"
#include "git2/reflog.h"
#include "git2/refs.h"
#include "git2/refspec.h"
#include "git2/remote.h"
#include "git2/repository.h"
#include "git2/reset.h"
#include "git2/revparse.h"
#include "git2/revwalk.h"
#include "git2/signature.h"
#include "git2/stash.h"
#include "git2/status.h"
#include "git2/submodule.h"
#include "git2/tag.h"
#include "git2/threads.h"
#include "git2/transport.h"
#include "git2/tree.h"
#include "git2/types.h"
#include "git2/version.h"
#endif
......@@ -255,7 +255,7 @@ typedef struct git_checkout_opts {
*/
GIT_EXTERN(int) git_checkout_head(
git_repository *repo,
git_checkout_opts *opts);
const git_checkout_opts *opts);
/**
* Updates files in the working tree to match the content of the index.
......
......@@ -35,30 +35,12 @@ GIT_BEGIN_DECL
* set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT.
* - `bare` should be set to zero to create a standard repo, non-zero for
* a bare repo
* - `fetch_progress_cb` is optional callback for fetch progress. Be aware that
* this is called inline with network and indexing operations, so performance
* may be affected.
* - `fetch_progress_payload` is payload for fetch_progress_cb
* - `ignore_cert_errors` should be set to 1 if errors validating the remote host's
* certificate should be ignored.
*
* ** "origin" remote options: **
* - `remote_name` is the name given to the "origin" remote. The default is
* "origin".
* - `pushurl` is a URL to be used for pushing. NULL means use the fetch url.
* - `fetch_spec` is the fetch specification to be used for fetching. NULL
* results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.
* - `push_spec` is the fetch specification to be used for pushing. NULL means
* use the same spec as for fetching.
* - `cred_acquire_cb` is a callback to be used if credentials are required
* during the initial fetch.
* - `cred_acquire_payload` is the payload for the above callback.
* - `transport_flags` is flags used to create transport if no transport is
* provided.
* - `transport` is a custom transport to be used for the initial fetch. NULL
* means use the transport autodetected from the URL.
* - `remote_callbacks` may be used to specify custom progress callbacks for
* the origin remote before the fetch is initiated.
* - `remote_autotag` may be used to specify the autotag setting before the
* initial fetch. The default is GIT_REMOTE_DOWNLOAD_TAGS_ALL.
* - `checkout_branch` gives the name of the branch to checkout. NULL means
* use the remote's HEAD.
*/
......@@ -67,30 +49,23 @@ typedef struct git_clone_options {
unsigned int version;
git_checkout_opts checkout_opts;
git_repository_init_options *init_options;
int bare;
git_transfer_progress_callback fetch_progress_cb;
void *fetch_progress_payload;
git_remote_callbacks remote_callbacks;
int bare;
int ignore_cert_errors;
const char *remote_name;
const char *pushurl;
const char *fetch_spec;
const char *push_spec;
git_cred_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
git_transport_flags_t transport_flags;
git_transport *transport;
git_remote_callbacks *remote_callbacks;
git_remote_autotag_option_t remote_autotag;
const char* checkout_branch;
} git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION 1
#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}}
#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
/**
* Clone a remote repository, and checkout the branch pointed to by the remote
* HEAD.
* Clone a remote repository.
*
* This version handles the simple case. If you'd like to create the
* repository or remote with non-default settings, you can create and
* configure them and then use `git_clone_into()`.
*
* @param out pointer that will receive the resulting repository object
* @param url the remote repository to clone
......@@ -106,6 +81,22 @@ GIT_EXTERN(int) git_clone(
const char *local_path,
const git_clone_options *options);
/**
* Clone into a repository
*
* After creating the repository and remote and configuring them for
* paths and callbacks respectively, you can call this function to
* perform the clone operation and optionally checkout files.
*
* @param repo the repository to use
* @param remote the remote repository to clone from
* @param co_opts options to use during checkout
* @param branch the branch to checkout after the clone, pass NULL for the remote's
* default branch
* @return 0 on success or an error code
*/
GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch);
/** @} */
GIT_END_DECL
#endif
......@@ -100,12 +100,23 @@ GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit);
/**
* Get the full message of a commit.
*
* The returned message will be slightly prettified by removing any
* potential leading newlines.
*
* @param commit a previously loaded commit.
* @return the message of a commit
*/
GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
/**
* Get the full raw message of a commit.
*
* @param commit a previously loaded commit.
* @return the raw message of a commit
*/
GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
/**
* Get the commit time (i.e. committer time) of a commit.
*
* @param commit a previously loaded commit.
......
......@@ -46,6 +46,14 @@
GIT_BEGIN_DECL
/**
* Stages that are reported by the packbuilder progress callback.
*/
typedef enum {
GIT_PACKBUILDER_ADDING_OBJECTS = 0,
GIT_PACKBUILDER_DELTAFICATION = 1,
} git_packbuilder_stage_t;
/**
* Initialize a new packbuilder
*
* @param out The new packbuilder object
......@@ -149,6 +157,28 @@ GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
*/
GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
/** Packbuilder progress notification function */
typedef void (*git_packbuilder_progress)(
int stage,
unsigned int current,
unsigned int total,
void *payload);
/**
* Set the callbacks for a packbuilder
*
* @param pb The packbuilder object
* @param progress_cb Function to call with progress information during
* pack building. Be aware that this is called inline with pack building
* operations, so performance may be affected.
* @param progress_cb_payload Payload for progress callback.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_packbuilder_set_callbacks(
git_packbuilder *pb,
git_packbuilder_progress progress_cb,
void *progress_cb_payload);
/**
* Free the packbuilder and all associated data
*
......
......@@ -8,6 +8,7 @@
#define INCLUDE_git_push_h__
#include "common.h"
#include "pack.h"
/**
* @file git2/push.h
......@@ -38,6 +39,13 @@ typedef struct {
#define GIT_PUSH_OPTIONS_VERSION 1
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION }
/** Push network progress notification function */
typedef void (*git_push_transfer_progress)(
unsigned int current,
unsigned int total,
size_t bytes,
void* payload);
/**
* Create a new push object
*
......@@ -61,6 +69,27 @@ GIT_EXTERN(int) git_push_set_options(
const git_push_options *opts);
/**
* Set the callbacks for a push
*
* @param push The push object
* @param pack_progress_cb Function to call with progress information during
* pack building. Be aware that this is called inline with pack building
* operations, so performance may be affected.
* @param pack_progress_cb_payload Payload for the pack progress callback.
* @param transfer_progress_cb Function to call with progress information during
* the upload portion of a push. Be aware that this is called inline with
* pack building operations, so performance may be affected.
* @param transfer_progress_cb_payload Payload for the network progress callback.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_push_set_callbacks(
git_push *push,
git_packbuilder_progress pack_progress_cb,
void *pack_progress_cb_payload,
git_push_transfer_progress transfer_progress_cb,
void *transfer_progress_cb_payload);
/**
* Add a refspec to be pushed
*
* @param push The push object
......
......@@ -257,17 +257,9 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
* The .idx file will be created and both it and the packfile with be
* renamed to their final name.
*
* @param remote the remote to download from
* @param progress_cb function to call with progress information. Be aware that
* this is called inline with network and indexing operations, so performance
* may be affected.
* @param payload payload for the progress callback
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_download(
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *payload);
GIT_EXTERN(int) git_remote_download(git_remote *remote);
/**
* Check whether the remote is connected
......@@ -319,6 +311,17 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
/**
* Download new data and update tips
*
* Convenience function to connect to a remote, download the data,
* disconnect and update the remote-tracking branches.
*
* @param remote the remote to fetch from
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_fetch(git_remote *remote);
/**
* Return whether a string is a valid remote URL
*
* @param url the url to check
......@@ -397,13 +400,47 @@ typedef enum git_remote_completion_type {
/**
* The callback settings structure
*
* Set the calbacks to be called by the remote.
* Set the callbacks to be called by the remote when informing the user
* about the progress of the network operations.
*/
struct git_remote_callbacks {
unsigned int version;
/**
* Textual progress from the remote. Text send over the
* progress side-band will be passed to this function (this is
* the 'counting objects' output.
*/
void (*progress)(const char *str, int len, void *data);
/**
* Completion is called when different parts of the download
* process are done (currently unused).
*/
int (*completion)(git_remote_completion_type type, void *data);
/**
* This will be called if the remote host requires
* authentication in order to connect to it.
*/
int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data);
/**
* During the download of new data, this will be regularly
* called with the current count of progress done by the
* indexer.
*/
int (*transfer_progress)(const git_transfer_progress *stats, void *data);
/**
* Each time a reference is updated locally, this function
* will be called with information about it.
*/
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
/**
* This will be passed to each of the callbacks in this struct
* as the last parameter.
*/
void *payload;
};
......@@ -420,7 +457,7 @@ struct git_remote_callbacks {
* @param callbacks a pointer to the user's callback settings
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
/**
* Get the statistics structure that is filled in by the fetch operation.
......
......@@ -253,6 +253,40 @@ GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const
/* Signature of a function which creates a transport */
typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
/**
* Add a custom transport definition, to be used in addition to the built-in
* set of transports that come with libgit2.
*
* The caller is responsible for synchronizing calls to git_transport_register
* and git_transport_unregister with other calls to the library that
* instantiate transports.
*
* @param prefix The scheme (ending in "://") to match, i.e. "git://"
* @param priority The priority of this transport relative to others with
* the same prefix. Built-in transports have a priority of 1.
* @param cb The callback used to create an instance of the transport
* @param param A fixed parameter to pass to cb at creation time
* @return 0 or an error code
*/
GIT_EXTERN(int) git_transport_register(
const char *prefix,
unsigned priority,
git_transport_cb cb,
void *param);
/**
*
* Unregister a custom transport definition which was previously registered
* with git_transport_register.
*
* @param prefix From the previous call to git_transport_register
* @param priority From the previous call to git_transport_register
* @return 0 or an error code
*/
GIT_EXTERN(int) git_transport_unregister(
const char *prefix,
unsigned priority);
/* Transports which come with libgit2 (match git_transport_cb). The expected
* value for "param" is listed in-line below. */
......
......@@ -1119,7 +1119,7 @@ static void checkout_data_clear(checkout_data *data)
static int checkout_data_init(
checkout_data *data,
git_iterator *target,
git_checkout_opts *proposed)
const git_checkout_opts *proposed)
{
int error = 0;
git_repository *repo = git_iterator_owner(target);
......@@ -1229,7 +1229,7 @@ cleanup:
int git_checkout_iterator(
git_iterator *target,
git_checkout_opts *opts)
const git_checkout_opts *opts)
{
int error = 0;
git_iterator *baseline = NULL, *workdir = NULL;
......@@ -1404,7 +1404,7 @@ int git_checkout_tree(
int git_checkout_head(
git_repository *repo,
git_checkout_opts *opts)
const git_checkout_opts *opts)
{
int error;
git_tree *head = NULL;
......
......@@ -19,6 +19,6 @@
*/
extern int git_checkout_iterator(
git_iterator *target,
git_checkout_opts *opts);
const git_checkout_opts *opts);
#endif
......@@ -270,23 +270,23 @@ cleanup:
static int update_head_to_branch(
git_repository *repo,
const git_clone_options *options)
const char *remote_name,
const char *branch)
{
int retcode;
git_buf remote_branch_name = GIT_BUF_INIT;
git_reference* remote_ref = NULL;
assert(options->checkout_branch);
assert(remote_name && branch);
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
options->remote_name, options->checkout_branch)) < 0 )
remote_name, branch)) < 0 )
goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
goto cleanup;
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
options->checkout_branch);
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
cleanup:
git_reference_free(remote_ref);
......@@ -306,41 +306,18 @@ static int create_and_configure_origin(
{
int error;
git_remote *origin = NULL;
const char *name;
if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0)
goto on_error;
git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
options->cred_acquire_payload);
git_remote_set_autotag(origin, options->remote_autotag);
/*
* Don't write FETCH_HEAD, we'll check out the remote tracking
* branch ourselves based on the server's default.
*/
git_remote_set_update_fetchhead(origin, 0);
if (options->remote_callbacks &&
(error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
name = options->remote_name ? options->remote_name : "origin";
if ((error = git_remote_create(&origin, repo, name, url)) < 0)
goto on_error;
if (options->fetch_spec) {
git_remote_clear_refspecs(origin);
if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
goto on_error;
}
if (options->push_spec &&
(error = git_remote_add_push(origin, options->push_spec)) < 0)
goto on_error;
if (options->ignore_cert_errors)
git_remote_check_cert(origin, 0);
if (options->pushurl &&
(error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
goto on_error;
if (options->transport_flags == GIT_TRANSPORTFLAGS_NO_CHECK_CERT) {
git_remote_check_cert(origin, 0);
}
if ((error = git_remote_save(origin)) < 0)
goto on_error;
......@@ -352,59 +329,10 @@ on_error:
return error;
}
static int setup_remotes_and_fetch(
git_repository *repo,
const char *url,
const git_clone_options *options)
{
int retcode = GIT_ERROR;
git_remote *origin = NULL;
/* Construct an origin remote */
if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0)
goto on_error;
git_remote_set_update_fetchhead(origin, 0);
/* If the download_tags value has not been specified, then make sure to
* download tags as well. It is set here because we want to download tags
* on the initial clone, but do not want to persist the value in the
* configuration file.
*/
if (origin->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO &&
((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
goto on_error;
/* Connect and download everything */
if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
goto on_error;
if ((retcode = git_remote_download(origin, options->fetch_progress_cb,
options->fetch_progress_payload)) < 0)
goto on_error;
/* Create "origin/foo" branches for all remote branches */
if ((retcode = git_remote_update_tips(origin)) < 0)
goto on_error;
/* Point HEAD to the requested branch */
if (options->checkout_branch)
retcode = update_head_to_branch(repo, options);
/* Point HEAD to the same ref as the remote's head */
else
retcode = update_head_to_remote(repo, origin);
on_error:
git_remote_free(origin);
return retcode;
}
static bool should_checkout(
git_repository *repo,
bool is_bare,
git_checkout_opts *opts)
const git_checkout_opts *opts)
{
if (is_bare)
return false;
......@@ -418,39 +346,63 @@ static bool should_checkout(
return !git_repository_head_unborn(repo);
}
static void normalize_options(git_clone_options *dst, const git_clone_options *src, git_repository_init_options *initOptions)
int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
{
git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
if (!src) src = &default_options;
*dst = *src;
/* Provide defaults for null pointers */
if (!dst->remote_name) dst->remote_name = "origin";
if (!dst->init_options) {
dst->init_options = initOptions;
initOptions->flags = GIT_REPOSITORY_INIT_MKPATH;
if (dst->bare)
initOptions->flags |= GIT_REPOSITORY_INIT_BARE;
int error = 0, old_fetchhead;
size_t nspecs;
assert(repo && remote);
if (!git_repository_is_empty(repo)) {
giterr_set(GITERR_INVALID, "the repository is not empty");
return -1;
}
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
return error;
old_fetchhead = git_remote_update_fetchhead(remote);
git_remote_set_update_fetchhead(remote, 0);
if ((error = git_remote_fetch(remote)) < 0)
goto cleanup;
if (branch)
error = update_head_to_branch(repo, git_remote_name(remote), branch);
/* Point HEAD to the same ref as the remote's head */
else
error = update_head_to_remote(repo, remote);
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
error = git_checkout_head(repo, co_opts);
cleanup:
git_remote_set_update_fetchhead(remote, old_fetchhead);
/* Remove the tags refspec */
nspecs = git_remote_refspec_count(remote);
git_remote_remove_refspec(remote, nspecs);
return error;
}
int git_clone(
git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options)
const git_clone_options *_options)
{
int retcode = GIT_ERROR;
git_repository *repo = NULL;
git_clone_options normOptions;
git_remote *origin;
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
int remove_directory_on_failure = 0;
git_repository_init_options initOptions = GIT_REPOSITORY_INIT_OPTIONS_INIT;
assert(out && url && local_path);
normalize_options(&normOptions, options, &initOptions);
GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
if (_options)
memcpy(&options, _options, sizeof(git_clone_options));
GITERR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
/* Only clone to a new directory or an empty directory */
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
......@@ -462,24 +414,27 @@ int git_clone(
/* Only remove the directory on failure if we create it */
remove_directory_on_failure = !git_path_exists(local_path);
if (!(retcode = git_repository_init_ext(&repo, local_path, normOptions.init_options))) {
if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
/* Failed to fetch; clean up */
git_repository_free(repo);
if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
return retcode;
if (remove_directory_on_failure)
git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
else
git_futils_cleanupdir_r(local_path);
if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
goto cleanup;
} else {
*out = repo;
retcode = 0;
}
}
retcode = git_clone_into(repo, origin, &options.checkout_opts, options.checkout_branch);
git_remote_free(origin);
if (retcode < 0)
goto cleanup;
if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts))
retcode = git_checkout_head(*out, &normOptions.checkout_opts);
*out = repo;
return 0;
cleanup:
git_repository_free(repo);
if (remove_directory_on_failure)
git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
else
git_futils_cleanupdir_r(local_path);
return retcode;
}
......@@ -29,7 +29,7 @@ void git_commit__free(void *_commit)
git_signature_free(commit->committer);
git__free(commit->raw_header);
git__free(commit->message);
git__free(commit->raw_message);
git__free(commit->message_encoding);
git__free(commit);
......@@ -245,8 +245,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
/* extract commit message */
if (buffer <= buffer_end) {
commit->message = git__strndup(buffer, buffer_end - buffer);
GITERR_CHECK_ALLOC(commit->message);
commit->raw_message = git__strndup(buffer, buffer_end - buffer);
GITERR_CHECK_ALLOC(commit->raw_message);
}
return 0;
......@@ -265,7 +265,7 @@ bad_buffer:
GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
GIT_COMMIT_GETTER(const char *, message, commit->message)
GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
......@@ -273,6 +273,19 @@ GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
const char *git_commit_message(const git_commit *commit)
{
const char *message = commit->raw_message;
assert(commit);
/* trim leading newlines from raw message */
while (*message && *message == '\n')
++message;
return message;
}
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{
assert(commit);
......
......@@ -24,7 +24,7 @@ struct git_commit {
git_signature *committer;
char *message_encoding;
char *message;
char *raw_message;
char *raw_header;
};
......
......@@ -712,7 +712,6 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
int c, rpos;
char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT;
int quote_marks;
/*
* base_name is what came before the space. We should be at the
* first quotation mark, except for now, line isn't being kept in
......@@ -731,21 +730,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
git_buf_printf(&buf, "%s.", base_name);
rpos = 0;
quote_marks = 0;
line = first_quote;
c = line[rpos++];
c = line[++rpos];
/*
* At the end of each iteration, whatever is stored in c will be
* added to the string. In case of error, jump to out
*/
do {
if (quote_marks == 2) {
set_parse_error(reader, rpos, "Unexpected text after closing quotes");
git_buf_free(&buf);
return -1;
}
switch (c) {
case 0:
......@@ -754,25 +747,13 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
return -1;
case '"':
++quote_marks;
continue;
goto end_parse;
case '\\':
c = line[rpos++];
switch (c) {
case '"':
if (&line[rpos-1] == last_quote) {
set_parse_error(reader, 0, "Missing closing quotation mark in section header");
git_buf_free(&buf);
return -1;
}
case '\\':
break;
c = line[++rpos];
default:
set_parse_error(reader, rpos, "Unsupported escape sequence");
if (c == 0) {
set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
git_buf_free(&buf);
return -1;
}
......@@ -782,7 +763,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
}
git_buf_putc(&buf, (char)c);
} while ((c = line[rpos++]) != ']');
c = line[++rpos];
} while (line + rpos < last_quote);
end_parse:
if (line[rpos] != '"' || line[rpos + 1] != ']') {
set_parse_error(reader, rpos, "Unexpected text after closing quotes");
git_buf_free(&buf);
return -1;
}
*section_name = git_buf_detach(&buf);
return 0;
......@@ -800,7 +789,7 @@ static int parse_section_header(struct reader *reader, char **section_out)
return -1;
/* find the end of the variable's name */
name_end = strchr(line, ']');
name_end = strrchr(line, ']');
if (name_end == NULL) {
git__free(line);
set_parse_error(reader, 0, "Missing ']' in section header");
......
......@@ -119,15 +119,13 @@ int git_fetch_negotiate(git_remote *remote)
remote->refs.length);
}
int git_fetch_download_pack(
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *progress_payload)
int git_fetch_download_pack(git_remote *remote)
{
git_transport *t = remote->transport;
if(!remote->need_pack)
return 0;
return t->download_pack(t, remote->repo, &remote->stats, progress_cb, progress_payload);
return t->download_pack(t, remote->repo, &remote->stats,
remote->callbacks.transfer_progress, remote->callbacks.payload);
}
......@@ -11,10 +11,7 @@
int git_fetch_negotiate(git_remote *remote);
int git_fetch_download_pack(
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *progress_payload);
int git_fetch_download_pack(git_remote *remote);
int git_fetch__download_pack(
git_transport *t,
......
......@@ -426,7 +426,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1;
hash_partially(idx, data, size);
hash_partially(idx, data, (int)size);
/* Make sure we set the new size of the pack */
if (idx->opened_pack) {
......
......@@ -573,6 +573,93 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
}
static const char *prefix_http = "http://";
static const char *prefix_https = "https://";
int gitno_connection_data_from_url(
gitno_connection_data *data,
const char *url,
const char *service_suffix)
{
int error = -1;
const char *default_port = NULL;
char *original_host = NULL;
/* service_suffix is optional */
assert(data && url);
/* Save these for comparison later */
original_host = data->host;
data->host = NULL;
gitno_connection_data_free_ptrs(data);
if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http);
default_port = "80";
if (data->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
goto cleanup;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
data->use_ssl = true;
}
if (url[0] == '/')
default_port = data->use_ssl ? "443" : "80";
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
goto cleanup;
}
error = gitno_extract_url_parts(
&data->host, &data->port, &data->user, &data->pass,
url, default_port);
if (url[0] == '/') {
/* Relative redirect; reuse original host name and port */
git__free(data->host);
data->host = original_host;
original_host = NULL;
}
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
data->path = git__strndup(path, pathlen - suffixlen);
else
data->path = git__strdup(path);
/* Check for errors in the resulting data */
if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
}
cleanup:
if (original_host) git__free(original_host);
return error;
}
void gitno_connection_data_free_ptrs(gitno_connection_data *d)
{
git__free(d->host); d->host = NULL;
git__free(d->port); d->port = NULL;
git__free(d->path); d->path = NULL;
git__free(d->user); d->user = NULL;
git__free(d->pass); d->pass = NULL;
}
int gitno_extract_url_parts(
char **host,
char **port,
......
......@@ -66,6 +66,29 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags);
int gitno_close(gitno_socket *s);
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
typedef struct gitno_connection_data {
char *host;
char *port;
char *path;
char *user;
char *pass;
bool use_ssl;
} gitno_connection_data;
/*
* This replaces all the pointers in `data` with freshly-allocated strings,
* that the caller is responsible for freeing.
* `gitno_connection_data_free_ptrs` is good for this.
*/
int gitno_connection_data_from_url(
gitno_connection_data *data,
const char *url,
const char *service_suffix);
/* This frees all the pointers IN the struct, but not the struct itself. */
void gitno_connection_data_free_ptrs(gitno_connection_data *data);
int gitno_extract_url_parts(
char **host,
char **port,
......
......@@ -14,6 +14,7 @@
#include "pack.h"
#include "thread-utils.h"
#include "tree.h"
#include "util.h"
#include "git2/pack.h"
#include "git2/commit.h"
......@@ -57,6 +58,9 @@ struct pack_write_context {
#define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
/* The minimal interval between progress updates (in seconds). */
#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
static unsigned name_hash(const char *name)
{
unsigned c, hash = 0;
......@@ -212,6 +216,14 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
assert(ret != 0);
kh_value(pb->object_ix, pos) = po;
if (pb->progress_cb) {
double current_time = git__timer();
if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
pb->last_progress_report_time = current_time;
pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload);
}
}
pb->done = false;
return 0;
}
......@@ -1207,6 +1219,13 @@ static int prepare_pack(git_packbuilder *pb)
if (pb->nr_objects == 0 || pb->done)
return 0; /* nothing to do */
/*
* Although we do not report progress during deltafication, we
* at least report that we are in the deltafication stage
*/
if (pb->progress_cb)
pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
GITERR_CHECK_ALLOC(delta_list);
......@@ -1348,6 +1367,17 @@ uint32_t git_packbuilder_written(git_packbuilder *pb)
return pb->nr_written;
}
int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
{
if (!pb)
return -1;
pb->progress_cb = progress_cb;
pb->progress_cb_payload = progress_cb_payload;
return 0;
}
void git_packbuilder_free(git_packbuilder *pb)
{
if (pb == NULL)
......
......@@ -16,6 +16,7 @@
#include "netops.h"
#include "git2/oid.h"
#include "git2/pack.h"
#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
#define GIT_PACK_DEPTH 50 /* max delta depth */
......@@ -79,6 +80,10 @@ struct git_packbuilder {
int nr_threads; /* nr of threads to use */
git_packbuilder_progress progress_cb;
void *progress_cb_payload;
double last_progress_report_time; /* the time progress was last reported */
bool done;
};
......
......@@ -70,6 +70,25 @@ int git_push_set_options(git_push *push, const git_push_options *opts)
return 0;
}
int git_push_set_callbacks(
git_push *push,
git_packbuilder_progress pack_progress_cb,
void *pack_progress_cb_payload,
git_push_transfer_progress transfer_progress_cb,
void *transfer_progress_cb_payload)
{
if (!push)
return -1;
push->pack_progress_cb = pack_progress_cb;
push->pack_progress_cb_payload = pack_progress_cb_payload;
push->transfer_progress_cb = transfer_progress_cb;
push->transfer_progress_cb_payload = transfer_progress_cb_payload;
return 0;
}
static void free_refspec(push_spec *spec)
{
if (spec == NULL)
......@@ -583,6 +602,10 @@ static int do_push(git_push *push)
git_packbuilder_set_threads(push->pb, push->pb_parallelism);
if (push->pack_progress_cb)
if ((error = git_packbuilder_set_callbacks(push->pb, push->pack_progress_cb, push->pack_progress_cb_payload)) < 0)
goto on_error;
if ((error = calculate_work(push)) < 0 ||
(error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push)) < 0)
......
......@@ -39,6 +39,11 @@ struct git_push {
/* options */
unsigned pb_parallelism;
git_packbuilder_progress pack_progress_cb;
void *pack_progress_cb_payload;
git_push_transfer_progress transfer_progress_cb;
void *transfer_progress_cb_payload;
};
/**
......
......@@ -591,7 +591,7 @@ int git_remote_connect(git_remote *remote, git_direction direction)
if (!remote->check_cert)
flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
if (t->connect(t, url, remote->cred_acquire_cb, remote->cred_acquire_payload, direction, flags) < 0)
if (t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags) < 0)
goto on_error;
remote->transport = t;
......@@ -742,10 +742,7 @@ static int remote_head_cmp(const void *_a, const void *_b)
return git__strcmp_cb(a->name, b->name);
}
int git_remote_download(
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *progress_payload)
int git_remote_download(git_remote *remote)
{
int error;
git_vector refs;
......@@ -767,7 +764,25 @@ int git_remote_download(
if ((error = git_fetch_negotiate(remote)) < 0)
return error;
return git_fetch_download_pack(remote, progress_cb, progress_payload);
return git_fetch_download_pack(remote);
}
int git_remote_fetch(git_remote *remote)
{
int error;
/* Connect and download everything */
if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0)
return error;
if ((error = git_remote_download(remote)) < 0)
return error;
/* We don't need to be connected anymore */
git_remote_disconnect(remote);
/* Create "remote/foo" branches for all remote branches */
return git_remote_update_tips(remote);
}
static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
......@@ -1138,7 +1153,7 @@ void git_remote_check_cert(git_remote *remote, int check)
remote->check_cert = check;
}
int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
{
assert(remote && callbacks);
......@@ -1147,7 +1162,7 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
if (remote->transport && remote->transport->set_callbacks)
remote->transport->set_callbacks(remote->transport,
return remote->transport->set_callbacks(remote->transport,
remote->callbacks.progress,
NULL,
remote->callbacks.payload);
......@@ -1155,17 +1170,6 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
return 0;
}
void git_remote_set_cred_acquire_cb(
git_remote *remote,
git_cred_acquire_cb cred_acquire_cb,
void *payload)
{
assert(remote);
remote->cred_acquire_cb = cred_acquire_cb;
remote->cred_acquire_payload = payload;
}
int git_remote_set_transport(git_remote *remote, git_transport *transport)
{
assert(remote && transport);
......
......@@ -21,8 +21,6 @@ struct git_remote {
char *pushurl;
git_vector refs;
git_vector refspecs;
git_cred_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
......
......@@ -316,6 +316,8 @@ static int build_workdir_tree(
struct cb_data data = {0};
int error;
opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
goto cleanup;
......@@ -474,12 +476,14 @@ static int ensure_there_are_changes_to_stash(
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
if (include_untracked_files)
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
if (include_ignored_files)
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
......
......@@ -42,6 +42,8 @@ static transport_definition transports[] = {
{NULL, 0, 0}
};
static git_vector additional_transports = GIT_VECTOR_INIT;
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
......@@ -61,6 +63,14 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
definition = definition_iter;
}
git_vector_foreach(&additional_transports, i, definition_iter) {
if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
continue;
if (definition_iter->priority > priority)
definition = definition_iter;
}
#ifdef GIT_WIN32
/* On Windows, it might not be possible to discern between absolute local
* and ssh paths - first check if this is a valid local path that points
......@@ -135,6 +145,62 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url)
return 0;
}
int git_transport_register(
const char *prefix,
unsigned priority,
git_transport_cb cb,
void *param)
{
transport_definition *d;
d = git__calloc(sizeof(transport_definition), 1);
GITERR_CHECK_ALLOC(d);
d->prefix = git__strdup(prefix);
if (!d->prefix)
goto on_error;
d->priority = priority;
d->fn = cb;
d->param = param;
if (git_vector_insert(&additional_transports, d) < 0)
goto on_error;
return 0;
on_error:
git__free(d->prefix);
git__free(d);
return -1;
}
int git_transport_unregister(
const char *prefix,
unsigned priority)
{
transport_definition *d;
unsigned i;
git_vector_foreach(&additional_transports, i, d) {
if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
if (git_vector_remove(&additional_transports, i) < 0)
return -1;
git__free(d->prefix);
git__free(d);
if (!additional_transports.length)
git_vector_free(&additional_transports);
return 0;
}
}
return GIT_ENOTFOUND;
}
/* from remote.h */
int git_remote_valid_url(const char *url)
{
......
......@@ -12,8 +12,6 @@
#include "netops.h"
#include "smart.h"
static const char *prefix_http = "http://";
static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
static const char *upload_pack_service_url = "/git-upload-pack";
......@@ -59,16 +57,11 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
gitno_socket socket;
char *path;
char *host;
char *port;
char *user_from_url;
char *pass_from_url;
gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
http_authmechanism_t auth_mechanism;
unsigned connected : 1,
use_ssl : 1;
bool connected;
/* Parser structures */
http_parser parser;
......@@ -125,12 +118,12 @@ static int gen_request(
size_t content_length)
{
http_subtransport *t = OWNING_SUBTRANSPORT(s);
const char *path = t->path ? t->path : "/";
const char *path = t->connection_data.path ? t->connection_data.path : "/";
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
git_buf_printf(buf, "Host: %s\r\n", t->host);
git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
if (s->chunked || content_length > 0) {
git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
......@@ -150,9 +143,9 @@ static int gen_request(
return -1;
/* Use url-parsed basic auth if username and password are both provided */
if (!t->cred && t->user_from_url && t->pass_from_url) {
if (!t->url_cred &&
git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
if (!t->cred && t->connection_data.user && t->connection_data.pass) {
if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
t->connection_data.user, t->connection_data.pass) < 0)
return -1;
if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
}
......@@ -249,98 +242,6 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0;
}
static void free_connection_data(http_subtransport *t)
{
if (t->host) {
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->path) {
git__free(t->path);
t->path = NULL;
}
}
static int set_connection_data_from_url(
http_subtransport *t, const char *url, const char *service_suffix)
{
int error = 0;
const char *default_port = NULL;
char *original_host = NULL;
if (!git__prefixcmp(url, prefix_http)) {
url = url + strlen(prefix_http);
default_port = "80";
if (t->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
return -1;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
return -1;
}
/* preserve original host name for checking */
original_host = t->host;
t->host = NULL;
free_connection_data(t);
error = gitno_extract_url_parts(
&t->host, &t->port, &t->user_from_url, &t->pass_from_url,
url, default_port);
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
t->path = git__strndup(path, pathlen - suffixlen);
else
t->path = git__strdup(path);
/* Allow '/'-led urls, or a change of protocol */
if (original_host != NULL) {
if (strcmp(original_host, t->host) && t->location[0] != '/') {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
git__free(original_host);
}
}
return error;
}
static int on_headers_complete(http_parser *parser)
{
parser_context *ctx = (parser_context *) parser->data;
......@@ -369,7 +270,7 @@ static int on_headers_complete(http_parser *parser)
if (t->owner->cred_acquire_cb(&t->cred,
t->owner->url,
t->user_from_url,
t->connection_data.user,
allowed_types,
t->owner->cred_acquire_payload) < 0)
return PARSE_ERROR_GENERIC;
......@@ -384,17 +285,17 @@ static int on_headers_complete(http_parser *parser)
/* Check for a redirect.
* Right now we only permit a redirect to the same hostname. */
if ((parser->status_code == 301 ||
parser->status_code == 302 ||
(parser->status_code == 303 && get_verb == s->verb) ||
parser->status_code == 307) &&
t->location) {
parser->status_code == 302 ||
(parser->status_code == 303 && get_verb == s->verb) ||
parser->status_code == 307) &&
t->location) {
if (s->redirect_count >= 7) {
giterr_set(GITERR_NET, "Too many redirects");
return t->parse_error = PARSE_ERROR_GENERIC;
}
if (set_connection_data_from_url(t, t->location, s->service_url) < 0)
if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
/* Set the redirect URL on the stream. This is a transfer of
......@@ -552,7 +453,7 @@ static int http_connect(http_subtransport *t)
if (t->socket.socket)
gitno_close(&t->socket);
if (t->use_ssl) {
if (t->connection_data.use_ssl) {
int tflags;
if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
......@@ -564,7 +465,7 @@ static int http_connect(http_subtransport *t)
flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
}
if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
return -1;
t->connected = 1;
......@@ -911,10 +812,9 @@ static int http_action(
if (!stream)
return -1;
if (!t->host || !t->port || !t->path) {
if ((ret = set_connection_data_from_url(t, url, NULL)) < 0)
return ret;
}
if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
(ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
return ret;
if (http_connect(t) < 0)
return -1;
......@@ -958,7 +858,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL;
}
free_connection_data(t);
gitno_connection_data_free_ptrs(&t->connection_data);
return 0;
}
......
......@@ -434,7 +434,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
push->remote->callbacks.credentials, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error;
}
......
......@@ -13,8 +13,11 @@
#include "push.h"
#include "pack-objects.h"
#include "remote.h"
#include "util.h"
#define NETWORK_XFER_THRESHOLD (100*1024)
/* The minimal interval between progress updates (in seconds). */
#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
int git_smart__store_refs(transport_smart *t, int flushes)
{
......@@ -801,22 +804,53 @@ static int update_refs_from_report(
return 0;
}
struct push_packbuilder_payload
{
git_smart_subtransport_stream *stream;
git_packbuilder *pb;
git_push_transfer_progress cb;
void *cb_payload;
size_t last_bytes;
double last_progress_report_time;
};
static int stream_thunk(void *buf, size_t size, void *data)
{
git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
int error = 0;
struct push_packbuilder_payload *payload = data;
if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
return error;
if (payload->cb) {
double current_time = git__timer();
payload->last_bytes += size;
return s->write(s, (const char *)buf, size);
if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
payload->last_progress_report_time = current_time;
payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
}
}
return error;
}
int git_smart__push(git_transport *transport, git_push *push)
{
transport_smart *t = (transport_smart *)transport;
git_smart_subtransport_stream *s;
struct push_packbuilder_payload packbuilder_payload = {0};
git_buf pktline = GIT_BUF_INIT;
int error = -1, need_pack = 0;
push_spec *spec;
unsigned int i;
packbuilder_payload.pb = push->pb;
if (push->transfer_progress_cb) {
packbuilder_payload.cb = push->transfer_progress_cb;
packbuilder_payload.cb_payload = push->transfer_progress_cb_payload;
}
#ifdef PUSH_DEBUG
{
git_remote_head *head;
......@@ -848,12 +882,12 @@ int git_smart__push(git_transport *transport, git_push *push)
}
}
if (git_smart__get_push_stream(t, &s) < 0 ||
if (git_smart__get_push_stream(t, &packbuilder_payload.stream) < 0 ||
gen_pktline(&pktline, push) < 0 ||
s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
goto on_error;
if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload) < 0)
goto on_error;
/* If we sent nothing or the server doesn't support report-status, then
......@@ -863,6 +897,11 @@ int git_smart__push(git_transport *transport, git_push *push)
else if (parse_report(&t->buffer, push) < 0)
goto on_error;
/* If progress is being reported write the final report */
if (push->transfer_progress_cb) {
push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
}
if (push->status.length &&
update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
goto on_error;
......
......@@ -73,17 +73,12 @@ typedef struct {
typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
char *path;
char *host;
char *port;
char *user_from_url;
char *pass_from_url;
gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
int auth_mechanism;
HINTERNET session;
HINTERNET connection;
unsigned use_ssl : 1;
} winhttp_subtransport;
static int apply_basic_credential(HINTERNET request, git_cred *cred)
......@@ -155,7 +150,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */
git_buf_printf(&buf, "%s%s", t->path, s->service_url);
git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
if (git_buf_oom(&buf))
return -1;
......@@ -188,7 +183,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
NULL,
WINHTTP_NO_REFERER,
types,
t->use_ssl ? WINHTTP_FLAG_SECURE : 0);
t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
if (!s->request) {
giterr_set(GITERR_OS, "Failed to open request");
......@@ -196,7 +191,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* Set proxy if necessary */
if (git_remote__get_http_proxy(t->owner->owner, !!t->use_ssl, &proxy_url) < 0)
if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
goto on_error;
if (proxy_url) {
......@@ -285,7 +280,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* If requested, disable certificate validation */
if (t->use_ssl) {
if (t->connection_data.use_ssl) {
int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
......@@ -308,9 +303,9 @@ static int winhttp_stream_connect(winhttp_stream *s)
/* If no other credentials have been applied and the URL has username and
* password, use those */
if (!t->cred && t->user_from_url && t->pass_from_url) {
if (!t->cred && t->connection_data.user && t->connection_data.pass) {
if (!t->url_cred &&
git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
goto on_error;
if (apply_basic_credential(s->request, t->url_cred) < 0)
goto on_error;
......@@ -392,98 +387,6 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0;
}
static void free_connection_data(winhttp_subtransport *t)
{
if (t->host) {
git__free(t->host);
t->host = NULL;
}
if (t->port) {
git__free(t->port);
t->port = NULL;
}
if (t->user_from_url) {
git__free(t->user_from_url);
t->user_from_url = NULL;
}
if (t->pass_from_url) {
git__free(t->pass_from_url);
t->pass_from_url = NULL;
}
if (t->path) {
git__free(t->path);
t->path = NULL;
}
}
static int set_connection_data_from_url(
winhttp_subtransport *t, const char *url, const char *service_suffix)
{
int error = 0;
const char *default_port = NULL;
char *original_host = NULL;
const char *original_url = url;
if (!git__prefixcmp(url, prefix_http)) {
url += strlen(prefix_http);
default_port = "80";
if (t->use_ssl) {
giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
return -1;
}
}
if (!git__prefixcmp(url, prefix_https)) {
url += strlen(prefix_https);
default_port = "443";
t->use_ssl = 1;
}
if (!default_port) {
giterr_set(GITERR_NET, "Unrecognized URL prefix");
return -1;
}
/* preserve original host name for checking */
original_host = t->host;
t->host = NULL;
free_connection_data(t);
error = gitno_extract_url_parts(
&t->host, &t->port, &t->user_from_url, &t->pass_from_url,
url, default_port);
if (!error) {
const char *path = strchr(url, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
if (suffixlen &&
!memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
t->path = git__strndup(path, pathlen - suffixlen);
else
t->path = git__strdup(path);
/* Allow '/'-led urls, or a change of protocol */
if (original_host != NULL) {
if (strcmp(original_host, t->host) && original_url[0] != '/') {
giterr_set(GITERR_NET, "Cross host redirect not allowed");
error = -1;
}
git__free(original_host);
}
}
return error;
}
static int winhttp_connect(
winhttp_subtransport *t,
const char *url)
......@@ -494,11 +397,11 @@ static int winhttp_connect(
const char *default_port = "80";
/* Prepare port */
if (git__strtol32(&port, t->port, NULL, 10) < 0)
if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
return -1;
/* Prepare host */
git_win32_path_from_c(host, t->host);
git_win32_path_from_c(host, t->connection_data.host);
/* Establish session */
t->session = WinHttpOpen(
......@@ -699,7 +602,8 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */
set_connection_data_from_url(t, location8, s->service_url);
if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0)
return -1;
winhttp_connect(t, location8);
}
......@@ -718,7 +622,8 @@ replay:
if (allowed_types &&
(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0)
if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types,
t->owner->cred_acquire_payload) < 0)
return -1;
assert(t->cred);
......@@ -1101,10 +1006,10 @@ static int winhttp_action(
winhttp_stream *s;
int ret = -1;
if (!t->connection &&
(set_connection_data_from_url(t, url, NULL) < 0 ||
winhttp_connect(t, url) < 0))
return -1;
if (!t->connection)
if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 ||
winhttp_connect(t, url) < 0)
return -1;
if (winhttp_stream_alloc(t, &s) < 0)
return -1;
......@@ -1145,7 +1050,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0;
free_connection_data(t);
gitno_connection_data_free_ptrs(&t->connection_data);
if (t->cred) {
t->cred->free(t->cred);
......
......@@ -353,4 +353,65 @@ GIT_INLINE(void) git__memzero(void *data, size_t size)
#endif
}
#ifdef GIT_WIN32
GIT_INLINE(double) git__timer(void)
{
/* We need the initial tick count to detect if the tick
* count has rolled over. */
static DWORD initial_tick_count = 0;
/* GetTickCount returns the number of milliseconds that have
* elapsed since the system was started. */
DWORD count = GetTickCount();
if(initial_tick_count == 0) {
initial_tick_count = count;
} else if (count < initial_tick_count) {
/* The tick count has rolled over - adjust for it. */
count = (0xFFFFFFFF - initial_tick_count) + count;
}
return (double) count / (double) 1000;
}
#elif __APPLE__
#include <mach/mach_time.h>
GIT_INLINE(double) git__timer(void)
{
uint64_t time = mach_absolute_time();
static double scaling_factor = 0;
if (scaling_factor == 0) {
mach_timebase_info_data_t info;
(void)mach_timebase_info(&info);
scaling_factor = (double)info.numer / (double)info.denom;
}
return (double)time * scaling_factor / 1.0E-9;
}
#else
#include <sys/time.h>
GIT_INLINE(double) git__timer(void)
{
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9;
} else {
/* Fall back to using gettimeofday */
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6;
}
}
#endif
#endif /* INCLUDE_util_h__ */
......@@ -440,7 +440,7 @@ void clar__assert_equal_file(
int ignore_cr,
const char *path,
const char *file,
size_t line)
int line)
{
char buf[4000];
ssize_t bytes, total_bytes = 0;
......
......@@ -44,7 +44,7 @@ GIT_INLINE(void) clar__assert_in_range(
}
#define cl_assert_equal_sz(sz1,sz2) do { \
size_t __sz1 = (sz1), __sz2 = (sz2); \
size_t __sz1 = (size_t)(sz1), __sz2 = (size_t)(sz2); \
clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
} while (0)
......@@ -52,10 +52,10 @@ GIT_INLINE(void) clar__assert_in_range(
clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
#define cl_assert_equal_file(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,__LINE__)
clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,(int)__LINE__)
#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,__LINE__)
clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,(int)__LINE__)
void clar__assert_equal_file(
const char *expected_data,
......@@ -63,7 +63,7 @@ void clar__assert_equal_file(
int ignore_cr,
const char *path,
const char *file,
size_t line);
int line);
/*
* Some utility macros for building long strings
......
......@@ -10,12 +10,14 @@ static git_repository *g_repo_cloned;
void test_clone_empty__initialize(void)
{
git_repository *sandbox = cl_git_sandbox_init("empty_bare.git");
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.remote_callbacks = dummy_callbacks;
}
void test_clone_empty__cleanup(void)
......
......@@ -15,6 +15,7 @@ static git_remote* g_remote;
void test_clone_nonetwork__initialize(void)
{
git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
g_repo = NULL;
......@@ -22,6 +23,7 @@ void test_clone_nonetwork__initialize(void)
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.checkout_opts = dummy_opts;
g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
g_options.remote_callbacks = dummy_callbacks;
}
void test_clone_nonetwork__cleanup(void)
......@@ -124,84 +126,17 @@ void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(vo
void test_clone_nonetwork__custom_origin_name(void)
{
g_options.remote_name = "my_origin";
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
g_options.remote_name = "my_origin";
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
}
void test_clone_nonetwork__custom_push_url(void)
void test_clone_nonetwork__defaults(void)
{
const char *url = "http://example.com";
g_options.pushurl = url;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
cl_assert_equal_s(url, git_remote_pushurl(g_remote));
}
void test_clone_nonetwork__custom_fetch_spec(void)
{
const git_refspec *actual_fs;
const char *spec = "+refs/heads/master:refs/heads/foo";
g_options.fetch_spec = spec;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
actual_fs = git_remote_get_refspec(g_remote, 0);
cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs));
cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs));
cl_git_pass(git_reference_lookup(&g_ref, g_repo, "refs/heads/foo"));
}
void test_clone_nonetwork__custom_push_spec(void)
{
const git_refspec *actual_fs;
const char *spec = "+refs/heads/master:refs/heads/foo";
g_options.push_spec = spec;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL));
cl_assert(g_repo);
cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
actual_fs = git_remote_get_refspec(g_remote, git_remote_refspec_count(g_remote) - 1);
cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs));
cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs));
}
void test_clone_nonetwork__custom_autotag(void)
{
git_remote *origin;
git_strarray tags = {0};
g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_tag_list(&tags, g_repo));
cl_assert_equal_sz(0, tags.count);
cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_NONE, origin->download_tags);
git_strarray_free(&tags);
git_remote_free(origin);
}
void test_clone_nonetwork__custom_autotag_tags_all(void)
{
git_strarray tags = {0};
git_remote *origin;
g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_ALL, origin->download_tags);
git_strarray_free(&tags);
git_remote_free(origin);
}
void test_clone_nonetwork__cope_with_already_existing_directory(void)
......
......@@ -7,77 +7,77 @@
static git_repository *g_repo;
void test_commit_parse__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo");
g_repo = cl_git_sandbox_init("testrepo");
}
void test_commit_parse__cleanup(void)
{
cl_git_sandbox_cleanup();
cl_git_sandbox_cleanup();
}
// Header parsing
typedef struct {
const char *line;
const char *header;
const char *line;
const char *header;
} parse_test_case;
static parse_test_case passing_header_cases[] = {
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
{ "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
{ "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
{ "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
{ "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
{ "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
{ NULL, NULL }
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
{ "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
{ "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
{ "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
{ "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
{ "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
{ NULL, NULL }
};
static parse_test_case failing_header_cases[] = {
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
{ "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
{ "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
{ "", "tree " },
{ "", "" },
{ NULL, NULL }
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
{ "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
{ "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
{ "", "tree " },
{ "", "" },
{ NULL, NULL }
};
void test_commit_parse__header(void)
{
git_oid oid;
git_oid oid;
parse_test_case *testcase;
for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
{
const char *line = testcase->line;
const char *line_end = line + strlen(line);
parse_test_case *testcase;
for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
{
const char *line = testcase->line;
const char *line_end = line + strlen(line);
cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
cl_assert(line == line_end);
}
cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
cl_assert(line == line_end);
}
for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
{
const char *line = testcase->line;
const char *line_end = line + strlen(line);
for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
{
const char *line = testcase->line;
const char *line_end = line + strlen(line);
cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
}
cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
}
}
// Signature parsing
typedef struct {
const char *string;
const char *header;
const char *name;
const char *email;
git_time_t time;
int offset;
const char *string;
const char *header;
const char *name;
const char *email;
git_time_t time;
int offset;
} passing_signature_test_case;
passing_signature_test_case passing_signature_cases[] = {
......@@ -122,12 +122,12 @@ passing_signature_test_case passing_signature_cases[] = {
{"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0},
{"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0},
{NULL,NULL,NULL,NULL,0,0}
{NULL,NULL,NULL,NULL,0,0}
};
typedef struct {
const char *string;
const char *header;
const char *string;
const char *header;
} failing_signature_test_case;
failing_signature_test_case failing_signature_cases[] = {
......@@ -143,31 +143,31 @@ failing_signature_test_case failing_signature_cases[] = {
void test_commit_parse__signature(void)
{
passing_signature_test_case *passcase;
failing_signature_test_case *failcase;
for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
{
const char *str = passcase->string;
size_t len = strlen(passcase->string);
struct git_signature person = {0};
cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
cl_assert_equal_s(passcase->name, person.name);
cl_assert_equal_s(passcase->email, person.email);
cl_assert_equal_i((int)passcase->time, (int)person.when.time);
cl_assert_equal_i(passcase->offset, person.when.offset);
git__free(person.name); git__free(person.email);
}
for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
{
const char *str = failcase->string;
size_t len = strlen(failcase->string);
git_signature person = {0};
cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
git__free(person.name); git__free(person.email);
}
passing_signature_test_case *passcase;
failing_signature_test_case *failcase;
for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
{
const char *str = passcase->string;
size_t len = strlen(passcase->string);
struct git_signature person = {0};
cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
cl_assert_equal_s(passcase->name, person.name);
cl_assert_equal_s(passcase->email, person.email);
cl_assert_equal_i((int)passcase->time, (int)person.when.time);
cl_assert_equal_i(passcase->offset, person.when.offset);
git__free(person.name); git__free(person.email);
}
for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
{
const char *str = failcase->string;
size_t len = strlen(failcase->string);
git_signature person = {0};
cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
git__free(person.name); git__free(person.email);
}
}
......@@ -312,17 +312,17 @@ void test_commit_parse__entire_commit(void)
// query the details on a parsed commit
void test_commit_parse__details0(void) {
static const char *commit_ids[] = {
"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
};
static const char *commit_ids[] = {
"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
};
const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
unsigned int i;
unsigned int i;
for (i = 0; i < commit_count; ++i) {
git_oid id;
......@@ -349,7 +349,6 @@ void test_commit_parse__details0(void) {
cl_assert_equal_s("Scott Chacon", committer->name);
cl_assert_equal_s("schacon@gmail.com", committer->email);
cl_assert(message != NULL);
cl_assert(strchr(message, '\n') != NULL);
cl_assert(commit_time > 0);
cl_assert(parents <= 2);
for (p = 0;p < parents;p++) {
......@@ -382,11 +381,33 @@ committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
This commit has a few LF at the start of the commit message";
const char *message =
"This commit has a few LF at the start of the commit message";
const char *raw_message =
"\n\
\n\
This commit has a few LF at the start of the commit message";
cl_git_pass(parse_commit(&commit, buffer));
cl_assert_equal_s(message, git_commit_message(commit));
cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
git_commit__free(commit);
}
void test_commit_parse__only_lf(void)
{
git_commit *commit;
const char *buffer =
"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
parent e90810b8df3e80c413d903f631643c716887138d\n\
author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\
\n\
\n";
const char *message = "";
const char *raw_message = "\n\n";
cl_git_pass(parse_commit(&commit, buffer));
cl_assert_equal_s(message, git_commit_message(commit));
cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
git_commit__free(commit);
}
......@@ -164,6 +164,13 @@ void test_config_read__empty_files(void)
git_config_free(cfg);
}
void test_config_read__symbol_headers(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
git_config_free(cfg);
}
void test_config_read__header_in_last_line(void)
{
git_config *cfg;
......@@ -524,6 +531,28 @@ void test_config_read__corrupt_header(void)
git_config_free(cfg);
}
void test_config_read__corrupt_header2(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[unclosed \"bracket\"\n lib = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__corrupt_header3(void)
{
git_config *cfg;
cl_set_cleanup(&clean_test_config, NULL);
cl_git_mkfile("./testconfig", "[unclosed \"slash\\\"]\n lib = git2\n");
cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
git_config_free(cfg);
}
void test_config_read__override_variable(void)
{
git_config *cfg;
......
......@@ -25,13 +25,18 @@ void test_network_fetchlocal__complete(void)
git_strarray refnames = {0};
const char *url = cl_git_fixture_url("testrepo.git");
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
callbacks.transfer_progress = transfer_cb;
callbacks.payload = &callcount;
cl_set_cleanup(&cleanup_local_repo, "foo");
cl_git_pass(git_repository_init(&repo, "foo", true));
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
git_remote_set_callbacks(origin, &callbacks);
cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_download(origin, transfer_cb, &callcount));
cl_git_pass(git_remote_download(origin));
cl_git_pass(git_remote_update_tips(origin));
cl_git_pass(git_reference_list(&refnames, repo));
......@@ -56,6 +61,10 @@ void test_network_fetchlocal__partial(void)
int callcount = 0;
git_strarray refnames = {0};
const char *url;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
callbacks.transfer_progress = transfer_cb;
callbacks.payload = &callcount;
cl_set_cleanup(&cleanup_sandbox, NULL);
cl_git_pass(git_reference_list(&refnames, repo));
......@@ -63,8 +72,9 @@ void test_network_fetchlocal__partial(void)
url = cl_git_fixture_url("testrepo.git");
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
git_remote_set_callbacks(origin, &callbacks);
cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_download(origin, transfer_cb, &callcount));
cl_git_pass(git_remote_download(origin));
cl_git_pass(git_remote_update_tips(origin));
git_strarray_free(&refnames);
......
......@@ -123,7 +123,7 @@ void test_network_remote_local__shorthand_fetch_refspec0(void)
cl_git_pass(git_remote_add_fetch(remote, refspec));
cl_git_pass(git_remote_add_fetch(remote, refspec2));
cl_git_pass(git_remote_download(remote, NULL, NULL));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
......@@ -145,7 +145,7 @@ void test_network_remote_local__shorthand_fetch_refspec1(void)
cl_git_pass(git_remote_add_fetch(remote, refspec));
cl_git_pass(git_remote_add_fetch(remote, refspec2));
cl_git_pass(git_remote_download(remote, NULL, NULL));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
......@@ -160,7 +160,7 @@ void test_network_remote_local__tagopt(void)
connect_to_local_repository(cl_fixture("testrepo.git"));
git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
cl_git_pass(git_remote_download(remote, NULL, NULL));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
......@@ -179,7 +179,7 @@ void test_network_remote_local__push_to_bare_remote(void)
/* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git"));
cl_git_pass(git_remote_add_fetch(remote, "master:master"));
cl_git_pass(git_remote_download(remote, NULL, NULL));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote);
......@@ -215,7 +215,7 @@ void test_network_remote_local__push_to_non_bare_remote(void)
/* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git"));
cl_git_pass(git_remote_add_fetch(remote, "master:master"));
cl_git_pass(git_remote_download(remote, NULL, NULL));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote);
......
......@@ -2,10 +2,12 @@
#include "netops.h"
char *host, *port, *user, *pass;
gitno_connection_data conndata;
void test_network_urlparse__initialize(void)
{
host = port = user = pass = NULL;
memset(&conndata, 0, sizeof(conndata));
}
void test_network_urlparse__cleanup(void)
......@@ -15,6 +17,8 @@ void test_network_urlparse__cleanup(void)
FREE_AND_NULL(port);
FREE_AND_NULL(user);
FREE_AND_NULL(pass);
gitno_connection_data_free_ptrs(&conndata);
}
void test_network_urlparse__trivial(void)
......@@ -80,3 +84,80 @@ void test_network_urlparse__user_pass_port(void)
cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass");
}
void test_network_urlparse__connection_data_http(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"http://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, false);
}
void test_network_urlparse__connection_data_ssl(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://example.com/foo/bar/baz", "bar/baz"));
cl_assert_equal_s(conndata.host, "example.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/foo/");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, true);
}
void test_network_urlparse__connection_data_cross_host_redirect(void)
{
conndata.host = git__strdup("bar.com");
cl_git_fail_with(gitno_connection_data_from_url(&conndata,
"https://foo.com/bar/baz", NULL),
-1);
}
void test_network_urlparse__connection_data_http_downgrade(void)
{
conndata.use_ssl = true;
cl_git_fail_with(gitno_connection_data_from_url(&conndata,
"http://foo.com/bar/baz", NULL),
-1);
}
void test_network_urlparse__connection_data_relative_redirect(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"http://foo.com/bar/baz/biff", NULL));
cl_git_pass(gitno_connection_data_from_url(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "80");
cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, false);
}
void test_network_urlparse__connection_data_relative_redirect_ssl(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://foo.com/bar/baz/biff", NULL));
cl_git_pass(gitno_connection_data_from_url(&conndata,
"/zap/baz/biff?bam", NULL));
cl_assert_equal_s(conndata.host, "foo.com");
cl_assert_equal_s(conndata.port, "443");
cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
cl_assert_equal_p(conndata.user, NULL);
cl_assert_equal_p(conndata.pass, NULL);
cl_assert_equal_i(conndata.use_ssl, true);
}
/* Run this under valgrind */
void test_network_urlparse__connection_data_cleanup(void)
{
cl_git_pass(gitno_connection_data_from_url(&conndata,
"http://foo.com/bar/baz/biff", "baz/biff"));
cl_git_pass(gitno_connection_data_from_url(&conndata,
"https://foo.com/bar/baz/biff", "baz/biff"));
}
......@@ -18,6 +18,7 @@ static git_clone_options g_options;
void test_online_clone__initialize(void)
{
git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
g_repo = NULL;
......@@ -25,6 +26,7 @@ void test_online_clone__initialize(void)
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.checkout_opts = dummy_opts;
g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
g_options.remote_callbacks = dummy_callbacks;
}
void test_online_clone__cleanup(void)
......@@ -103,8 +105,8 @@ void test_online_clone__can_checkout_a_cloned_repo(void)
g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
g_options.checkout_opts.progress_cb = &checkout_progress;
g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
g_options.fetch_progress_cb = &fetch_progress;
g_options.fetch_progress_payload = &fetch_progress_cb_was_called;
g_options.remote_callbacks.transfer_progress = &fetch_progress;
g_options.remote_callbacks.payload = &fetch_progress_cb_was_called;
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
......@@ -122,6 +124,45 @@ void test_online_clone__can_checkout_a_cloned_repo(void)
git_buf_free(&path);
}
void test_online_clone__clone_into(void)
{
git_buf path = GIT_BUF_INIT;
git_remote *remote;
git_reference *head;
git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
bool checkout_progress_cb_was_called = false,
fetch_progress_cb_was_called = false;
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
checkout_opts.progress_cb = &checkout_progress;
checkout_opts.progress_payload = &checkout_progress_cb_was_called;
cl_git_pass(git_repository_init(&g_repo, "./foo", false));
cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
callbacks.transfer_progress = &fetch_progress;
callbacks.payload = &fetch_progress_cb_was_called;
git_remote_set_callbacks(remote, &callbacks);
cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL));
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
cl_assert_equal_i(true, checkout_progress_cb_was_called);
cl_assert_equal_i(true, fetch_progress_cb_was_called);
git_remote_free(remote);
git_reference_free(head);
git_buf_free(&path);
}
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
{
int *callcount = (int*)payload;
......@@ -132,12 +173,10 @@ static int update_tips(const char *refname, const git_oid *a, const git_oid *b,
void test_online_clone__custom_remote_callbacks(void)
{
git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT;
int callcount = 0;
g_options.remote_callbacks = &remote_callbacks;
remote_callbacks.update_tips = update_tips;
remote_callbacks.payload = &callcount;
g_options.remote_callbacks.update_tips = update_tips;
g_options.remote_callbacks.payload = &callcount;
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
cl_assert(callcount > 0);
......@@ -154,8 +193,8 @@ void test_online_clone__credentials(void)
if (!remote_url) return;
g_options.cred_acquire_cb = git_cred_userpass;
g_options.cred_acquire_payload = &user_pass;
g_options.remote_callbacks.credentials = git_cred_userpass;
g_options.remote_callbacks.payload = &user_pass;
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
......@@ -168,8 +207,8 @@ void test_online_clone__bitbucket_style(void)
"libgit2", "libgit2"
};
g_options.cred_acquire_cb = git_cred_userpass;
g_options.cred_acquire_payload = &user_pass;
g_options.remote_callbacks.credentials = git_cred_userpass;
g_options.remote_callbacks.payload = &user_pass;
cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL;
......@@ -199,6 +238,13 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload)
void test_online_clone__can_cancel(void)
{
g_options.fetch_progress_cb = cancel_at_half;
g_options.remote_callbacks.transfer_progress = cancel_at_half;
cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER);
}
......@@ -38,14 +38,16 @@ static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
size_t bytes_received = 0;
callbacks.transfer_progress = progress;
callbacks.update_tips = update_tips;
callbacks.payload = &bytes_received;
counter = 0;
cl_git_pass(git_remote_create(&remote, _repo, "test", url));
git_remote_set_callbacks(remote, &callbacks);
git_remote_set_autotag(remote, flag);
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_download(remote, progress, &bytes_received));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote);
cl_assert_equal_i(counter, n);
......@@ -93,6 +95,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
git_repository *_repository;
bool invoked = false;
git_remote *remote;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
opts.bare = true;
......@@ -107,7 +110,10 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
cl_assert_equal_i(false, invoked);
cl_git_pass(git_remote_download(remote, &transferProgressCallback, &invoked));
callbacks.transfer_progress = &transferProgressCallback;
callbacks.payload = &invoked;
git_remote_set_callbacks(remote, &callbacks);
cl_git_pass(git_remote_download(remote));
cl_assert_equal_i(false, invoked);
......@@ -131,11 +137,17 @@ void test_online_fetch__can_cancel(void)
{
git_remote *remote;
size_t bytes_received = 0;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
cl_git_pass(git_remote_create(&remote, _repo, "test",
"http://github.com/libgit2/TestGitRepository.git"));
callbacks.transfer_progress = cancel_at_half;
callbacks.payload = &bytes_received;
git_remote_set_callbacks(remote, &callbacks);
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
cl_git_fail_with(git_remote_download(remote, cancel_at_half, &bytes_received), GIT_EUSER);
cl_git_fail_with(git_remote_download(remote), GIT_EUSER);
git_remote_disconnect(remote);
git_remote_free(remote);
}
......
......@@ -12,10 +12,12 @@ static git_clone_options g_options;
void test_online_fetchhead__initialize(void)
{
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.remote_callbacks = dummy_callbacks;
}
void test_online_fetchhead__cleanup(void)
......@@ -48,7 +50,7 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet
}
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_download(remote, NULL, NULL));
cl_git_pass(git_remote_download(remote));
cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote);
git_remote_free(remote);
......
......@@ -17,6 +17,8 @@ static char *_remote_url;
static char *_remote_user;
static char *_remote_pass;
static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
static git_remote *_remote;
static bool _cred_acquire_called;
static record_callbacks_data _record_cbs_data = {{ 0 }};
......@@ -294,7 +296,6 @@ void test_online_push__initialize(void)
if (_remote_url) {
cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
git_remote_set_cred_acquire_cb(_remote, cred_acquire_cb, &_cred_acquire_called);
record_callbacks_data_clear(&_record_cbs_data);
git_remote_set_callbacks(_remote, &_record_cbs);
......@@ -326,7 +327,7 @@ void test_online_push__initialize(void)
/* Now that we've deleted everything, fetch from the remote */
cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_download(_remote, NULL, NULL));
cl_git_pass(git_remote_download(_remote));
cl_git_pass(git_remote_update_tips(_remote));
git_remote_disconnect(_remote);
} else
......@@ -348,6 +349,18 @@ void test_online_push__cleanup(void)
cl_git_sandbox_cleanup();
}
static void push_pack_progress_cb(int stage, unsigned int current, unsigned int total, void* payload)
{
int *was_called = (int *) payload;
*was_called = 1;
}
static void push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload)
{
int *was_called = (int *) payload;
*was_called = 1;
}
/**
* Calls push and relists refs on remote to verify success.
*
......@@ -356,15 +369,17 @@ void test_online_push__cleanup(void)
* @param expected_refs expected remote refs after push
* @param expected_refs_len length of expected_refs
* @param expected_ret expected return value from git_push_finish()
* @param check_progress_cb Check that the push progress callbacks are called
*/
static void do_push(const char *refspecs[], size_t refspecs_len,
push_status expected_statuses[], size_t expected_statuses_len,
expected_ref expected_refs[], size_t expected_refs_len, int expected_ret)
expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb)
{
git_push *push;
git_push_options opts = GIT_PUSH_OPTIONS_INIT;
size_t i;
int ret;
int pack_progress_called = 0, transfer_progress_called = 0;
if (_remote) {
/* Auto-detect the number of threads to use */
......@@ -375,6 +390,9 @@ static void do_push(const char *refspecs[], size_t refspecs_len,
cl_git_pass(git_push_new(&push, _remote));
cl_git_pass(git_push_set_options(push, &opts));
if (check_progress_cb)
cl_git_pass(git_push_set_callbacks(push, push_pack_progress_cb, &pack_progress_called, push_transfer_progress_cb, &transfer_progress_called));
for (i = 0; i < refspecs_len; i++)
cl_git_pass(git_push_add_refspec(push, refspecs[i]));
......@@ -387,6 +405,11 @@ static void do_push(const char *refspecs[], size_t refspecs_len,
cl_assert_equal_i(1, git_push_unpack_ok(push));
}
if (check_progress_cb) {
cl_assert_equal_i(1, pack_progress_called);
cl_assert_equal_i(1, transfer_progress_called);
}
do_verify_push_status(push, expected_statuses, expected_statuses_len);
cl_assert_equal_i(expected_ret, ret);
......@@ -405,7 +428,7 @@ static void do_push(const char *refspecs[], size_t refspecs_len,
/* Call push_finish() without ever calling git_push_add_refspec() */
void test_online_push__noop(void)
{
do_push(NULL, 0, NULL, 0, NULL, 0, 0);
do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0);
}
void test_online_push__b1(void)
......@@ -415,7 +438,7 @@ void test_online_push__b1(void)
expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__b2(void)
......@@ -425,7 +448,7 @@ void test_online_push__b2(void)
expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__b3(void)
......@@ -435,7 +458,7 @@ void test_online_push__b3(void)
expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__b4(void)
......@@ -445,7 +468,7 @@ void test_online_push__b4(void)
expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__b5(void)
......@@ -455,7 +478,7 @@ void test_online_push__b5(void)
expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__multi(void)
......@@ -483,7 +506,7 @@ void test_online_push__multi(void)
};
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__implicit_tgt(void)
......@@ -501,10 +524,10 @@ void test_online_push__implicit_tgt(void)
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
exp_refs1, ARRAY_SIZE(exp_refs1), 0);
exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
do_push(specs2, ARRAY_SIZE(specs2),
exp_stats2, ARRAY_SIZE(exp_stats2),
exp_refs2, ARRAY_SIZE(exp_refs2), 0);
exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0);
}
void test_online_push__fast_fwd(void)
......@@ -526,19 +549,19 @@ void test_online_push__fast_fwd(void)
do_push(specs_init, ARRAY_SIZE(specs_init),
exp_stats_init, ARRAY_SIZE(exp_stats_init),
exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1);
do_push(specs_ff, ARRAY_SIZE(specs_ff),
exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0);
do_push(specs_reset, ARRAY_SIZE(specs_reset),
exp_stats_init, ARRAY_SIZE(exp_stats_init),
exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0);
do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0);
}
void test_online_push__tag_commit(void)
......@@ -548,7 +571,7 @@ void test_online_push__tag_commit(void)
expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__tag_tree(void)
......@@ -558,7 +581,7 @@ void test_online_push__tag_tree(void)
expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__tag_blob(void)
......@@ -568,7 +591,7 @@ void test_online_push__tag_blob(void)
expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__tag_lightweight(void)
......@@ -578,7 +601,7 @@ void test_online_push__tag_lightweight(void)
expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
}
void test_online_push__tag_to_tag(void)
......@@ -588,7 +611,7 @@ void test_online_push__tag_to_tag(void)
expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } };
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 0);
}
void test_online_push__force(void)
......@@ -605,16 +628,16 @@ void test_online_push__force(void)
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
exp_refs1, ARRAY_SIZE(exp_refs1), 0);
exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
do_push(specs2, ARRAY_SIZE(specs2),
NULL, 0,
exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD);
exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0);
/* Non-fast-forward update with force should pass. */
do_push(specs2_force, ARRAY_SIZE(specs2_force),
exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0);
exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1);
}
void test_online_push__delete(void)
......@@ -645,7 +668,7 @@ void test_online_push__delete(void)
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
exp_refs1, ARRAY_SIZE(exp_refs1), 0);
exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
/* When deleting a non-existent branch, the git client sends zero for both
* the old and new commit id. This should succeed on the server with the
......@@ -655,23 +678,23 @@ void test_online_push__delete(void)
*/
do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
exp_stats_fake, 1,
exp_refs1, ARRAY_SIZE(exp_refs1), 0);
exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
exp_stats_fake, 1,
exp_refs1, ARRAY_SIZE(exp_refs1), 0);
exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
/* Delete one of the pushed branches. */
do_push(specs_delete, ARRAY_SIZE(specs_delete),
exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0);
/* Re-push branches and retry delete with force. */
do_push(specs1, ARRAY_SIZE(specs1),
exp_stats1, ARRAY_SIZE(exp_stats1),
exp_refs1, ARRAY_SIZE(exp_refs1), 0);
exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0);
}
void test_online_push__bad_refspecs(void)
......@@ -703,11 +726,11 @@ void test_online_push__expressions(void)
/* TODO: Find a more precise way of checking errors than a exit code of -1. */
do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
NULL, 0,
NULL, 0, -1);
NULL, 0, -1, 0);
do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
NULL, 0, 0);
NULL, 0, 0, 1);
}
void test_online_push__notes(void)
......@@ -727,7 +750,7 @@ void test_online_push__notes(void)
do_push(specs, ARRAY_SIZE(specs),
exp_stats, ARRAY_SIZE(exp_stats),
exp_refs, ARRAY_SIZE(exp_refs), 0);
exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
git_signature_free(signature);
}
......@@ -12,7 +12,7 @@ extern const git_oid OID_ZERO;
* @param data pointer to a record_callbacks_data instance
*/
#define RECORD_CALLBACKS_INIT(data) \
{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, record_update_tips_cb, data }
{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, record_update_tips_cb, data }
typedef struct {
char *name;
......
[valid "[subsection]"]
something = a
; we don't allow anything after closing "
[sec "[subsec]/child"]
parent = grand
[sec2 "[subsec2]/child2"]
type = dvcs
[sec3 "escape\"quote"]
vcs = git
[sec4 "escaping\\slash"]
lib = git2
......@@ -113,33 +113,15 @@ $ git status --short
cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
}
static void assert_status(
const char *path,
int status_flags)
{
unsigned int status;
int error;
error = git_status_file(&status, repo, path);
if (status_flags < 0) {
cl_assert_equal_i(status_flags, error);
return;
}
cl_assert_equal_i(0, error);
cl_assert_equal_i((unsigned int)status_flags, status);
}
void test_stash_save__can_keep_index(void)
{
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
assert_status("what", GIT_STATUS_INDEX_MODIFIED);
assert_status("how", GIT_STATUS_INDEX_MODIFIED);
assert_status("who", GIT_STATUS_CURRENT);
assert_status("when", GIT_STATUS_WT_NEW);
assert_status("just.ignore", GIT_STATUS_IGNORED);
assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
assert_status(repo, "who", GIT_STATUS_CURRENT);
assert_status(repo, "when", GIT_STATUS_WT_NEW);
assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
}
static void assert_commit_message_contains(const char *revision, const char *fragment)
......@@ -308,25 +290,25 @@ void test_stash_save__can_stage_normal_then_stage_untracked(void)
* 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
*/
assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
assert_status("how", GIT_STATUS_INDEX_MODIFIED);
assert_status("who", GIT_STATUS_WT_MODIFIED);
assert_status("when", GIT_STATUS_WT_NEW);
assert_status("just.ignore", GIT_STATUS_IGNORED);
assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
assert_status(repo, "when", GIT_STATUS_WT_NEW);
assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
assert_status("what", GIT_STATUS_CURRENT);
assert_status("how", GIT_STATUS_CURRENT);
assert_status("who", GIT_STATUS_CURRENT);
assert_status("when", GIT_STATUS_WT_NEW);
assert_status("just.ignore", GIT_STATUS_IGNORED);
assert_status(repo, "what", GIT_STATUS_CURRENT);
assert_status(repo, "how", GIT_STATUS_CURRENT);
assert_status(repo, "who", GIT_STATUS_CURRENT);
assert_status(repo, "when", GIT_STATUS_WT_NEW);
assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
assert_status("what", GIT_STATUS_CURRENT);
assert_status("how", GIT_STATUS_CURRENT);
assert_status("who", GIT_STATUS_CURRENT);
assert_status("when", GIT_ENOTFOUND);
assert_status("just.ignore", GIT_STATUS_IGNORED);
assert_status(repo, "what", GIT_STATUS_CURRENT);
assert_status(repo, "how", GIT_STATUS_CURRENT);
assert_status(repo, "who", GIT_STATUS_CURRENT);
assert_status(repo, "when", GIT_ENOTFOUND);
assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
......@@ -360,11 +342,11 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_
{
cl_git_pass(p_unlink("stash/when"));
assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
assert_status("how", GIT_STATUS_INDEX_MODIFIED);
assert_status("who", GIT_STATUS_WT_MODIFIED);
assert_status("when", GIT_ENOTFOUND);
assert_status("just.ignore", GIT_STATUS_IGNORED);
assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
assert_status(repo, "when", GIT_ENOTFOUND);
assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
......
......@@ -35,3 +35,22 @@ void setup_stash(git_repository *repo, git_signature *signature)
git_index_free(index);
}
void assert_status(
git_repository *repo,
const char *path,
int status_flags)
{
unsigned int status;
int error;
error = git_status_file(&status, repo, path);
if (status_flags < 0) {
cl_assert_equal_i(status_flags, error);
return;
}
cl_assert_equal_i(0, error);
cl_assert_equal_i((unsigned int)status_flags, status);
}
void setup_stash(
git_repository *repo,
git_signature *signature);
void assert_status(
git_repository *repo,
const char *path,
int status_flags);
#include "clar_libgit2.h"
#include "stash_helpers.h"
#include "../submodule/submodule_helpers.h"
static git_repository *repo;
static git_signature *signature;
static git_oid stash_tip_oid;
static git_submodule *sm;
void test_stash_submodules__initialize(void)
{
cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
repo = setup_fixture_submodules();
cl_git_pass(git_submodule_lookup(&sm, repo, "testrepo"));
}
void test_stash_submodules__cleanup(void)
{
git_signature_free(signature);
signature = NULL;
}
void test_stash_submodules__does_not_stash_modified_submodules(void)
{
static git_index *smindex;
static git_repository *smrepo;
assert_status(repo, "modified", GIT_STATUS_WT_MODIFIED);
/* modify file in submodule */
cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
/* add file to index in submodule */
cl_git_pass(git_submodule_open(&smrepo, sm));
cl_git_pass(git_repository_index(&smindex, smrepo));
cl_git_pass(git_index_add_bypath(smindex, "README"));
/* commit changed index of submodule */
cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
assert_status(repo, "modified", GIT_STATUS_CURRENT);
git_index_free(smindex);
git_repository_free(smrepo);
}
void test_stash_submodules__stash_is_empty_with_modified_submodules(void)
{
static git_index *smindex;
static git_repository *smrepo;
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
assert_status(repo, "modified", GIT_STATUS_CURRENT);
/* modify file in submodule */
cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
/* add file to index in submodule */
cl_git_pass(git_submodule_open(&smrepo, sm));
cl_git_pass(git_repository_index(&smindex, smrepo));
cl_git_pass(git_index_add_bypath(smindex, "README"));
/* commit changed index of submodule */
cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
cl_git_fail_with(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT), GIT_ENOTFOUND);
git_index_free(smindex);
git_repository_free(smrepo);
}
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