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) ...@@ -56,6 +56,8 @@ FUNCTION(TARGET_OS_LIBRARIES target)
TARGET_LINK_LIBRARIES(${target} ws2_32) TARGET_LINK_LIBRARIES(${target} ws2_32)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
TARGET_LINK_LIBRARIES(${target} socket nsl) TARGET_LINK_LIBRARIES(${target} socket nsl)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
TARGET_LINK_LIBRARIES(${target} rt)
ENDIF () ENDIF ()
IF(THREADSAFE) IF(THREADSAFE)
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT}) TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
......
...@@ -74,9 +74,9 @@ int do_clone(git_repository *repo, int argc, char **argv) ...@@ -74,9 +74,9 @@ int do_clone(git_repository *repo, int argc, char **argv)
checkout_opts.progress_cb = checkout_progress; checkout_opts.progress_cb = checkout_progress;
checkout_opts.progress_payload = &pd; checkout_opts.progress_payload = &pd;
clone_opts.checkout_opts = checkout_opts; clone_opts.checkout_opts = checkout_opts;
clone_opts.fetch_progress_cb = &fetch_progress; clone_opts.remote_callbacks.transfer_progress = &fetch_progress;
clone_opts.fetch_progress_payload = &pd; clone_opts.remote_callbacks.credentials = cred_acquire_cb;
clone_opts.cred_acquire_cb = cred_acquire_cb; clone_opts.remote_callbacks.payload = &pd;
// Do the clone // Do the clone
error = git_clone(&cloned_repo, url, path, &clone_opts); error = git_clone(&cloned_repo, url, path, &clone_opts);
......
...@@ -35,7 +35,7 @@ static void *download(void *ptr) ...@@ -35,7 +35,7 @@ static void *download(void *ptr)
// Download the packfile and index it. This function updates the // Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you // amount of received data and the indexer stats which lets you
// inform the user about progress. // inform the user about progress.
if (git_remote_download(data->remote, NULL, NULL) < 0) { if (git_remote_download(data->remote) < 0) {
data->ret = -1; data->ret = -1;
goto exit; goto exit;
} }
...@@ -91,8 +91,8 @@ int fetch(git_repository *repo, int argc, char **argv) ...@@ -91,8 +91,8 @@ int fetch(git_repository *repo, int argc, char **argv)
// Set up the callbacks (only update_tips for now) // Set up the callbacks (only update_tips for now)
callbacks.update_tips = &update_cb; callbacks.update_tips = &update_cb;
callbacks.progress = &progress_cb; callbacks.progress = &progress_cb;
callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks); 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 // Set up the information for the background worker thread
data.remote = remote; data.remote = remote;
......
...@@ -18,6 +18,7 @@ static int use_remote(git_repository *repo, char *name) ...@@ -18,6 +18,7 @@ static int use_remote(git_repository *repo, char *name)
{ {
git_remote *remote = NULL; git_remote *remote = NULL;
int error; int error;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
// Find the remote by name // Find the remote by name
error = git_remote_load(&remote, repo, name); error = git_remote_load(&remote, repo, name);
...@@ -27,7 +28,8 @@ static int use_remote(git_repository *repo, char *name) ...@@ -27,7 +28,8 @@ static int use_remote(git_repository *repo, char *name)
goto cleanup; 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); error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
if (error < 0) if (error < 0)
......
...@@ -8,58 +8,51 @@ ...@@ -8,58 +8,51 @@
#ifndef INCLUDE_git_git_h__ #ifndef INCLUDE_git_git_h__
#define INCLUDE_git_git_h__ #define INCLUDE_git_git_h__
#include "git2/version.h" #include "git2/attr.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/blob.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/commit.h"
#include "git2/tag.h" #include "git2/common.h"
#include "git2/tree.h"
#include "git2/diff.h"
#include "git2/index.h"
#include "git2/config.h" #include "git2/config.h"
#include "git2/transport.h" #include "git2/diff.h"
#include "git2/remote.h" #include "git2/errors.h"
#include "git2/clone.h" #include "git2/filter.h"
#include "git2/checkout.h" #include "git2/graph.h"
#include "git2/push.h"
#include "git2/attr.h"
#include "git2/ignore.h" #include "git2/ignore.h"
#include "git2/branch.h" #include "git2/index.h"
#include "git2/refspec.h"
#include "git2/net.h"
#include "git2/status.h"
#include "git2/indexer.h" #include "git2/indexer.h"
#include "git2/submodule.h" #include "git2/merge.h"
#include "git2/notes.h"
#include "git2/reset.h"
#include "git2/message.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/pack.h"
#include "git2/stash.h"
#include "git2/pathspec.h" #include "git2/pathspec.h"
#include "git2/blame.h" #include "git2/push.h"
#include "git2/refdb.h"
#include "git2/buffer.h" #include "git2/reflog.h"
#include "git2/filter.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 #endif
...@@ -255,7 +255,7 @@ typedef struct git_checkout_opts { ...@@ -255,7 +255,7 @@ typedef struct git_checkout_opts {
*/ */
GIT_EXTERN(int) git_checkout_head( GIT_EXTERN(int) git_checkout_head(
git_repository *repo, 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. * Updates files in the working tree to match the content of the index.
......
...@@ -35,30 +35,12 @@ GIT_BEGIN_DECL ...@@ -35,30 +35,12 @@ GIT_BEGIN_DECL
* set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT. * set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT.
* - `bare` should be set to zero to create a standard repo, non-zero for * - `bare` should be set to zero to create a standard repo, non-zero for
* a bare repo * a bare repo
* - `fetch_progress_cb` is optional callback for fetch progress. Be aware that * - `ignore_cert_errors` should be set to 1 if errors validating the remote host's
* this is called inline with network and indexing operations, so performance * certificate should be ignored.
* may be affected.
* - `fetch_progress_payload` is payload for fetch_progress_cb
* *
* ** "origin" remote options: ** * ** "origin" remote options: **
* - `remote_name` is the name given to the "origin" remote. The default is * - `remote_name` is the name given to the "origin" remote. The default is
* "origin". * "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 * - `checkout_branch` gives the name of the branch to checkout. NULL means
* use the remote's HEAD. * use the remote's HEAD.
*/ */
...@@ -67,30 +49,23 @@ typedef struct git_clone_options { ...@@ -67,30 +49,23 @@ typedef struct git_clone_options {
unsigned int version; unsigned int version;
git_checkout_opts checkout_opts; git_checkout_opts checkout_opts;
git_repository_init_options *init_options; git_remote_callbacks remote_callbacks;
int bare;
git_transfer_progress_callback fetch_progress_cb;
void *fetch_progress_payload;
int bare;
int ignore_cert_errors;
const char *remote_name; 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; const char* checkout_branch;
} git_clone_options; } git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION 1 #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 * Clone a remote repository.
* HEAD. *
* 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 out pointer that will receive the resulting repository object
* @param url the remote repository to clone * @param url the remote repository to clone
...@@ -106,6 +81,22 @@ GIT_EXTERN(int) git_clone( ...@@ -106,6 +81,22 @@ GIT_EXTERN(int) git_clone(
const char *local_path, const char *local_path,
const git_clone_options *options); 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 GIT_END_DECL
#endif #endif
...@@ -100,12 +100,23 @@ GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit); ...@@ -100,12 +100,23 @@ GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit);
/** /**
* Get the full message of a 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. * @param commit a previously loaded commit.
* @return the message of a commit * @return the message of a commit
*/ */
GIT_EXTERN(const char *) git_commit_message(const git_commit *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. * Get the commit time (i.e. committer time) of a commit.
* *
* @param commit a previously loaded commit. * @param commit a previously loaded commit.
......
...@@ -46,6 +46,14 @@ ...@@ -46,6 +46,14 @@
GIT_BEGIN_DECL 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 * Initialize a new packbuilder
* *
* @param out The new packbuilder object * @param out The new packbuilder object
...@@ -149,6 +157,28 @@ GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb); ...@@ -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); 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 * Free the packbuilder and all associated data
* *
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#define INCLUDE_git_push_h__ #define INCLUDE_git_push_h__
#include "common.h" #include "common.h"
#include "pack.h"
/** /**
* @file git2/push.h * @file git2/push.h
...@@ -38,6 +39,13 @@ typedef struct { ...@@ -38,6 +39,13 @@ typedef struct {
#define GIT_PUSH_OPTIONS_VERSION 1 #define GIT_PUSH_OPTIONS_VERSION 1
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION } #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 * Create a new push object
* *
...@@ -61,6 +69,27 @@ GIT_EXTERN(int) git_push_set_options( ...@@ -61,6 +69,27 @@ GIT_EXTERN(int) git_push_set_options(
const git_push_options *opts); 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 * Add a refspec to be pushed
* *
* @param push The push object * @param push The push object
......
...@@ -257,17 +257,9 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void ...@@ -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 * The .idx file will be created and both it and the packfile with be
* renamed to their final name. * 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 * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_remote_download( GIT_EXTERN(int) git_remote_download(git_remote *remote);
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *payload);
/** /**
* Check whether the remote is connected * Check whether the remote is connected
...@@ -319,6 +311,17 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote); ...@@ -319,6 +311,17 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
GIT_EXTERN(int) git_remote_update_tips(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 * Return whether a string is a valid remote URL
* *
* @param url the url to check * @param url the url to check
...@@ -397,13 +400,47 @@ typedef enum git_remote_completion_type { ...@@ -397,13 +400,47 @@ typedef enum git_remote_completion_type {
/** /**
* The callback settings structure * 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 { struct git_remote_callbacks {
unsigned int version; 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); 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); 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); 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; void *payload;
}; };
...@@ -420,7 +457,7 @@ struct git_remote_callbacks { ...@@ -420,7 +457,7 @@ struct git_remote_callbacks {
* @param callbacks a pointer to the user's callback settings * @param callbacks a pointer to the user's callback settings
* @return 0 or an error code * @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. * 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 ...@@ -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 */ /* Signature of a function which creates a transport */
typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); 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 /* Transports which come with libgit2 (match git_transport_cb). The expected
* value for "param" is listed in-line below. */ * value for "param" is listed in-line below. */
......
...@@ -1119,7 +1119,7 @@ static void checkout_data_clear(checkout_data *data) ...@@ -1119,7 +1119,7 @@ static void checkout_data_clear(checkout_data *data)
static int checkout_data_init( static int checkout_data_init(
checkout_data *data, checkout_data *data,
git_iterator *target, git_iterator *target,
git_checkout_opts *proposed) const git_checkout_opts *proposed)
{ {
int error = 0; int error = 0;
git_repository *repo = git_iterator_owner(target); git_repository *repo = git_iterator_owner(target);
...@@ -1229,7 +1229,7 @@ cleanup: ...@@ -1229,7 +1229,7 @@ cleanup:
int git_checkout_iterator( int git_checkout_iterator(
git_iterator *target, git_iterator *target,
git_checkout_opts *opts) const git_checkout_opts *opts)
{ {
int error = 0; int error = 0;
git_iterator *baseline = NULL, *workdir = NULL; git_iterator *baseline = NULL, *workdir = NULL;
...@@ -1404,7 +1404,7 @@ int git_checkout_tree( ...@@ -1404,7 +1404,7 @@ int git_checkout_tree(
int git_checkout_head( int git_checkout_head(
git_repository *repo, git_repository *repo,
git_checkout_opts *opts) const git_checkout_opts *opts)
{ {
int error; int error;
git_tree *head = NULL; git_tree *head = NULL;
......
...@@ -19,6 +19,6 @@ ...@@ -19,6 +19,6 @@
*/ */
extern int git_checkout_iterator( extern int git_checkout_iterator(
git_iterator *target, git_iterator *target,
git_checkout_opts *opts); const git_checkout_opts *opts);
#endif #endif
...@@ -270,23 +270,23 @@ cleanup: ...@@ -270,23 +270,23 @@ cleanup:
static int update_head_to_branch( static int update_head_to_branch(
git_repository *repo, git_repository *repo,
const git_clone_options *options) const char *remote_name,
const char *branch)
{ {
int retcode; int retcode;
git_buf remote_branch_name = GIT_BUF_INIT; git_buf remote_branch_name = GIT_BUF_INIT;
git_reference* remote_ref = NULL; 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", 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; goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0) if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
goto cleanup; goto cleanup;
retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
options->checkout_branch);
cleanup: cleanup:
git_reference_free(remote_ref); git_reference_free(remote_ref);
...@@ -306,41 +306,18 @@ static int create_and_configure_origin( ...@@ -306,41 +306,18 @@ static int create_and_configure_origin(
{ {
int error; int error;
git_remote *origin = NULL; git_remote *origin = NULL;
const char *name;
if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0) name = options->remote_name ? options->remote_name : "origin";
goto on_error; if ((error = git_remote_create(&origin, repo, name, url)) < 0)
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)
goto on_error; goto on_error;
if (options->fetch_spec) { if (options->ignore_cert_errors)
git_remote_clear_refspecs(origin); git_remote_check_cert(origin, 0);
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->pushurl && if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
(error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
goto on_error; 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) if ((error = git_remote_save(origin)) < 0)
goto on_error; goto on_error;
...@@ -352,59 +329,10 @@ on_error: ...@@ -352,59 +329,10 @@ on_error:
return 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( static bool should_checkout(
git_repository *repo, git_repository *repo,
bool is_bare, bool is_bare,
git_checkout_opts *opts) const git_checkout_opts *opts)
{ {
if (is_bare) if (is_bare)
return false; return false;
...@@ -418,39 +346,63 @@ static bool should_checkout( ...@@ -418,39 +346,63 @@ static bool should_checkout(
return !git_repository_head_unborn(repo); 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; int error = 0, old_fetchhead;
if (!src) src = &default_options; size_t nspecs;
*dst = *src; assert(repo && remote);
/* Provide defaults for null pointers */ if (!git_repository_is_empty(repo)) {
if (!dst->remote_name) dst->remote_name = "origin"; giterr_set(GITERR_INVALID, "the repository is not empty");
if (!dst->init_options) { return -1;
dst->init_options = initOptions;
initOptions->flags = GIT_REPOSITORY_INIT_MKPATH;
if (dst->bare)
initOptions->flags |= GIT_REPOSITORY_INIT_BARE;
} }
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( int git_clone(
git_repository **out, git_repository **out,
const char *url, const char *url,
const char *local_path, const char *local_path,
const git_clone_options *options) const git_clone_options *_options)
{ {
int retcode = GIT_ERROR; int retcode = GIT_ERROR;
git_repository *repo = NULL; 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; int remove_directory_on_failure = 0;
git_repository_init_options initOptions = GIT_REPOSITORY_INIT_OPTIONS_INIT;
assert(out && url && local_path); assert(out && url && local_path);
normalize_options(&normOptions, options, &initOptions); if (_options)
GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_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 */ /* Only clone to a new directory or an empty directory */
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) { if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
...@@ -462,24 +414,27 @@ int git_clone( ...@@ -462,24 +414,27 @@ int git_clone(
/* Only remove the directory on failure if we create it */ /* Only remove the directory on failure if we create it */
remove_directory_on_failure = !git_path_exists(local_path); remove_directory_on_failure = !git_path_exists(local_path);
if (!(retcode = git_repository_init_ext(&repo, local_path, normOptions.init_options))) { if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) { return retcode;
/* Failed to fetch; clean up */
git_repository_free(repo);
if (remove_directory_on_failure) if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES); goto cleanup;
else
git_futils_cleanupdir_r(local_path);
} else { retcode = git_clone_into(repo, origin, &options.checkout_opts, options.checkout_branch);
*out = repo; git_remote_free(origin);
retcode = 0;
} if (retcode < 0)
} goto cleanup;
if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts)) *out = repo;
retcode = git_checkout_head(*out, &normOptions.checkout_opts); 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; return retcode;
} }
...@@ -29,7 +29,7 @@ void git_commit__free(void *_commit) ...@@ -29,7 +29,7 @@ void git_commit__free(void *_commit)
git_signature_free(commit->committer); git_signature_free(commit->committer);
git__free(commit->raw_header); git__free(commit->raw_header);
git__free(commit->message); git__free(commit->raw_message);
git__free(commit->message_encoding); git__free(commit->message_encoding);
git__free(commit); git__free(commit);
...@@ -245,8 +245,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj) ...@@ -245,8 +245,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
/* extract commit message */ /* extract commit message */
if (buffer <= buffer_end) { if (buffer <= buffer_end) {
commit->message = git__strndup(buffer, buffer_end - buffer); commit->raw_message = git__strndup(buffer, buffer_end - buffer);
GITERR_CHECK_ALLOC(commit->message); GITERR_CHECK_ALLOC(commit->raw_message);
} }
return 0; return 0;
...@@ -265,7 +265,7 @@ bad_buffer: ...@@ -265,7 +265,7 @@ bad_buffer:
GIT_COMMIT_GETTER(const git_signature *, author, commit->author) GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer) 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 *, message_encoding, commit->message_encoding)
GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header) GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time) 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) ...@@ -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(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id); 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) int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{ {
assert(commit); assert(commit);
......
...@@ -24,7 +24,7 @@ struct git_commit { ...@@ -24,7 +24,7 @@ struct git_commit {
git_signature *committer; git_signature *committer;
char *message_encoding; char *message_encoding;
char *message; char *raw_message;
char *raw_header; char *raw_header;
}; };
......
...@@ -712,7 +712,6 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con ...@@ -712,7 +712,6 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
int c, rpos; int c, rpos;
char *first_quote, *last_quote; char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
int quote_marks;
/* /*
* base_name is what came before the space. We should be at the * 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 * 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 ...@@ -731,21 +730,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
git_buf_printf(&buf, "%s.", base_name); git_buf_printf(&buf, "%s.", base_name);
rpos = 0; rpos = 0;
quote_marks = 0;
line = first_quote; line = first_quote;
c = line[rpos++]; c = line[++rpos];
/* /*
* At the end of each iteration, whatever is stored in c will be * At the end of each iteration, whatever is stored in c will be
* added to the string. In case of error, jump to out * added to the string. In case of error, jump to out
*/ */
do { do {
if (quote_marks == 2) {
set_parse_error(reader, rpos, "Unexpected text after closing quotes");
git_buf_free(&buf);
return -1;
}
switch (c) { switch (c) {
case 0: case 0:
...@@ -754,25 +747,13 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con ...@@ -754,25 +747,13 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
return -1; return -1;
case '"': case '"':
++quote_marks; goto end_parse;
continue;
case '\\': case '\\':
c = line[rpos++]; 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;
default: if (c == 0) {
set_parse_error(reader, rpos, "Unsupported escape sequence"); set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
git_buf_free(&buf); git_buf_free(&buf);
return -1; return -1;
} }
...@@ -782,7 +763,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con ...@@ -782,7 +763,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
} }
git_buf_putc(&buf, (char)c); 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); *section_name = git_buf_detach(&buf);
return 0; return 0;
...@@ -800,7 +789,7 @@ static int parse_section_header(struct reader *reader, char **section_out) ...@@ -800,7 +789,7 @@ static int parse_section_header(struct reader *reader, char **section_out)
return -1; return -1;
/* find the end of the variable's name */ /* find the end of the variable's name */
name_end = strchr(line, ']'); name_end = strrchr(line, ']');
if (name_end == NULL) { if (name_end == NULL) {
git__free(line); git__free(line);
set_parse_error(reader, 0, "Missing ']' in section header"); set_parse_error(reader, 0, "Missing ']' in section header");
......
...@@ -119,15 +119,13 @@ int git_fetch_negotiate(git_remote *remote) ...@@ -119,15 +119,13 @@ int git_fetch_negotiate(git_remote *remote)
remote->refs.length); remote->refs.length);
} }
int git_fetch_download_pack( int git_fetch_download_pack(git_remote *remote)
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *progress_payload)
{ {
git_transport *t = remote->transport; git_transport *t = remote->transport;
if(!remote->need_pack) if(!remote->need_pack)
return 0; 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 @@ ...@@ -11,10 +11,7 @@
int git_fetch_negotiate(git_remote *remote); int git_fetch_negotiate(git_remote *remote);
int git_fetch_download_pack( int git_fetch_download_pack(git_remote *remote);
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *progress_payload);
int git_fetch__download_pack( int git_fetch__download_pack(
git_transport *t, git_transport *t,
......
...@@ -426,7 +426,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz ...@@ -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) if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1; return -1;
hash_partially(idx, data, size); hash_partially(idx, data, (int)size);
/* Make sure we set the new size of the pack */ /* Make sure we set the new size of the pack */
if (idx->opened_pack) { if (idx->opened_pack) {
......
...@@ -573,6 +573,93 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) ...@@ -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); 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( int gitno_extract_url_parts(
char **host, char **host,
char **port, char **port,
......
...@@ -66,6 +66,29 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags); ...@@ -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_close(gitno_socket *s);
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
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( int gitno_extract_url_parts(
char **host, char **host,
char **port, char **port,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "pack.h" #include "pack.h"
#include "thread-utils.h" #include "thread-utils.h"
#include "tree.h" #include "tree.h"
#include "util.h"
#include "git2/pack.h" #include "git2/pack.h"
#include "git2/commit.h" #include "git2/commit.h"
...@@ -57,6 +58,9 @@ struct pack_write_context { ...@@ -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_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock) #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) static unsigned name_hash(const char *name)
{ {
unsigned c, hash = 0; unsigned c, hash = 0;
...@@ -212,6 +216,14 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid, ...@@ -212,6 +216,14 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
assert(ret != 0); assert(ret != 0);
kh_value(pb->object_ix, pos) = po; 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; pb->done = false;
return 0; return 0;
} }
...@@ -1207,6 +1219,13 @@ static int prepare_pack(git_packbuilder *pb) ...@@ -1207,6 +1219,13 @@ static int prepare_pack(git_packbuilder *pb)
if (pb->nr_objects == 0 || pb->done) if (pb->nr_objects == 0 || pb->done)
return 0; /* nothing to do */ 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)); delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
GITERR_CHECK_ALLOC(delta_list); GITERR_CHECK_ALLOC(delta_list);
...@@ -1348,6 +1367,17 @@ uint32_t git_packbuilder_written(git_packbuilder *pb) ...@@ -1348,6 +1367,17 @@ uint32_t git_packbuilder_written(git_packbuilder *pb)
return pb->nr_written; 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) void git_packbuilder_free(git_packbuilder *pb)
{ {
if (pb == NULL) if (pb == NULL)
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "netops.h" #include "netops.h"
#include "git2/oid.h" #include "git2/oid.h"
#include "git2/pack.h"
#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */ #define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
#define GIT_PACK_DEPTH 50 /* max delta depth */ #define GIT_PACK_DEPTH 50 /* max delta depth */
...@@ -79,6 +80,10 @@ struct git_packbuilder { ...@@ -79,6 +80,10 @@ struct git_packbuilder {
int nr_threads; /* nr of threads to use */ 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; bool done;
}; };
......
...@@ -70,6 +70,25 @@ int git_push_set_options(git_push *push, const git_push_options *opts) ...@@ -70,6 +70,25 @@ int git_push_set_options(git_push *push, const git_push_options *opts)
return 0; 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) static void free_refspec(push_spec *spec)
{ {
if (spec == NULL) if (spec == NULL)
...@@ -583,6 +602,10 @@ static int do_push(git_push *push) ...@@ -583,6 +602,10 @@ static int do_push(git_push *push)
git_packbuilder_set_threads(push->pb, push->pb_parallelism); 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 || if ((error = calculate_work(push)) < 0 ||
(error = queue_objects(push)) < 0 || (error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push)) < 0) (error = transport->push(transport, push)) < 0)
......
...@@ -39,6 +39,11 @@ struct git_push { ...@@ -39,6 +39,11 @@ struct git_push {
/* options */ /* options */
unsigned pb_parallelism; 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) ...@@ -591,7 +591,7 @@ int git_remote_connect(git_remote *remote, git_direction direction)
if (!remote->check_cert) if (!remote->check_cert)
flags |= GIT_TRANSPORTFLAGS_NO_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; goto on_error;
remote->transport = t; remote->transport = t;
...@@ -742,10 +742,7 @@ static int remote_head_cmp(const void *_a, const void *_b) ...@@ -742,10 +742,7 @@ static int remote_head_cmp(const void *_a, const void *_b)
return git__strcmp_cb(a->name, b->name); return git__strcmp_cb(a->name, b->name);
} }
int git_remote_download( int git_remote_download(git_remote *remote)
git_remote *remote,
git_transfer_progress_callback progress_cb,
void *progress_payload)
{ {
int error; int error;
git_vector refs; git_vector refs;
...@@ -767,7 +764,25 @@ int git_remote_download( ...@@ -767,7 +764,25 @@ int git_remote_download(
if ((error = git_fetch_negotiate(remote)) < 0) if ((error = git_fetch_negotiate(remote)) < 0)
return error; 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) 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) ...@@ -1138,7 +1153,7 @@ void git_remote_check_cert(git_remote *remote, int check)
remote->check_cert = 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); assert(remote && callbacks);
...@@ -1147,7 +1162,7 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *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)); memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
if (remote->transport && remote->transport->set_callbacks) if (remote->transport && remote->transport->set_callbacks)
remote->transport->set_callbacks(remote->transport, return remote->transport->set_callbacks(remote->transport,
remote->callbacks.progress, remote->callbacks.progress,
NULL, NULL,
remote->callbacks.payload); remote->callbacks.payload);
...@@ -1155,17 +1170,6 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks ...@@ -1155,17 +1170,6 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
return 0; 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) int git_remote_set_transport(git_remote *remote, git_transport *transport)
{ {
assert(remote && transport); assert(remote && transport);
......
...@@ -21,8 +21,6 @@ struct git_remote { ...@@ -21,8 +21,6 @@ struct git_remote {
char *pushurl; char *pushurl;
git_vector refs; git_vector refs;
git_vector refspecs; git_vector refspecs;
git_cred_acquire_cb cred_acquire_cb;
void *cred_acquire_payload;
git_transport *transport; git_transport *transport;
git_repository *repo; git_repository *repo;
git_remote_callbacks callbacks; git_remote_callbacks callbacks;
......
...@@ -316,6 +316,8 @@ static int build_workdir_tree( ...@@ -316,6 +316,8 @@ static int build_workdir_tree(
struct cb_data data = {0}; struct cb_data data = {0};
int error; int error;
opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
if ((error = git_commit_tree(&b_tree, b_commit)) < 0) if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
goto cleanup; goto cleanup;
...@@ -474,12 +476,14 @@ static int ensure_there_are_changes_to_stash( ...@@ -474,12 +476,14 @@ static int ensure_there_are_changes_to_stash(
git_status_options opts = GIT_STATUS_OPTIONS_INIT; git_status_options opts = GIT_STATUS_OPTIONS_INIT;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
if (include_untracked_files) if (include_untracked_files)
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
if (include_ignored_files) 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); error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
......
...@@ -42,6 +42,8 @@ static transport_definition transports[] = { ...@@ -42,6 +42,8 @@ static transport_definition transports[] = {
{NULL, 0, 0} {NULL, 0, 0}
}; };
static git_vector additional_transports = GIT_VECTOR_INIT;
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1 #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
static int transport_find_fn(const char *url, git_transport_cb *callback, void **param) 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 * ...@@ -61,6 +63,14 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
definition = definition_iter; 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 #ifdef GIT_WIN32
/* On Windows, it might not be possible to discern between absolute local /* 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 * 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) ...@@ -135,6 +145,62 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url)
return 0; 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 */ /* from remote.h */
int git_remote_valid_url(const char *url) int git_remote_valid_url(const char *url)
{ {
......
...@@ -12,8 +12,6 @@ ...@@ -12,8 +12,6 @@
#include "netops.h" #include "netops.h"
#include "smart.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_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-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"; static const char *upload_pack_service_url = "/git-upload-pack";
...@@ -59,16 +57,11 @@ typedef struct { ...@@ -59,16 +57,11 @@ typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
gitno_socket socket; gitno_socket socket;
char *path; gitno_connection_data connection_data;
char *host;
char *port;
char *user_from_url;
char *pass_from_url;
git_cred *cred; git_cred *cred;
git_cred *url_cred; git_cred *url_cred;
http_authmechanism_t auth_mechanism; http_authmechanism_t auth_mechanism;
unsigned connected : 1, bool connected;
use_ssl : 1;
/* Parser structures */ /* Parser structures */
http_parser parser; http_parser parser;
...@@ -125,12 +118,12 @@ static int gen_request( ...@@ -125,12 +118,12 @@ static int gen_request(
size_t content_length) size_t content_length)
{ {
http_subtransport *t = OWNING_SUBTRANSPORT(s); 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_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_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) { if (s->chunked || content_length > 0) {
git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service); git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
...@@ -150,9 +143,9 @@ static int gen_request( ...@@ -150,9 +143,9 @@ static int gen_request(
return -1; return -1;
/* Use url-parsed basic auth if username and password are both provided */ /* 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->cred && t->connection_data.user && t->connection_data.pass) {
if (!t->url_cred && if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0) t->connection_data.user, t->connection_data.pass) < 0)
return -1; return -1;
if (apply_basic_credential(buf, t->url_cred) < 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) ...@@ -249,98 +242,6 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0; 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) static int on_headers_complete(http_parser *parser)
{ {
parser_context *ctx = (parser_context *) parser->data; parser_context *ctx = (parser_context *) parser->data;
...@@ -369,7 +270,7 @@ static int on_headers_complete(http_parser *parser) ...@@ -369,7 +270,7 @@ static int on_headers_complete(http_parser *parser)
if (t->owner->cred_acquire_cb(&t->cred, if (t->owner->cred_acquire_cb(&t->cred,
t->owner->url, t->owner->url,
t->user_from_url, t->connection_data.user,
allowed_types, allowed_types,
t->owner->cred_acquire_payload) < 0) t->owner->cred_acquire_payload) < 0)
return PARSE_ERROR_GENERIC; return PARSE_ERROR_GENERIC;
...@@ -384,17 +285,17 @@ static int on_headers_complete(http_parser *parser) ...@@ -384,17 +285,17 @@ static int on_headers_complete(http_parser *parser)
/* Check for a redirect. /* Check for a redirect.
* Right now we only permit a redirect to the same hostname. */ * Right now we only permit a redirect to the same hostname. */
if ((parser->status_code == 301 || if ((parser->status_code == 301 ||
parser->status_code == 302 || parser->status_code == 302 ||
(parser->status_code == 303 && get_verb == s->verb) || (parser->status_code == 303 && get_verb == s->verb) ||
parser->status_code == 307) && parser->status_code == 307) &&
t->location) { t->location) {
if (s->redirect_count >= 7) { if (s->redirect_count >= 7) {
giterr_set(GITERR_NET, "Too many redirects"); giterr_set(GITERR_NET, "Too many redirects");
return t->parse_error = PARSE_ERROR_GENERIC; 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; return t->parse_error = PARSE_ERROR_GENERIC;
/* Set the redirect URL on the stream. This is a transfer of /* Set the redirect URL on the stream. This is a transfer of
...@@ -552,7 +453,7 @@ static int http_connect(http_subtransport *t) ...@@ -552,7 +453,7 @@ static int http_connect(http_subtransport *t)
if (t->socket.socket) if (t->socket.socket)
gitno_close(&t->socket); gitno_close(&t->socket);
if (t->use_ssl) { if (t->connection_data.use_ssl) {
int tflags; int tflags;
if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0) if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
...@@ -564,7 +465,7 @@ static int http_connect(http_subtransport *t) ...@@ -564,7 +465,7 @@ static int http_connect(http_subtransport *t)
flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT; 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; return -1;
t->connected = 1; t->connected = 1;
...@@ -911,10 +812,9 @@ static int http_action( ...@@ -911,10 +812,9 @@ static int http_action(
if (!stream) if (!stream)
return -1; return -1;
if (!t->host || !t->port || !t->path) { if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
if ((ret = set_connection_data_from_url(t, url, NULL)) < 0) (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
return ret; return ret;
}
if (http_connect(t) < 0) if (http_connect(t) < 0)
return -1; return -1;
...@@ -958,7 +858,7 @@ static int http_close(git_smart_subtransport *subtransport) ...@@ -958,7 +858,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL; t->url_cred = NULL;
} }
free_connection_data(t); gitno_connection_data_free_ptrs(&t->connection_data);
return 0; return 0;
} }
......
...@@ -434,7 +434,7 @@ static int local_push( ...@@ -434,7 +434,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 || if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url, 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; goto on_error;
} }
......
...@@ -13,8 +13,11 @@ ...@@ -13,8 +13,11 @@
#include "push.h" #include "push.h"
#include "pack-objects.h" #include "pack-objects.h"
#include "remote.h" #include "remote.h"
#include "util.h"
#define NETWORK_XFER_THRESHOLD (100*1024) #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) int git_smart__store_refs(transport_smart *t, int flushes)
{ {
...@@ -801,22 +804,53 @@ static int update_refs_from_report( ...@@ -801,22 +804,53 @@ static int update_refs_from_report(
return 0; 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) 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) int git_smart__push(git_transport *transport, git_push *push)
{ {
transport_smart *t = (transport_smart *)transport; transport_smart *t = (transport_smart *)transport;
git_smart_subtransport_stream *s; struct push_packbuilder_payload packbuilder_payload = {0};
git_buf pktline = GIT_BUF_INIT; git_buf pktline = GIT_BUF_INIT;
int error = -1, need_pack = 0; int error = -1, need_pack = 0;
push_spec *spec; push_spec *spec;
unsigned int i; 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 #ifdef PUSH_DEBUG
{ {
git_remote_head *head; git_remote_head *head;
...@@ -848,12 +882,12 @@ int git_smart__push(git_transport *transport, git_push *push) ...@@ -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 || 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; 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; goto on_error;
/* If we sent nothing or the server doesn't support report-status, then /* 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) ...@@ -863,6 +897,11 @@ int git_smart__push(git_transport *transport, git_push *push)
else if (parse_report(&t->buffer, push) < 0) else if (parse_report(&t->buffer, push) < 0)
goto on_error; 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 && if (push->status.length &&
update_refs_from_report(&t->refs, &push->specs, &push->status) < 0) update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
goto on_error; goto on_error;
......
...@@ -73,17 +73,12 @@ typedef struct { ...@@ -73,17 +73,12 @@ typedef struct {
typedef struct { typedef struct {
git_smart_subtransport parent; git_smart_subtransport parent;
transport_smart *owner; transport_smart *owner;
char *path; gitno_connection_data connection_data;
char *host;
char *port;
char *user_from_url;
char *pass_from_url;
git_cred *cred; git_cred *cred;
git_cred *url_cred; git_cred *url_cred;
int auth_mechanism; int auth_mechanism;
HINTERNET session; HINTERNET session;
HINTERNET connection; HINTERNET connection;
unsigned use_ssl : 1;
} winhttp_subtransport; } winhttp_subtransport;
static int apply_basic_credential(HINTERNET request, git_cred *cred) static int apply_basic_credential(HINTERNET request, git_cred *cred)
...@@ -155,7 +150,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -155,7 +150,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS; unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */ /* 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)) if (git_buf_oom(&buf))
return -1; return -1;
...@@ -188,7 +183,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -188,7 +183,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
NULL, NULL,
WINHTTP_NO_REFERER, WINHTTP_NO_REFERER,
types, types,
t->use_ssl ? WINHTTP_FLAG_SECURE : 0); t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
if (!s->request) { if (!s->request) {
giterr_set(GITERR_OS, "Failed to open request"); giterr_set(GITERR_OS, "Failed to open request");
...@@ -196,7 +191,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -196,7 +191,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
} }
/* Set proxy if necessary */ /* 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; goto on_error;
if (proxy_url) { if (proxy_url) {
...@@ -285,7 +280,7 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -285,7 +280,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
} }
/* If requested, disable certificate validation */ /* If requested, disable certificate validation */
if (t->use_ssl) { if (t->connection_data.use_ssl) {
int flags; int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0) if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
...@@ -308,9 +303,9 @@ static int winhttp_stream_connect(winhttp_stream *s) ...@@ -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 /* If no other credentials have been applied and the URL has username and
* password, use those */ * 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 && 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; goto on_error;
if (apply_basic_credential(s->request, t->url_cred) < 0) if (apply_basic_credential(s->request, t->url_cred) < 0)
goto on_error; goto on_error;
...@@ -392,98 +387,6 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len) ...@@ -392,98 +387,6 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0; 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( static int winhttp_connect(
winhttp_subtransport *t, winhttp_subtransport *t,
const char *url) const char *url)
...@@ -494,11 +397,11 @@ static int winhttp_connect( ...@@ -494,11 +397,11 @@ static int winhttp_connect(
const char *default_port = "80"; const char *default_port = "80";
/* Prepare port */ /* Prepare port */
if (git__strtol32(&port, t->port, NULL, 10) < 0) if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
return -1; return -1;
/* Prepare host */ /* Prepare host */
git_win32_path_from_c(host, t->host); git_win32_path_from_c(host, t->connection_data.host);
/* Establish session */ /* Establish session */
t->session = WinHttpOpen( t->session = WinHttpOpen(
...@@ -699,7 +602,8 @@ replay: ...@@ -699,7 +602,8 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) { if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */ /* 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); winhttp_connect(t, location8);
} }
...@@ -718,7 +622,8 @@ replay: ...@@ -718,7 +622,8 @@ replay:
if (allowed_types && if (allowed_types &&
(!t->cred || 0 == (t->cred->credtype & 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; return -1;
assert(t->cred); assert(t->cred);
...@@ -1101,10 +1006,10 @@ static int winhttp_action( ...@@ -1101,10 +1006,10 @@ static int winhttp_action(
winhttp_stream *s; winhttp_stream *s;
int ret = -1; int ret = -1;
if (!t->connection && if (!t->connection)
(set_connection_data_from_url(t, url, NULL) < 0 || if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 ||
winhttp_connect(t, url) < 0)) winhttp_connect(t, url) < 0)
return -1; return -1;
if (winhttp_stream_alloc(t, &s) < 0) if (winhttp_stream_alloc(t, &s) < 0)
return -1; return -1;
...@@ -1145,7 +1050,7 @@ static int winhttp_close(git_smart_subtransport *subtransport) ...@@ -1145,7 +1050,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport; winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0; int ret = 0;
free_connection_data(t); gitno_connection_data_free_ptrs(&t->connection_data);
if (t->cred) { if (t->cred) {
t->cred->free(t->cred); t->cred->free(t->cred);
......
...@@ -353,4 +353,65 @@ GIT_INLINE(void) git__memzero(void *data, size_t size) ...@@ -353,4 +353,65 @@ GIT_INLINE(void) git__memzero(void *data, size_t size)
#endif #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__ */ #endif /* INCLUDE_util_h__ */
...@@ -440,7 +440,7 @@ void clar__assert_equal_file( ...@@ -440,7 +440,7 @@ void clar__assert_equal_file(
int ignore_cr, int ignore_cr,
const char *path, const char *path,
const char *file, const char *file,
size_t line) int line)
{ {
char buf[4000]; char buf[4000];
ssize_t bytes, total_bytes = 0; ssize_t bytes, total_bytes = 0;
......
...@@ -44,7 +44,7 @@ GIT_INLINE(void) clar__assert_in_range( ...@@ -44,7 +44,7 @@ GIT_INLINE(void) clar__assert_in_range(
} }
#define cl_assert_equal_sz(sz1,sz2) do { \ #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); \ clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
} while (0) } while (0)
...@@ -52,10 +52,10 @@ GIT_INLINE(void) clar__assert_in_range( ...@@ -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) clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
#define cl_assert_equal_file(DATA,SIZE,PATH) \ #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) \ #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( void clar__assert_equal_file(
const char *expected_data, const char *expected_data,
...@@ -63,7 +63,7 @@ void clar__assert_equal_file( ...@@ -63,7 +63,7 @@ void clar__assert_equal_file(
int ignore_cr, int ignore_cr,
const char *path, const char *path,
const char *file, const char *file,
size_t line); int line);
/* /*
* Some utility macros for building long strings * Some utility macros for building long strings
......
...@@ -10,12 +10,14 @@ static git_repository *g_repo_cloned; ...@@ -10,12 +10,14 @@ static git_repository *g_repo_cloned;
void test_clone_empty__initialize(void) void test_clone_empty__initialize(void)
{ {
git_repository *sandbox = cl_git_sandbox_init("empty_bare.git"); 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"); cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
g_repo = NULL; g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options)); memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.remote_callbacks = dummy_callbacks;
} }
void test_clone_empty__cleanup(void) void test_clone_empty__cleanup(void)
......
...@@ -15,6 +15,7 @@ static git_remote* g_remote; ...@@ -15,6 +15,7 @@ static git_remote* g_remote;
void test_clone_nonetwork__initialize(void) void test_clone_nonetwork__initialize(void)
{ {
git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
g_repo = NULL; g_repo = NULL;
...@@ -22,6 +23,7 @@ void test_clone_nonetwork__initialize(void) ...@@ -22,6 +23,7 @@ void test_clone_nonetwork__initialize(void)
g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.checkout_opts = dummy_opts; g_options.checkout_opts = dummy_opts;
g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
g_options.remote_callbacks = dummy_callbacks;
} }
void test_clone_nonetwork__cleanup(void) void test_clone_nonetwork__cleanup(void)
...@@ -124,84 +126,17 @@ void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(vo ...@@ -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) void test_clone_nonetwork__custom_origin_name(void)
{ {
g_options.remote_name = "my_origin"; 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_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"; cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL));
cl_assert(g_repo);
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_remote_load(&g_remote, g_repo, "origin")); 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) void test_clone_nonetwork__cope_with_already_existing_directory(void)
......
...@@ -7,77 +7,77 @@ ...@@ -7,77 +7,77 @@
static git_repository *g_repo; static git_repository *g_repo;
void test_commit_parse__initialize(void) 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) void test_commit_parse__cleanup(void)
{ {
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
} }
// Header parsing // Header parsing
typedef struct { typedef struct {
const char *line; const char *line;
const char *header; const char *header;
} parse_test_case; } parse_test_case;
static parse_test_case passing_header_cases[] = { static parse_test_case passing_header_cases[] = {
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " }, { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " }, { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " }, { "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
{ "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" }, { "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
{ "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " }, { "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
{ "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " }, { "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
{ "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " }, { "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
{ "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " }, { "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
{ NULL, NULL } { NULL, NULL }
}; };
static parse_test_case failing_header_cases[] = { static parse_test_case failing_header_cases[] = {
{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " }, { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
{ "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " }, { "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " }, { "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
{ "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " }, { "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " }, { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " }, { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " }, { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
{ "", "tree " }, { "", "tree " },
{ "", "" }, { "", "" },
{ NULL, NULL } { NULL, NULL }
}; };
void test_commit_parse__header(void) void test_commit_parse__header(void)
{ {
git_oid oid; git_oid oid;
parse_test_case *testcase; parse_test_case *testcase;
for (testcase = passing_header_cases; testcase->line != NULL; testcase++) for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
{ {
const char *line = testcase->line; const char *line = testcase->line;
const char *line_end = line + strlen(line); const char *line_end = line + strlen(line);
cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header)); cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
cl_assert(line == line_end); cl_assert(line == line_end);
} }
for (testcase = failing_header_cases; testcase->line != NULL; testcase++) for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
{ {
const char *line = testcase->line; const char *line = testcase->line;
const char *line_end = line + strlen(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 // Signature parsing
typedef struct { typedef struct {
const char *string; const char *string;
const char *header; const char *header;
const char *name; const char *name;
const char *email; const char *email;
git_time_t time; git_time_t time;
int offset; int offset;
} passing_signature_test_case; } passing_signature_test_case;
passing_signature_test_case passing_signature_cases[] = { passing_signature_test_case passing_signature_cases[] = {
...@@ -122,12 +122,12 @@ 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> 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}, {"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 { typedef struct {
const char *string; const char *string;
const char *header; const char *header;
} failing_signature_test_case; } failing_signature_test_case;
failing_signature_test_case failing_signature_cases[] = { failing_signature_test_case failing_signature_cases[] = {
...@@ -143,31 +143,31 @@ failing_signature_test_case failing_signature_cases[] = { ...@@ -143,31 +143,31 @@ failing_signature_test_case failing_signature_cases[] = {
void test_commit_parse__signature(void) void test_commit_parse__signature(void)
{ {
passing_signature_test_case *passcase; passing_signature_test_case *passcase;
failing_signature_test_case *failcase; failing_signature_test_case *failcase;
for (passcase = passing_signature_cases; passcase->string != NULL; passcase++) for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
{ {
const char *str = passcase->string; const char *str = passcase->string;
size_t len = strlen(passcase->string); size_t len = strlen(passcase->string);
struct git_signature person = {0}; struct git_signature person = {0};
cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n')); 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->name, person.name);
cl_assert_equal_s(passcase->email, person.email); cl_assert_equal_s(passcase->email, person.email);
cl_assert_equal_i((int)passcase->time, (int)person.when.time); cl_assert_equal_i((int)passcase->time, (int)person.when.time);
cl_assert_equal_i(passcase->offset, person.when.offset); cl_assert_equal_i(passcase->offset, person.when.offset);
git__free(person.name); git__free(person.email); git__free(person.name); git__free(person.email);
} }
for (failcase = failing_signature_cases; failcase->string != NULL; failcase++) for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
{ {
const char *str = failcase->string; const char *str = failcase->string;
size_t len = strlen(failcase->string); size_t len = strlen(failcase->string);
git_signature person = {0}; git_signature person = {0};
cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n')); cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
git__free(person.name); git__free(person.email); git__free(person.name); git__free(person.email);
} }
} }
...@@ -312,17 +312,17 @@ void test_commit_parse__entire_commit(void) ...@@ -312,17 +312,17 @@ void test_commit_parse__entire_commit(void)
// query the details on a parsed commit // query the details on a parsed commit
void test_commit_parse__details0(void) { void test_commit_parse__details0(void) {
static const char *commit_ids[] = { static const char *commit_ids[] = {
"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */ "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
"a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
}; };
const size_t commit_count = sizeof(commit_ids) / sizeof(const char *); const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
unsigned int i; unsigned int i;
for (i = 0; i < commit_count; ++i) { for (i = 0; i < commit_count; ++i) {
git_oid id; git_oid id;
...@@ -349,7 +349,6 @@ void test_commit_parse__details0(void) { ...@@ -349,7 +349,6 @@ void test_commit_parse__details0(void) {
cl_assert_equal_s("Scott Chacon", committer->name); cl_assert_equal_s("Scott Chacon", committer->name);
cl_assert_equal_s("schacon@gmail.com", committer->email); cl_assert_equal_s("schacon@gmail.com", committer->email);
cl_assert(message != NULL); cl_assert(message != NULL);
cl_assert(strchr(message, '\n') != NULL);
cl_assert(commit_time > 0); cl_assert(commit_time > 0);
cl_assert(parents <= 2); cl_assert(parents <= 2);
for (p = 0;p < parents;p++) { for (p = 0;p < parents;p++) {
...@@ -382,11 +381,33 @@ committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\ ...@@ -382,11 +381,33 @@ committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
\n\ \n\
This commit has a few LF at the start of the commit message"; This commit has a few LF at the start of the commit message";
const char *message = const char *message =
"This commit has a few LF at the start of the commit message";
const char *raw_message =
"\n\ "\n\
\n\ \n\
This commit has a few LF at the start of the commit message"; 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_git_pass(parse_commit(&commit, buffer));
cl_assert_equal_s(message, git_commit_message(commit)); cl_assert_equal_s(message, git_commit_message(commit));
cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
git_commit__free(commit); git_commit__free(commit);
} }
...@@ -164,6 +164,13 @@ void test_config_read__empty_files(void) ...@@ -164,6 +164,13 @@ void test_config_read__empty_files(void)
git_config_free(cfg); 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) void test_config_read__header_in_last_line(void)
{ {
git_config *cfg; git_config *cfg;
...@@ -524,6 +531,28 @@ void test_config_read__corrupt_header(void) ...@@ -524,6 +531,28 @@ void test_config_read__corrupt_header(void)
git_config_free(cfg); 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) void test_config_read__override_variable(void)
{ {
git_config *cfg; git_config *cfg;
......
...@@ -25,13 +25,18 @@ void test_network_fetchlocal__complete(void) ...@@ -25,13 +25,18 @@ void test_network_fetchlocal__complete(void)
git_strarray refnames = {0}; git_strarray refnames = {0};
const char *url = cl_git_fixture_url("testrepo.git"); 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_set_cleanup(&cleanup_local_repo, "foo");
cl_git_pass(git_repository_init(&repo, "foo", true)); cl_git_pass(git_repository_init(&repo, "foo", true));
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); 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_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_remote_update_tips(origin));
cl_git_pass(git_reference_list(&refnames, repo)); cl_git_pass(git_reference_list(&refnames, repo));
...@@ -56,6 +61,10 @@ void test_network_fetchlocal__partial(void) ...@@ -56,6 +61,10 @@ void test_network_fetchlocal__partial(void)
int callcount = 0; int callcount = 0;
git_strarray refnames = {0}; git_strarray refnames = {0};
const char *url; 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_set_cleanup(&cleanup_sandbox, NULL);
cl_git_pass(git_reference_list(&refnames, repo)); cl_git_pass(git_reference_list(&refnames, repo));
...@@ -63,8 +72,9 @@ void test_network_fetchlocal__partial(void) ...@@ -63,8 +72,9 @@ void test_network_fetchlocal__partial(void)
url = cl_git_fixture_url("testrepo.git"); url = cl_git_fixture_url("testrepo.git");
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); 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_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_remote_update_tips(origin));
git_strarray_free(&refnames); git_strarray_free(&refnames);
......
...@@ -123,7 +123,7 @@ void test_network_remote_local__shorthand_fetch_refspec0(void) ...@@ -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, refspec));
cl_git_pass(git_remote_add_fetch(remote, refspec2)); 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_remote_update_tips(remote));
cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); 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) ...@@ -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, refspec));
cl_git_pass(git_remote_add_fetch(remote, refspec2)); 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_remote_update_tips(remote));
cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
...@@ -160,7 +160,7 @@ void test_network_remote_local__tagopt(void) ...@@ -160,7 +160,7 @@ void test_network_remote_local__tagopt(void)
connect_to_local_repository(cl_fixture("testrepo.git")); connect_to_local_repository(cl_fixture("testrepo.git"));
git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL); 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)); cl_git_pass(git_remote_update_tips(remote));
...@@ -179,7 +179,7 @@ void test_network_remote_local__push_to_bare_remote(void) ...@@ -179,7 +179,7 @@ void test_network_remote_local__push_to_bare_remote(void)
/* Get some commits */ /* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git")); connect_to_local_repository(cl_fixture("testrepo.git"));
cl_git_pass(git_remote_add_fetch(remote, "master:master")); 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)); cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote); git_remote_disconnect(remote);
...@@ -215,7 +215,7 @@ void test_network_remote_local__push_to_non_bare_remote(void) ...@@ -215,7 +215,7 @@ void test_network_remote_local__push_to_non_bare_remote(void)
/* Get some commits */ /* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git")); connect_to_local_repository(cl_fixture("testrepo.git"));
cl_git_pass(git_remote_add_fetch(remote, "master:master")); 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)); cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote); git_remote_disconnect(remote);
......
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
#include "netops.h" #include "netops.h"
char *host, *port, *user, *pass; char *host, *port, *user, *pass;
gitno_connection_data conndata;
void test_network_urlparse__initialize(void) void test_network_urlparse__initialize(void)
{ {
host = port = user = pass = NULL; host = port = user = pass = NULL;
memset(&conndata, 0, sizeof(conndata));
} }
void test_network_urlparse__cleanup(void) void test_network_urlparse__cleanup(void)
...@@ -15,6 +17,8 @@ void test_network_urlparse__cleanup(void) ...@@ -15,6 +17,8 @@ void test_network_urlparse__cleanup(void)
FREE_AND_NULL(port); FREE_AND_NULL(port);
FREE_AND_NULL(user); FREE_AND_NULL(user);
FREE_AND_NULL(pass); FREE_AND_NULL(pass);
gitno_connection_data_free_ptrs(&conndata);
} }
void test_network_urlparse__trivial(void) void test_network_urlparse__trivial(void)
...@@ -80,3 +84,80 @@ void test_network_urlparse__user_pass_port(void) ...@@ -80,3 +84,80 @@ void test_network_urlparse__user_pass_port(void)
cl_assert_equal_s(user, "user"); cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass"); 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; ...@@ -18,6 +18,7 @@ static git_clone_options g_options;
void test_online_clone__initialize(void) void test_online_clone__initialize(void)
{ {
git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
g_repo = NULL; g_repo = NULL;
...@@ -25,6 +26,7 @@ void test_online_clone__initialize(void) ...@@ -25,6 +26,7 @@ void test_online_clone__initialize(void)
g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.checkout_opts = dummy_opts; g_options.checkout_opts = dummy_opts;
g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
g_options.remote_callbacks = dummy_callbacks;
} }
void test_online_clone__cleanup(void) void test_online_clone__cleanup(void)
...@@ -103,8 +105,8 @@ void test_online_clone__can_checkout_a_cloned_repo(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.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
g_options.checkout_opts.progress_cb = &checkout_progress; g_options.checkout_opts.progress_cb = &checkout_progress;
g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called; g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
g_options.fetch_progress_cb = &fetch_progress; g_options.remote_callbacks.transfer_progress = &fetch_progress;
g_options.fetch_progress_payload = &fetch_progress_cb_was_called; g_options.remote_callbacks.payload = &fetch_progress_cb_was_called;
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); 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) ...@@ -122,6 +124,45 @@ void test_online_clone__can_checkout_a_cloned_repo(void)
git_buf_free(&path); 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) static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
{ {
int *callcount = (int*)payload; int *callcount = (int*)payload;
...@@ -132,12 +173,10 @@ static int update_tips(const char *refname, const git_oid *a, const git_oid *b, ...@@ -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) void test_online_clone__custom_remote_callbacks(void)
{ {
git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT;
int callcount = 0; int callcount = 0;
g_options.remote_callbacks = &remote_callbacks; g_options.remote_callbacks.update_tips = update_tips;
remote_callbacks.update_tips = update_tips; g_options.remote_callbacks.payload = &callcount;
remote_callbacks.payload = &callcount;
cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
cl_assert(callcount > 0); cl_assert(callcount > 0);
...@@ -154,8 +193,8 @@ void test_online_clone__credentials(void) ...@@ -154,8 +193,8 @@ void test_online_clone__credentials(void)
if (!remote_url) return; if (!remote_url) return;
g_options.cred_acquire_cb = git_cred_userpass; g_options.remote_callbacks.credentials = git_cred_userpass;
g_options.cred_acquire_payload = &user_pass; g_options.remote_callbacks.payload = &user_pass;
cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL; git_repository_free(g_repo); g_repo = NULL;
...@@ -168,8 +207,8 @@ void test_online_clone__bitbucket_style(void) ...@@ -168,8 +207,8 @@ void test_online_clone__bitbucket_style(void)
"libgit2", "libgit2" "libgit2", "libgit2"
}; };
g_options.cred_acquire_cb = git_cred_userpass; g_options.remote_callbacks.credentials = git_cred_userpass;
g_options.cred_acquire_payload = &user_pass; g_options.remote_callbacks.payload = &user_pass;
cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options)); cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
git_repository_free(g_repo); g_repo = NULL; git_repository_free(g_repo); g_repo = NULL;
...@@ -199,6 +238,13 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload) ...@@ -199,6 +238,13 @@ static int cancel_at_half(const git_transfer_progress *stats, void *payload)
void test_online_clone__can_cancel(void) 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); 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) ...@@ -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; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
size_t bytes_received = 0; size_t bytes_received = 0;
callbacks.transfer_progress = progress;
callbacks.update_tips = update_tips; callbacks.update_tips = update_tips;
callbacks.payload = &bytes_received;
counter = 0; counter = 0;
cl_git_pass(git_remote_create(&remote, _repo, "test", url)); cl_git_pass(git_remote_create(&remote, _repo, "test", url));
git_remote_set_callbacks(remote, &callbacks); git_remote_set_callbacks(remote, &callbacks);
git_remote_set_autotag(remote, flag); git_remote_set_autotag(remote, flag);
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); 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)); cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote); git_remote_disconnect(remote);
cl_assert_equal_i(counter, n); 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 ...@@ -93,6 +95,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
git_repository *_repository; git_repository *_repository;
bool invoked = false; bool invoked = false;
git_remote *remote; git_remote *remote;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT; git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
opts.bare = true; opts.bare = true;
...@@ -107,7 +110,10 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date ...@@ -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_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); cl_assert_equal_i(false, invoked);
...@@ -131,11 +137,17 @@ void test_online_fetch__can_cancel(void) ...@@ -131,11 +137,17 @@ void test_online_fetch__can_cancel(void)
{ {
git_remote *remote; git_remote *remote;
size_t bytes_received = 0; size_t bytes_received = 0;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
cl_git_pass(git_remote_create(&remote, _repo, "test", cl_git_pass(git_remote_create(&remote, _repo, "test",
"http://github.com/libgit2/TestGitRepository.git")); "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_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_disconnect(remote);
git_remote_free(remote); git_remote_free(remote);
} }
......
...@@ -12,10 +12,12 @@ static git_clone_options g_options; ...@@ -12,10 +12,12 @@ static git_clone_options g_options;
void test_online_fetchhead__initialize(void) void test_online_fetchhead__initialize(void)
{ {
git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
g_repo = NULL; g_repo = NULL;
memset(&g_options, 0, sizeof(git_clone_options)); memset(&g_options, 0, sizeof(git_clone_options));
g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.version = GIT_CLONE_OPTIONS_VERSION;
g_options.remote_callbacks = dummy_callbacks;
} }
void test_online_fetchhead__cleanup(void) void test_online_fetchhead__cleanup(void)
...@@ -48,7 +50,7 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet ...@@ -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_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)); cl_git_pass(git_remote_update_tips(remote));
git_remote_disconnect(remote); git_remote_disconnect(remote);
git_remote_free(remote); git_remote_free(remote);
......
...@@ -12,7 +12,7 @@ extern const git_oid OID_ZERO; ...@@ -12,7 +12,7 @@ extern const git_oid OID_ZERO;
* @param data pointer to a record_callbacks_data instance * @param data pointer to a record_callbacks_data instance
*/ */
#define RECORD_CALLBACKS_INIT(data) \ #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 { typedef struct {
char *name; 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 ...@@ -113,33 +113,15 @@ $ git status --short
cl_assert_equal_i(GIT_STATUS_WT_NEW, status); 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) void test_stash_save__can_keep_index(void)
{ {
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX)); 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(repo, "what", GIT_STATUS_INDEX_MODIFIED);
assert_status("how", GIT_STATUS_INDEX_MODIFIED); assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
assert_status("who", GIT_STATUS_CURRENT); assert_status(repo, "who", GIT_STATUS_CURRENT);
assert_status("when", GIT_STATUS_WT_NEW); assert_status(repo, "when", GIT_STATUS_WT_NEW);
assert_status("just.ignore", GIT_STATUS_IGNORED); assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
} }
static void assert_commit_message_contains(const char *revision, const char *fragment) 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) ...@@ -308,25 +290,25 @@ void test_stash_save__can_stage_normal_then_stage_untracked(void)
* 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
*/ */
assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED); assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
assert_status("how", GIT_STATUS_INDEX_MODIFIED); assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
assert_status("who", GIT_STATUS_WT_MODIFIED); assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
assert_status("when", GIT_STATUS_WT_NEW); assert_status(repo, "when", GIT_STATUS_WT_NEW);
assert_status("just.ignore", GIT_STATUS_IGNORED); assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT)); cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
assert_status("what", GIT_STATUS_CURRENT); assert_status(repo, "what", GIT_STATUS_CURRENT);
assert_status("how", GIT_STATUS_CURRENT); assert_status(repo, "how", GIT_STATUS_CURRENT);
assert_status("who", GIT_STATUS_CURRENT); assert_status(repo, "who", GIT_STATUS_CURRENT);
assert_status("when", GIT_STATUS_WT_NEW); assert_status(repo, "when", GIT_STATUS_WT_NEW);
assert_status("just.ignore", GIT_STATUS_IGNORED); assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED)); cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
assert_status("what", GIT_STATUS_CURRENT); assert_status(repo, "what", GIT_STATUS_CURRENT);
assert_status("how", GIT_STATUS_CURRENT); assert_status(repo, "how", GIT_STATUS_CURRENT);
assert_status("who", GIT_STATUS_CURRENT); assert_status(repo, "who", GIT_STATUS_CURRENT);
assert_status("when", GIT_ENOTFOUND); assert_status(repo, "when", GIT_ENOTFOUND);
assert_status("just.ignore", GIT_STATUS_IGNORED); assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */ 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_ ...@@ -360,11 +342,11 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_
{ {
cl_git_pass(p_unlink("stash/when")); cl_git_pass(p_unlink("stash/when"));
assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED); assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
assert_status("how", GIT_STATUS_INDEX_MODIFIED); assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
assert_status("who", GIT_STATUS_WT_MODIFIED); assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
assert_status("when", GIT_ENOTFOUND); assert_status(repo, "when", GIT_ENOTFOUND);
assert_status("just.ignore", GIT_STATUS_IGNORED); assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED)); 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) ...@@ -35,3 +35,22 @@ void setup_stash(git_repository *repo, git_signature *signature)
git_index_free(index); 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( void setup_stash(
git_repository *repo, git_repository *repo,
git_signature *signature); 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