summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/checkout.c6
-rw-r--r--src/checkout.h2
-rw-r--r--src/clone.c189
-rw-r--r--src/commit.c21
-rw-r--r--src/commit.h2
-rw-r--r--src/config_file.c41
-rw-r--r--src/fetch.c8
-rw-r--r--src/fetch.h5
-rw-r--r--src/indexer.c2
-rw-r--r--src/netops.c87
-rw-r--r--src/netops.h23
-rw-r--r--src/pack-objects.c30
-rw-r--r--src/pack-objects.h5
-rw-r--r--src/push.c23
-rw-r--r--src/push.h5
-rw-r--r--src/remote.c42
-rw-r--r--src/remote.h2
-rw-r--r--src/stash.c8
-rw-r--r--src/transport.c66
-rw-r--r--src/transports/http.c138
-rw-r--r--src/transports/local.c2
-rw-r--r--src/transports/smart_protocol.c51
-rw-r--r--src/transports/winhttp.c131
-rw-r--r--src/util.h61
24 files changed, 526 insertions, 424 deletions
diff --git a/src/checkout.c b/src/checkout.c
index 0e9d11bff..5d741d393 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1119,7 +1119,7 @@ static void checkout_data_clear(checkout_data *data)
static int checkout_data_init(
checkout_data *data,
git_iterator *target,
- git_checkout_opts *proposed)
+ const git_checkout_opts *proposed)
{
int error = 0;
git_repository *repo = git_iterator_owner(target);
@@ -1229,7 +1229,7 @@ cleanup:
int git_checkout_iterator(
git_iterator *target,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
int error = 0;
git_iterator *baseline = NULL, *workdir = NULL;
@@ -1404,7 +1404,7 @@ int git_checkout_tree(
int git_checkout_head(
git_repository *repo,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
int error;
git_tree *head = NULL;
diff --git a/src/checkout.h b/src/checkout.h
index b1dc80c38..6d7186860 100644
--- a/src/checkout.h
+++ b/src/checkout.h
@@ -19,6 +19,6 @@
*/
extern int git_checkout_iterator(
git_iterator *target,
- git_checkout_opts *opts);
+ const git_checkout_opts *opts);
#endif
diff --git a/src/clone.c b/src/clone.c
index ff251be1b..f3e365c07 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -270,23 +270,23 @@ cleanup:
static int update_head_to_branch(
git_repository *repo,
- const git_clone_options *options)
+ const char *remote_name,
+ const char *branch)
{
int retcode;
git_buf remote_branch_name = GIT_BUF_INIT;
git_reference* remote_ref = NULL;
- assert(options->checkout_branch);
+ assert(remote_name && branch);
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
- options->remote_name, options->checkout_branch)) < 0 )
+ remote_name, branch)) < 0 )
goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
goto cleanup;
- retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
- options->checkout_branch);
+ retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
cleanup:
git_reference_free(remote_ref);
@@ -306,41 +306,18 @@ static int create_and_configure_origin(
{
int error;
git_remote *origin = NULL;
+ const char *name;
- if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0)
- goto on_error;
-
- git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
- options->cred_acquire_payload);
- git_remote_set_autotag(origin, options->remote_autotag);
- /*
- * Don't write FETCH_HEAD, we'll check out the remote tracking
- * branch ourselves based on the server's default.
- */
- git_remote_set_update_fetchhead(origin, 0);
-
- if (options->remote_callbacks &&
- (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
+ name = options->remote_name ? options->remote_name : "origin";
+ if ((error = git_remote_create(&origin, repo, name, url)) < 0)
goto on_error;
- if (options->fetch_spec) {
- git_remote_clear_refspecs(origin);
- if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
- goto on_error;
- }
-
- if (options->push_spec &&
- (error = git_remote_add_push(origin, options->push_spec)) < 0)
- goto on_error;
+ if (options->ignore_cert_errors)
+ git_remote_check_cert(origin, 0);
- if (options->pushurl &&
- (error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
+ if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
goto on_error;
- if (options->transport_flags == GIT_TRANSPORTFLAGS_NO_CHECK_CERT) {
- git_remote_check_cert(origin, 0);
- }
-
if ((error = git_remote_save(origin)) < 0)
goto on_error;
@@ -352,59 +329,10 @@ on_error:
return error;
}
-
-static int setup_remotes_and_fetch(
- git_repository *repo,
- const char *url,
- const git_clone_options *options)
-{
- int retcode = GIT_ERROR;
- git_remote *origin = NULL;
-
- /* Construct an origin remote */
- if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0)
- goto on_error;
-
- git_remote_set_update_fetchhead(origin, 0);
-
- /* If the download_tags value has not been specified, then make sure to
- * download tags as well. It is set here because we want to download tags
- * on the initial clone, but do not want to persist the value in the
- * configuration file.
- */
- if (origin->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO &&
- ((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
- goto on_error;
-
- /* Connect and download everything */
- if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
- goto on_error;
-
- if ((retcode = git_remote_download(origin, options->fetch_progress_cb,
- options->fetch_progress_payload)) < 0)
- goto on_error;
-
- /* Create "origin/foo" branches for all remote branches */
- if ((retcode = git_remote_update_tips(origin)) < 0)
- goto on_error;
-
- /* Point HEAD to the requested branch */
- if (options->checkout_branch)
- retcode = update_head_to_branch(repo, options);
- /* Point HEAD to the same ref as the remote's head */
- else
- retcode = update_head_to_remote(repo, origin);
-
-on_error:
- git_remote_free(origin);
- return retcode;
-}
-
-
static bool should_checkout(
git_repository *repo,
bool is_bare,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
if (is_bare)
return false;
@@ -418,39 +346,63 @@ static bool should_checkout(
return !git_repository_head_unborn(repo);
}
-static void normalize_options(git_clone_options *dst, const git_clone_options *src, git_repository_init_options *initOptions)
+int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
{
- git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
- if (!src) src = &default_options;
-
- *dst = *src;
-
- /* Provide defaults for null pointers */
- if (!dst->remote_name) dst->remote_name = "origin";
- if (!dst->init_options) {
- dst->init_options = initOptions;
- initOptions->flags = GIT_REPOSITORY_INIT_MKPATH;
- if (dst->bare)
- initOptions->flags |= GIT_REPOSITORY_INIT_BARE;
+ int error = 0, old_fetchhead;
+ size_t nspecs;
+
+ assert(repo && remote);
+
+ if (!git_repository_is_empty(repo)) {
+ giterr_set(GITERR_INVALID, "the repository is not empty");
+ return -1;
}
+
+ if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
+ return error;
+
+ old_fetchhead = git_remote_update_fetchhead(remote);
+ git_remote_set_update_fetchhead(remote, 0);
+
+ if ((error = git_remote_fetch(remote)) < 0)
+ goto cleanup;
+
+ if (branch)
+ error = update_head_to_branch(repo, git_remote_name(remote), branch);
+ /* Point HEAD to the same ref as the remote's head */
+ else
+ error = update_head_to_remote(repo, remote);
+
+ if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
+ error = git_checkout_head(repo, co_opts);
+
+cleanup:
+ git_remote_set_update_fetchhead(remote, old_fetchhead);
+ /* Remove the tags refspec */
+ nspecs = git_remote_refspec_count(remote);
+ git_remote_remove_refspec(remote, nspecs);
+
+ return error;
}
int git_clone(
git_repository **out,
const char *url,
const char *local_path,
- const git_clone_options *options)
+ const git_clone_options *_options)
{
int retcode = GIT_ERROR;
git_repository *repo = NULL;
- git_clone_options normOptions;
+ git_remote *origin;
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
int remove_directory_on_failure = 0;
- git_repository_init_options initOptions = GIT_REPOSITORY_INIT_OPTIONS_INIT;
assert(out && url && local_path);
- normalize_options(&normOptions, options, &initOptions);
- GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
+ if (_options)
+ memcpy(&options, _options, sizeof(git_clone_options));
+
+ GITERR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
/* Only clone to a new directory or an empty directory */
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
@@ -462,24 +414,27 @@ int git_clone(
/* Only remove the directory on failure if we create it */
remove_directory_on_failure = !git_path_exists(local_path);
- if (!(retcode = git_repository_init_ext(&repo, local_path, normOptions.init_options))) {
- if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
- /* Failed to fetch; clean up */
- git_repository_free(repo);
+ if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
+ return retcode;
- if (remove_directory_on_failure)
- git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
- else
- git_futils_cleanupdir_r(local_path);
+ if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
+ goto cleanup;
- } else {
- *out = repo;
- retcode = 0;
- }
- }
+ retcode = git_clone_into(repo, origin, &options.checkout_opts, options.checkout_branch);
+ git_remote_free(origin);
+
+ if (retcode < 0)
+ goto cleanup;
- if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts))
- retcode = git_checkout_head(*out, &normOptions.checkout_opts);
+ *out = repo;
+ return 0;
+
+cleanup:
+ git_repository_free(repo);
+ if (remove_directory_on_failure)
+ git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
+ else
+ git_futils_cleanupdir_r(local_path);
return retcode;
}
diff --git a/src/commit.c b/src/commit.c
index 15a195fe5..91b60bbb2 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -29,7 +29,7 @@ void git_commit__free(void *_commit)
git_signature_free(commit->committer);
git__free(commit->raw_header);
- git__free(commit->message);
+ git__free(commit->raw_message);
git__free(commit->message_encoding);
git__free(commit);
@@ -245,8 +245,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
/* extract commit message */
if (buffer <= buffer_end) {
- commit->message = git__strndup(buffer, buffer_end - buffer);
- GITERR_CHECK_ALLOC(commit->message);
+ commit->raw_message = git__strndup(buffer, buffer_end - buffer);
+ GITERR_CHECK_ALLOC(commit->raw_message);
}
return 0;
@@ -265,7 +265,7 @@ bad_buffer:
GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
-GIT_COMMIT_GETTER(const char *, message, commit->message)
+GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
@@ -273,6 +273,19 @@ GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
+const char *git_commit_message(const git_commit *commit)
+{
+ const char *message = commit->raw_message;
+
+ assert(commit);
+
+ /* trim leading newlines from raw message */
+ while (*message && *message == '\n')
+ ++message;
+
+ return message;
+}
+
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{
assert(commit);
diff --git a/src/commit.h b/src/commit.h
index 22fc898a1..d452e2975 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -24,7 +24,7 @@ struct git_commit {
git_signature *committer;
char *message_encoding;
- char *message;
+ char *raw_message;
char *raw_header;
};
diff --git a/src/config_file.c b/src/config_file.c
index 1a845d8ba..8fb43b990 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -712,7 +712,6 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
int c, rpos;
char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT;
- int quote_marks;
/*
* base_name is what came before the space. We should be at the
* first quotation mark, except for now, line isn't being kept in
@@ -731,21 +730,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
git_buf_printf(&buf, "%s.", base_name);
rpos = 0;
- quote_marks = 0;
line = first_quote;
- c = line[rpos++];
+ c = line[++rpos];
/*
* At the end of each iteration, whatever is stored in c will be
* added to the string. In case of error, jump to out
*/
do {
- if (quote_marks == 2) {
- set_parse_error(reader, rpos, "Unexpected text after closing quotes");
- git_buf_free(&buf);
- return -1;
- }
switch (c) {
case 0:
@@ -754,25 +747,13 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
return -1;
case '"':
- ++quote_marks;
- continue;
+ goto end_parse;
case '\\':
- c = line[rpos++];
-
- switch (c) {
- case '"':
- if (&line[rpos-1] == last_quote) {
- set_parse_error(reader, 0, "Missing closing quotation mark in section header");
- git_buf_free(&buf);
- return -1;
- }
-
- case '\\':
- break;
+ c = line[++rpos];
- default:
- set_parse_error(reader, rpos, "Unsupported escape sequence");
+ if (c == 0) {
+ set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
git_buf_free(&buf);
return -1;
}
@@ -782,7 +763,15 @@ static int parse_section_header_ext(struct reader *reader, const char *line, con
}
git_buf_putc(&buf, (char)c);
- } while ((c = line[rpos++]) != ']');
+ c = line[++rpos];
+ } while (line + rpos < last_quote);
+
+end_parse:
+ if (line[rpos] != '"' || line[rpos + 1] != ']') {
+ set_parse_error(reader, rpos, "Unexpected text after closing quotes");
+ git_buf_free(&buf);
+ return -1;
+ }
*section_name = git_buf_detach(&buf);
return 0;
@@ -800,7 +789,7 @@ static int parse_section_header(struct reader *reader, char **section_out)
return -1;
/* find the end of the variable's name */
- name_end = strchr(line, ']');
+ name_end = strrchr(line, ']');
if (name_end == NULL) {
git__free(line);
set_parse_error(reader, 0, "Missing ']' in section header");
diff --git a/src/fetch.c b/src/fetch.c
index 03fad5fec..5d97913e8 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -119,15 +119,13 @@ int git_fetch_negotiate(git_remote *remote)
remote->refs.length);
}
-int git_fetch_download_pack(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *progress_payload)
+int git_fetch_download_pack(git_remote *remote)
{
git_transport *t = remote->transport;
if(!remote->need_pack)
return 0;
- return t->download_pack(t, remote->repo, &remote->stats, progress_cb, progress_payload);
+ return t->download_pack(t, remote->repo, &remote->stats,
+ remote->callbacks.transfer_progress, remote->callbacks.payload);
}
diff --git a/src/fetch.h b/src/fetch.h
index 059251d04..9605da1b5 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -11,10 +11,7 @@
int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *progress_payload);
+int git_fetch_download_pack(git_remote *remote);
int git_fetch__download_pack(
git_transport *t,
diff --git a/src/indexer.c b/src/indexer.c
index ceb11f0b6..3b160df5d 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -426,7 +426,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1;
- hash_partially(idx, data, size);
+ hash_partially(idx, data, (int)size);
/* Make sure we set the new size of the pack */
if (idx->opened_pack) {
diff --git a/src/netops.c b/src/netops.c
index c1e74546f..7a61ef820 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -573,6 +573,93 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
}
+static const char *prefix_http = "http://";
+static const char *prefix_https = "https://";
+
+int gitno_connection_data_from_url(
+ gitno_connection_data *data,
+ const char *url,
+ const char *service_suffix)
+{
+ int error = -1;
+ const char *default_port = NULL;
+ char *original_host = NULL;
+
+ /* service_suffix is optional */
+ assert(data && url);
+
+ /* Save these for comparison later */
+ original_host = data->host;
+ data->host = NULL;
+ gitno_connection_data_free_ptrs(data);
+
+ if (!git__prefixcmp(url, prefix_http)) {
+ url = url + strlen(prefix_http);
+ default_port = "80";
+
+ if (data->use_ssl) {
+ giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
+ goto cleanup;
+ }
+ }
+
+ if (!git__prefixcmp(url, prefix_https)) {
+ url += strlen(prefix_https);
+ default_port = "443";
+ data->use_ssl = true;
+ }
+
+ if (url[0] == '/')
+ default_port = data->use_ssl ? "443" : "80";
+
+ if (!default_port) {
+ giterr_set(GITERR_NET, "Unrecognized URL prefix");
+ goto cleanup;
+ }
+
+ error = gitno_extract_url_parts(
+ &data->host, &data->port, &data->user, &data->pass,
+ url, default_port);
+
+ if (url[0] == '/') {
+ /* Relative redirect; reuse original host name and port */
+ git__free(data->host);
+ data->host = original_host;
+ original_host = NULL;
+ }
+
+ if (!error) {
+ const char *path = strchr(url, '/');
+ size_t pathlen = strlen(path);
+ size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
+
+ if (suffixlen &&
+ !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
+ data->path = git__strndup(path, pathlen - suffixlen);
+ else
+ data->path = git__strdup(path);
+
+ /* Check for errors in the resulting data */
+ if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
+ giterr_set(GITERR_NET, "Cross host redirect not allowed");
+ error = -1;
+ }
+ }
+
+cleanup:
+ if (original_host) git__free(original_host);
+ return error;
+}
+
+void gitno_connection_data_free_ptrs(gitno_connection_data *d)
+{
+ git__free(d->host); d->host = NULL;
+ git__free(d->port); d->port = NULL;
+ git__free(d->path); d->path = NULL;
+ git__free(d->user); d->user = NULL;
+ git__free(d->pass); d->pass = NULL;
+}
+
int gitno_extract_url_parts(
char **host,
char **port,
diff --git a/src/netops.h b/src/netops.h
index d352bf3b6..5c105d6e3 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -66,6 +66,29 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags);
int gitno_close(gitno_socket *s);
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
+typedef struct gitno_connection_data {
+ char *host;
+ char *port;
+ char *path;
+ char *user;
+ char *pass;
+ bool use_ssl;
+} gitno_connection_data;
+
+/*
+ * This replaces all the pointers in `data` with freshly-allocated strings,
+ * that the caller is responsible for freeing.
+ * `gitno_connection_data_free_ptrs` is good for this.
+ */
+
+int gitno_connection_data_from_url(
+ gitno_connection_data *data,
+ const char *url,
+ const char *service_suffix);
+
+/* This frees all the pointers IN the struct, but not the struct itself. */
+void gitno_connection_data_free_ptrs(gitno_connection_data *data);
+
int gitno_extract_url_parts(
char **host,
char **port,
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 7f427e3bd..2a2f36223 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -14,6 +14,7 @@
#include "pack.h"
#include "thread-utils.h"
#include "tree.h"
+#include "util.h"
#include "git2/pack.h"
#include "git2/commit.h"
@@ -57,6 +58,9 @@ struct pack_write_context {
#define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
+
static unsigned name_hash(const char *name)
{
unsigned c, hash = 0;
@@ -212,6 +216,14 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
assert(ret != 0);
kh_value(pb->object_ix, pos) = po;
+ if (pb->progress_cb) {
+ double current_time = git__timer();
+ if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ pb->last_progress_report_time = current_time;
+ pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload);
+ }
+ }
+
pb->done = false;
return 0;
}
@@ -1207,6 +1219,13 @@ static int prepare_pack(git_packbuilder *pb)
if (pb->nr_objects == 0 || pb->done)
return 0; /* nothing to do */
+ /*
+ * Although we do not report progress during deltafication, we
+ * at least report that we are in the deltafication stage
+ */
+ if (pb->progress_cb)
+ pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
+
delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
GITERR_CHECK_ALLOC(delta_list);
@@ -1348,6 +1367,17 @@ uint32_t git_packbuilder_written(git_packbuilder *pb)
return pb->nr_written;
}
+int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
+{
+ if (!pb)
+ return -1;
+
+ pb->progress_cb = progress_cb;
+ pb->progress_cb_payload = progress_cb_payload;
+
+ return 0;
+}
+
void git_packbuilder_free(git_packbuilder *pb)
{
if (pb == NULL)
diff --git a/src/pack-objects.h b/src/pack-objects.h
index 8e7ba7f78..0c94a5a7a 100644
--- a/src/pack-objects.h
+++ b/src/pack-objects.h
@@ -16,6 +16,7 @@
#include "netops.h"
#include "git2/oid.h"
+#include "git2/pack.h"
#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
#define GIT_PACK_DEPTH 50 /* max delta depth */
@@ -79,6 +80,10 @@ struct git_packbuilder {
int nr_threads; /* nr of threads to use */
+ git_packbuilder_progress progress_cb;
+ void *progress_cb_payload;
+ double last_progress_report_time; /* the time progress was last reported */
+
bool done;
};
diff --git a/src/push.c b/src/push.c
index eaaa46248..698079253 100644
--- a/src/push.c
+++ b/src/push.c
@@ -70,6 +70,25 @@ int git_push_set_options(git_push *push, const git_push_options *opts)
return 0;
}
+int git_push_set_callbacks(
+ git_push *push,
+ git_packbuilder_progress pack_progress_cb,
+ void *pack_progress_cb_payload,
+ git_push_transfer_progress transfer_progress_cb,
+ void *transfer_progress_cb_payload)
+{
+ if (!push)
+ return -1;
+
+ push->pack_progress_cb = pack_progress_cb;
+ push->pack_progress_cb_payload = pack_progress_cb_payload;
+
+ push->transfer_progress_cb = transfer_progress_cb;
+ push->transfer_progress_cb_payload = transfer_progress_cb_payload;
+
+ return 0;
+}
+
static void free_refspec(push_spec *spec)
{
if (spec == NULL)
@@ -583,6 +602,10 @@ static int do_push(git_push *push)
git_packbuilder_set_threads(push->pb, push->pb_parallelism);
+ if (push->pack_progress_cb)
+ if ((error = git_packbuilder_set_callbacks(push->pb, push->pack_progress_cb, push->pack_progress_cb_payload)) < 0)
+ goto on_error;
+
if ((error = calculate_work(push)) < 0 ||
(error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push)) < 0)
diff --git a/src/push.h b/src/push.h
index e982b8385..6c8bf7229 100644
--- a/src/push.h
+++ b/src/push.h
@@ -39,6 +39,11 @@ struct git_push {
/* options */
unsigned pb_parallelism;
+
+ git_packbuilder_progress pack_progress_cb;
+ void *pack_progress_cb_payload;
+ git_push_transfer_progress transfer_progress_cb;
+ void *transfer_progress_cb_payload;
};
/**
diff --git a/src/remote.c b/src/remote.c
index 95b907ff1..ccedf2386 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -591,7 +591,7 @@ int git_remote_connect(git_remote *remote, git_direction direction)
if (!remote->check_cert)
flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
- if (t->connect(t, url, remote->cred_acquire_cb, remote->cred_acquire_payload, direction, flags) < 0)
+ if (t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags) < 0)
goto on_error;
remote->transport = t;
@@ -742,10 +742,7 @@ static int remote_head_cmp(const void *_a, const void *_b)
return git__strcmp_cb(a->name, b->name);
}
-int git_remote_download(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *progress_payload)
+int git_remote_download(git_remote *remote)
{
int error;
git_vector refs;
@@ -767,7 +764,25 @@ int git_remote_download(
if ((error = git_fetch_negotiate(remote)) < 0)
return error;
- return git_fetch_download_pack(remote, progress_cb, progress_payload);
+ return git_fetch_download_pack(remote);
+}
+
+int git_remote_fetch(git_remote *remote)
+{
+ int error;
+
+ /* Connect and download everything */
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0)
+ return error;
+
+ if ((error = git_remote_download(remote)) < 0)
+ return error;
+
+ /* We don't need to be connected anymore */
+ git_remote_disconnect(remote);
+
+ /* Create "remote/foo" branches for all remote branches */
+ return git_remote_update_tips(remote);
}
static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
@@ -1138,7 +1153,7 @@ void git_remote_check_cert(git_remote *remote, int check)
remote->check_cert = check;
}
-int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
+int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
{
assert(remote && callbacks);
@@ -1147,7 +1162,7 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
if (remote->transport && remote->transport->set_callbacks)
- remote->transport->set_callbacks(remote->transport,
+ return remote->transport->set_callbacks(remote->transport,
remote->callbacks.progress,
NULL,
remote->callbacks.payload);
@@ -1155,17 +1170,6 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
return 0;
}
-void git_remote_set_cred_acquire_cb(
- git_remote *remote,
- git_cred_acquire_cb cred_acquire_cb,
- void *payload)
-{
- assert(remote);
-
- remote->cred_acquire_cb = cred_acquire_cb;
- remote->cred_acquire_payload = payload;
-}
-
int git_remote_set_transport(git_remote *remote, git_transport *transport)
{
assert(remote && transport);
diff --git a/src/remote.h b/src/remote.h
index dce4803ed..269584d96 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -21,8 +21,6 @@ struct git_remote {
char *pushurl;
git_vector refs;
git_vector refspecs;
- git_cred_acquire_cb cred_acquire_cb;
- void *cred_acquire_payload;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
diff --git a/src/stash.c b/src/stash.c
index ab4a68575..7742eee19 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -316,6 +316,8 @@ static int build_workdir_tree(
struct cb_data data = {0};
int error;
+ opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
+
if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
goto cleanup;
@@ -474,12 +476,14 @@ static int ensure_there_are_changes_to_stash(
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
if (include_untracked_files)
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
if (include_ignored_files)
- opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
diff --git a/src/transport.c b/src/transport.c
index 354789db1..ff926b1be 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -42,6 +42,8 @@ static transport_definition transports[] = {
{NULL, 0, 0}
};
+static git_vector additional_transports = GIT_VECTOR_INIT;
+
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
@@ -61,6 +63,14 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
definition = definition_iter;
}
+ git_vector_foreach(&additional_transports, i, definition_iter) {
+ if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
+ continue;
+
+ if (definition_iter->priority > priority)
+ definition = definition_iter;
+ }
+
#ifdef GIT_WIN32
/* On Windows, it might not be possible to discern between absolute local
* and ssh paths - first check if this is a valid local path that points
@@ -135,6 +145,62 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url)
return 0;
}
+int git_transport_register(
+ const char *prefix,
+ unsigned priority,
+ git_transport_cb cb,
+ void *param)
+{
+ transport_definition *d;
+
+ d = git__calloc(sizeof(transport_definition), 1);
+ GITERR_CHECK_ALLOC(d);
+
+ d->prefix = git__strdup(prefix);
+
+ if (!d->prefix)
+ goto on_error;
+
+ d->priority = priority;
+ d->fn = cb;
+ d->param = param;
+
+ if (git_vector_insert(&additional_transports, d) < 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ git__free(d->prefix);
+ git__free(d);
+ return -1;
+}
+
+int git_transport_unregister(
+ const char *prefix,
+ unsigned priority)
+{
+ transport_definition *d;
+ unsigned i;
+
+ git_vector_foreach(&additional_transports, i, d) {
+ if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
+ if (git_vector_remove(&additional_transports, i) < 0)
+ return -1;
+
+ git__free(d->prefix);
+ git__free(d);
+
+ if (!additional_transports.length)
+ git_vector_free(&additional_transports);
+
+ return 0;
+ }
+ }
+
+ return GIT_ENOTFOUND;
+}
+
/* from remote.h */
int git_remote_valid_url(const char *url)
{
diff --git a/src/transports/http.c b/src/transports/http.c
index ab2f9a47f..ace0d97d0 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -12,8 +12,6 @@
#include "netops.h"
#include "smart.h"
-static const char *prefix_http = "http://";
-static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
static const char *upload_pack_service_url = "/git-upload-pack";
@@ -59,16 +57,11 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
gitno_socket socket;
- char *path;
- char *host;
- char *port;
- char *user_from_url;
- char *pass_from_url;
+ gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
http_authmechanism_t auth_mechanism;
- unsigned connected : 1,
- use_ssl : 1;
+ bool connected;
/* Parser structures */
http_parser parser;
@@ -125,12 +118,12 @@ static int gen_request(
size_t content_length)
{
http_subtransport *t = OWNING_SUBTRANSPORT(s);
- const char *path = t->path ? t->path : "/";
+ const char *path = t->connection_data.path ? t->connection_data.path : "/";
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
- git_buf_printf(buf, "Host: %s\r\n", t->host);
+ git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
if (s->chunked || content_length > 0) {
git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
@@ -150,9 +143,9 @@ static int gen_request(
return -1;
/* Use url-parsed basic auth if username and password are both provided */
- if (!t->cred && t->user_from_url && t->pass_from_url) {
- if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+ if (!t->cred && t->connection_data.user && t->connection_data.pass) {
+ if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
+ t->connection_data.user, t->connection_data.pass) < 0)
return -1;
if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
}
@@ -249,98 +242,6 @@ static int on_header_value(http_parser *parser, const char *str, size_t len)
return 0;
}
-static void free_connection_data(http_subtransport *t)
-{
- if (t->host) {
- git__free(t->host);
- t->host = NULL;
- }
-
- if (t->port) {
- git__free(t->port);
- t->port = NULL;
- }
-
- if (t->user_from_url) {
- git__free(t->user_from_url);
- t->user_from_url = NULL;
- }
-
- if (t->pass_from_url) {
- git__free(t->pass_from_url);
- t->pass_from_url = NULL;
- }
-
- if (t->path) {
- git__free(t->path);
- t->path = NULL;
- }
-}
-
-static int set_connection_data_from_url(
- http_subtransport *t, const char *url, const char *service_suffix)
-{
- int error = 0;
- const char *default_port = NULL;
- char *original_host = NULL;
-
- if (!git__prefixcmp(url, prefix_http)) {
- url = url + strlen(prefix_http);
- default_port = "80";
-
- if (t->use_ssl) {
- giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
- return -1;
- }
- }
-
- if (!git__prefixcmp(url, prefix_https)) {
- url += strlen(prefix_https);
- default_port = "443";
- t->use_ssl = 1;
- }
-
- if (!default_port) {
- giterr_set(GITERR_NET, "Unrecognized URL prefix");
- return -1;
- }
-
- /* preserve original host name for checking */
- original_host = t->host;
- t->host = NULL;
-
- free_connection_data(t);
-
- error = gitno_extract_url_parts(
- &t->host, &t->port, &t->user_from_url, &t->pass_from_url,
- url, default_port);
-
- if (!error) {
- const char *path = strchr(url, '/');
- size_t pathlen = strlen(path);
- size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
-
- if (suffixlen &&
- !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
- t->path = git__strndup(path, pathlen - suffixlen);
- else
- t->path = git__strdup(path);
-
- /* Allow '/'-led urls, or a change of protocol */
- if (original_host != NULL) {
- if (strcmp(original_host, t->host) && t->location[0] != '/') {
- giterr_set(GITERR_NET, "Cross host redirect not allowed");
- error = -1;
- }
-
- git__free(original_host);
- }
- }
-
- return error;
-}
-
-
static int on_headers_complete(http_parser *parser)
{
parser_context *ctx = (parser_context *) parser->data;
@@ -369,7 +270,7 @@ static int on_headers_complete(http_parser *parser)
if (t->owner->cred_acquire_cb(&t->cred,
t->owner->url,
- t->user_from_url,
+ t->connection_data.user,
allowed_types,
t->owner->cred_acquire_payload) < 0)
return PARSE_ERROR_GENERIC;
@@ -384,17 +285,17 @@ static int on_headers_complete(http_parser *parser)
/* Check for a redirect.
* Right now we only permit a redirect to the same hostname. */
if ((parser->status_code == 301 ||
- parser->status_code == 302 ||
- (parser->status_code == 303 && get_verb == s->verb) ||
- parser->status_code == 307) &&
- t->location) {
+ parser->status_code == 302 ||
+ (parser->status_code == 303 && get_verb == s->verb) ||
+ parser->status_code == 307) &&
+ t->location) {
if (s->redirect_count >= 7) {
giterr_set(GITERR_NET, "Too many redirects");
return t->parse_error = PARSE_ERROR_GENERIC;
}
- if (set_connection_data_from_url(t, t->location, s->service_url) < 0)
+ if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
/* Set the redirect URL on the stream. This is a transfer of
@@ -552,7 +453,7 @@ static int http_connect(http_subtransport *t)
if (t->socket.socket)
gitno_close(&t->socket);
- if (t->use_ssl) {
+ if (t->connection_data.use_ssl) {
int tflags;
if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
@@ -564,7 +465,7 @@ static int http_connect(http_subtransport *t)
flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
}
- if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
+ if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
return -1;
t->connected = 1;
@@ -911,10 +812,9 @@ static int http_action(
if (!stream)
return -1;
- if (!t->host || !t->port || !t->path) {
- if ((ret = set_connection_data_from_url(t, url, NULL)) < 0)
- return ret;
- }
+ if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
+ (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
+ return ret;
if (http_connect(t) < 0)
return -1;
@@ -958,7 +858,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL;
}
- free_connection_data(t);
+ gitno_connection_data_free_ptrs(&t->connection_data);
return 0;
}
diff --git a/src/transports/local.c b/src/transports/local.c
index 9ebea979c..3c1f98804 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -434,7 +434,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
- push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
+ push->remote->callbacks.credentials, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error;
}
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 0cd5e831d..156b69e1f 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -13,8 +13,11 @@
#include "push.h"
#include "pack-objects.h"
#include "remote.h"
+#include "util.h"
#define NETWORK_XFER_THRESHOLD (100*1024)
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
int git_smart__store_refs(transport_smart *t, int flushes)
{
@@ -801,22 +804,53 @@ static int update_refs_from_report(
return 0;
}
+struct push_packbuilder_payload
+{
+ git_smart_subtransport_stream *stream;
+ git_packbuilder *pb;
+ git_push_transfer_progress cb;
+ void *cb_payload;
+ size_t last_bytes;
+ double last_progress_report_time;
+};
+
static int stream_thunk(void *buf, size_t size, void *data)
{
- git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
+ int error = 0;
+ struct push_packbuilder_payload *payload = data;
+
+ if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
+ return error;
+
+ if (payload->cb) {
+ double current_time = git__timer();
+ payload->last_bytes += size;
- return s->write(s, (const char *)buf, size);
+ if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ payload->last_progress_report_time = current_time;
+ payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
+ }
+ }
+
+ return error;
}
int git_smart__push(git_transport *transport, git_push *push)
{
transport_smart *t = (transport_smart *)transport;
- git_smart_subtransport_stream *s;
+ struct push_packbuilder_payload packbuilder_payload = {0};
git_buf pktline = GIT_BUF_INIT;
int error = -1, need_pack = 0;
push_spec *spec;
unsigned int i;
+ packbuilder_payload.pb = push->pb;
+
+ if (push->transfer_progress_cb) {
+ packbuilder_payload.cb = push->transfer_progress_cb;
+ packbuilder_payload.cb_payload = push->transfer_progress_cb_payload;
+ }
+
#ifdef PUSH_DEBUG
{
git_remote_head *head;
@@ -848,12 +882,12 @@ int git_smart__push(git_transport *transport, git_push *push)
}
}
- if (git_smart__get_push_stream(t, &s) < 0 ||
+ if (git_smart__get_push_stream(t, &packbuilder_payload.stream) < 0 ||
gen_pktline(&pktline, push) < 0 ||
- s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
+ packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
goto on_error;
- if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
+ if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload) < 0)
goto on_error;
/* If we sent nothing or the server doesn't support report-status, then
@@ -863,6 +897,11 @@ int git_smart__push(git_transport *transport, git_push *push)
else if (parse_report(&t->buffer, push) < 0)
goto on_error;
+ /* If progress is being reported write the final report */
+ if (push->transfer_progress_cb) {
+ push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
+ }
+
if (push->status.length &&
update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
goto on_error;
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 377f2ef97..067d6fcc3 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -73,17 +73,12 @@ typedef struct {
typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
- char *path;
- char *host;
- char *port;
- char *user_from_url;
- char *pass_from_url;
+ gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
int auth_mechanism;
HINTERNET session;
HINTERNET connection;
- unsigned use_ssl : 1;
} winhttp_subtransport;
static int apply_basic_credential(HINTERNET request, git_cred *cred)
@@ -155,7 +150,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */
- git_buf_printf(&buf, "%s%s", t->path, s->service_url);
+ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
if (git_buf_oom(&buf))
return -1;
@@ -188,7 +183,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
NULL,
WINHTTP_NO_REFERER,
types,
- t->use_ssl ? WINHTTP_FLAG_SECURE : 0);
+ t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
if (!s->request) {
giterr_set(GITERR_OS, "Failed to open request");
@@ -196,7 +191,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, !!t->use_ssl, &proxy_url) < 0)
+ if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
goto on_error;
if (proxy_url) {
@@ -285,7 +280,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* If requested, disable certificate validation */
- if (t->use_ssl) {
+ if (t->connection_data.use_ssl) {
int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
@@ -308,9 +303,9 @@ static int winhttp_stream_connect(winhttp_stream *s)
/* If no other credentials have been applied and the URL has username and
* password, use those */
- if (!t->cred && t->user_from_url && t->pass_from_url) {
+ if (!t->cred && t->connection_data.user && t->connection_data.pass) {
if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+ git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
goto on_error;
if (apply_basic_credential(s->request, t->url_cred) < 0)
goto on_error;
@@ -392,98 +387,6 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0;
}
-static void free_connection_data(winhttp_subtransport *t)
-{
- if (t->host) {
- git__free(t->host);
- t->host = NULL;
- }
-
- if (t->port) {
- git__free(t->port);
- t->port = NULL;
- }
-
- if (t->user_from_url) {
- git__free(t->user_from_url);
- t->user_from_url = NULL;
- }
-
- if (t->pass_from_url) {
- git__free(t->pass_from_url);
- t->pass_from_url = NULL;
- }
-
- if (t->path) {
- git__free(t->path);
- t->path = NULL;
- }
-}
-
-static int set_connection_data_from_url(
- winhttp_subtransport *t, const char *url, const char *service_suffix)
-{
- int error = 0;
- const char *default_port = NULL;
- char *original_host = NULL;
- const char *original_url = url;
-
- if (!git__prefixcmp(url, prefix_http)) {
- url += strlen(prefix_http);
- default_port = "80";
-
- if (t->use_ssl) {
- giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP not allowed");
- return -1;
- }
- }
-
- if (!git__prefixcmp(url, prefix_https)) {
- url += strlen(prefix_https);
- default_port = "443";
- t->use_ssl = 1;
- }
-
- if (!default_port) {
- giterr_set(GITERR_NET, "Unrecognized URL prefix");
- return -1;
- }
-
- /* preserve original host name for checking */
- original_host = t->host;
- t->host = NULL;
-
- free_connection_data(t);
-
- error = gitno_extract_url_parts(
- &t->host, &t->port, &t->user_from_url, &t->pass_from_url,
- url, default_port);
-
- if (!error) {
- const char *path = strchr(url, '/');
- size_t pathlen = strlen(path);
- size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
-
- if (suffixlen &&
- !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen))
- t->path = git__strndup(path, pathlen - suffixlen);
- else
- t->path = git__strdup(path);
-
- /* Allow '/'-led urls, or a change of protocol */
- if (original_host != NULL) {
- if (strcmp(original_host, t->host) && original_url[0] != '/') {
- giterr_set(GITERR_NET, "Cross host redirect not allowed");
- error = -1;
- }
-
- git__free(original_host);
- }
- }
-
- return error;
-}
-
static int winhttp_connect(
winhttp_subtransport *t,
const char *url)
@@ -494,11 +397,11 @@ static int winhttp_connect(
const char *default_port = "80";
/* Prepare port */
- if (git__strtol32(&port, t->port, NULL, 10) < 0)
+ if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
return -1;
/* Prepare host */
- git_win32_path_from_c(host, t->host);
+ git_win32_path_from_c(host, t->connection_data.host);
/* Establish session */
t->session = WinHttpOpen(
@@ -699,7 +602,8 @@ replay:
if (!git__prefixcmp_icase(location8, prefix_https)) {
/* Upgrade to secure connection; disconnect and start over */
- set_connection_data_from_url(t, location8, s->service_url);
+ if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0)
+ return -1;
winhttp_connect(t, location8);
}
@@ -718,7 +622,8 @@ replay:
if (allowed_types &&
(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
- if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0)
+ if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types,
+ t->owner->cred_acquire_payload) < 0)
return -1;
assert(t->cred);
@@ -1101,10 +1006,10 @@ static int winhttp_action(
winhttp_stream *s;
int ret = -1;
- if (!t->connection &&
- (set_connection_data_from_url(t, url, NULL) < 0 ||
- winhttp_connect(t, url) < 0))
- return -1;
+ if (!t->connection)
+ if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 ||
+ winhttp_connect(t, url) < 0)
+ return -1;
if (winhttp_stream_alloc(t, &s) < 0)
return -1;
@@ -1145,7 +1050,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0;
- free_connection_data(t);
+ gitno_connection_data_free_ptrs(&t->connection_data);
if (t->cred) {
t->cred->free(t->cred);
diff --git a/src/util.h b/src/util.h
index bd93b46b5..f9de909e9 100644
--- a/src/util.h
+++ b/src/util.h
@@ -353,4 +353,65 @@ GIT_INLINE(void) git__memzero(void *data, size_t size)
#endif
}
+#ifdef GIT_WIN32
+
+GIT_INLINE(double) git__timer(void)
+{
+ /* We need the initial tick count to detect if the tick
+ * count has rolled over. */
+ static DWORD initial_tick_count = 0;
+
+ /* GetTickCount returns the number of milliseconds that have
+ * elapsed since the system was started. */
+ DWORD count = GetTickCount();
+
+ if(initial_tick_count == 0) {
+ initial_tick_count = count;
+ } else if (count < initial_tick_count) {
+ /* The tick count has rolled over - adjust for it. */
+ count = (0xFFFFFFFF - initial_tick_count) + count;
+ }
+
+ return (double) count / (double) 1000;
+}
+
+#elif __APPLE__
+
+#include <mach/mach_time.h>
+
+GIT_INLINE(double) git__timer(void)
+{
+ uint64_t time = mach_absolute_time();
+ static double scaling_factor = 0;
+
+ if (scaling_factor == 0) {
+ mach_timebase_info_data_t info;
+ (void)mach_timebase_info(&info);
+ scaling_factor = (double)info.numer / (double)info.denom;
+ }
+
+ return (double)time * scaling_factor / 1.0E-9;
+}
+
+#else
+
+#include <sys/time.h>
+
+GIT_INLINE(double) git__timer(void)
+{
+ struct timespec tp;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+ return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9;
+ } else {
+ /* Fall back to using gettimeofday */
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday(&tv, &tz);
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6;
+ }
+}
+
+#endif
+
#endif /* INCLUDE_util_h__ */