summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--examples/blame.c2
-rw-r--r--examples/network/index-pack.c2
-rw-r--r--examples/network/ls-remote.c26
-rw-r--r--include/git2/indexer.h2
-rw-r--r--include/git2/pack.h12
-rw-r--r--include/git2/remote.h52
-rw-r--r--include/git2/transport.h12
-rw-r--r--src/blame.c18
-rw-r--r--src/clone.c41
-rw-r--r--src/config_file.c26
-rw-r--r--src/fetch.c59
-rw-r--r--src/fetchhead.c6
-rw-r--r--src/indexer.c7
-rw-r--r--src/merge.c4
-rw-r--r--src/netops.c99
-rw-r--r--src/netops.h1
-rw-r--r--src/odb_pack.c2
-rw-r--r--src/pack-objects.c17
-rw-r--r--src/push.c20
-rw-r--r--src/refspec.c68
-rw-r--r--src/refspec.h8
-rw-r--r--src/remote.c205
-rw-r--r--src/remote.h1
-rw-r--r--src/signature.c6
-rw-r--r--src/transports/git.c72
-rw-r--r--src/transports/local.c10
-rw-r--r--src/transports/smart.c44
-rw-r--r--src/transports/smart.h3
-rw-r--r--src/transports/smart_protocol.c26
-rw-r--r--src/transports/ssh.c6
-rw-r--r--tests-clar/blame/buffer.c36
-rw-r--r--tests-clar/config/write.c31
-rw-r--r--tests-clar/merge/workdir/setup.c3
-rw-r--r--tests-clar/merge/workdir/trivial.c2
-rw-r--r--tests-clar/network/remote/local.c51
-rw-r--r--tests-clar/network/urlparse.c61
-rw-r--r--tests-clar/online/fetch.c19
-rw-r--r--tests-clar/online/push.c15
-rw-r--r--tests-clar/online/push_util.c40
-rw-r--r--tests-clar/online/push_util.h14
-rw-r--r--tests-clar/pack/indexer.c6
-rw-r--r--tests-clar/pack/packbuilder.c60
-rw-r--r--tests-clar/repo/config.c2
44 files changed, 777 insertions, 422 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2440ed0b8..271af690c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -128,6 +128,8 @@ INCLUDE_DIRECTORIES(src include)
IF (WIN32 AND WINHTTP AND NOT MINGW)
ADD_DEFINITIONS(-DGIT_WINHTTP)
+ INCLUDE_DIRECTORIES(deps/http-parser)
+ FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
ELSE ()
IF (NOT AMIGA)
FIND_PACKAGE(OpenSSL)
diff --git a/examples/blame.c b/examples/blame.c
index 95bce6b9c..06310d540 100644
--- a/examples/blame.c
+++ b/examples/blame.c
@@ -114,7 +114,7 @@ int main(int argc, char *argv[])
rawdata+i);
}
- i = eol - rawdata + 1;
+ i = (int)(eol - rawdata + 1);
line++;
}
diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c
index 59fff416a..314f21160 100644
--- a/examples/network/index-pack.c
+++ b/examples/network/index-pack.c
@@ -46,7 +46,7 @@ int index_pack(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
- if (git_indexer_new(&idx, ".", NULL, NULL, NULL) < 0) {
+ if (git_indexer_new(&idx, ".", 0, NULL, NULL, NULL) < 0) {
puts("bad idx");
return -1;
}
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c
index 18cd02367..1e08b293e 100644
--- a/examples/network/ls-remote.c
+++ b/examples/network/ls-remote.c
@@ -4,21 +4,12 @@
#include <string.h>
#include "common.h"
-/** Callback to show each item */
-static int show_ref__cb(git_remote_head *head, void *payload)
-{
- char oid[GIT_OID_HEXSZ + 1] = {0};
-
- (void)payload;
- git_oid_fmt(oid, &head->oid);
- printf("%s\t%s\n", oid, head->name);
- return 0;
-}
-
static int use_remote(git_repository *repo, char *name)
{
git_remote *remote = NULL;
int error;
+ const git_remote_head **refs;
+ size_t refs_len, i;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
// Find the remote by name
@@ -40,7 +31,18 @@ static int use_remote(git_repository *repo, char *name)
if (error < 0)
goto cleanup;
- error = git_remote_ls(remote, &show_ref__cb, NULL);
+ /**
+ * Get the list of references on the remote and print out
+ * their name next to what they point to.
+ */
+ if (git_remote_ls(&refs, &refs_len, remote) < 0)
+ goto cleanup;
+
+ for (i = 0; i < refs_len; i++) {
+ char oid[GIT_OID_HEXSZ + 1] = {0};
+ git_oid_fmt(oid, &refs[i]->oid);
+ printf("%s\t%s\n", oid, refs[i]->name);
+ }
cleanup:
git_remote_free(remote);
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index fb55672a9..e4c03ad06 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -20,6 +20,7 @@ typedef struct git_indexer git_indexer;
*
* @param out where to store the indexer instance
* @param path to the directory where the packfile should be stored
+ * @param mode permissions to use creating packfile or 0 for defaults
* @param odb object database from which to read base objects when
* fixing thin packs. Pass NULL if no thin pack is expected (an error
* will be returned if there are bases missing)
@@ -29,6 +30,7 @@ typedef struct git_indexer git_indexer;
GIT_EXTERN(int) git_indexer_new(
git_indexer **out,
const char *path,
+ unsigned int mode,
git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_cb_payload);
diff --git a/include/git2/pack.h b/include/git2/pack.h
index 748817666..52e7adad2 100644
--- a/include/git2/pack.h
+++ b/include/git2/pack.h
@@ -119,6 +119,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
*
* @param pb The packbuilder
* @param path to the directory where the packfile and index should be stored
+ * @param mode permissions to use creating a packfile or 0 for defaults
* @param progress_cb function to call with progress information from the indexer (optional)
* @param progress_cb_payload payload for the progress callback (optional)
*
@@ -127,9 +128,20 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
GIT_EXTERN(int) git_packbuilder_write(
git_packbuilder *pb,
const char *path,
+ unsigned int mode,
git_transfer_progress_callback progress_cb,
void *progress_cb_payload);
+/**
+* Get the packfile's hash
+*
+* A packfile's name is derived from the sorted hashing of all object
+* names. This is only correct after the packfile has been written.
+*
+* @param pb The packbuilder object
+*/
+GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb);
+
typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
/**
* Create the new pack and pass each object to the callback
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 559b8dcc1..7410909dc 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -73,7 +73,7 @@ GIT_EXTERN(int) git_remote_create_with_fetchspec(
*
* @param out pointer to the new remote object
* @param repo the associated repository
- * @param fetch the fetch refspec to use for this remote. May be NULL for defaults.
+ * @param fetch the fetch refspec to use for this remote.
* @param url the remote repository's URL
* @return 0 or an error code
*/
@@ -164,8 +164,11 @@ GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
/**
* Add a fetch refspec to the remote
*
+ * Convenience function for adding a single fetch refspec to the
+ * current list in the remote.
+ *
* @param remote the remote
- * @apram refspec the new fetch refspec
+ * @param refspec the new fetch refspec
* @return 0 or an error value
*/
GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
@@ -182,8 +185,21 @@ GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
/**
+ * Set the remote's list of fetch refspecs
+ *
+ * The contents of the string array are copied.
+ *
+ * @param remote the remote to modify
+ * @param array the new list of fetch resfpecs
+ */
+GIT_EXTERN(int) git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array);
+
+/**
* Add a push refspec to the remote
*
+ * Convenience function for adding a single push refspec to the
+ * current list in the remote.
+ *
* @param remote the remote
* @param refspec the new push refspec
* @return 0 or an error value
@@ -202,6 +218,16 @@ GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec);
GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
/**
+ * Set the remote's list of push refspecs
+ *
+ * The contents of the string array are copied.
+ *
+ * @param remote the remote to modify
+ * @param array the new list of push resfpecs
+ */
+GIT_EXTERN(int) git_remote_set_push_refspecs(git_remote *remote, git_strarray *array);
+
+/**
* Clear the refspecs
*
* Remove all configured fetch and push refspecs from the remote.
@@ -228,15 +254,6 @@ GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote);
GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n);
/**
- * Remove a refspec from the remote
- *
- * @param remote the remote to query
- * @param n the refspec to remove
- * @return 0 or GIT_ENOTFOUND
- */
-GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n);
-
-/**
* Open a connection to a remote
*
* The transport is selected based on the URL. The direction argument
@@ -256,15 +273,16 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
* The remote (or more exactly its transport) must be connected. The
* memory belongs to the remote.
*
- * If you a return a non-zero value from the callback, this will stop
- * looping over the refs.
+ * The array will stay valid as long as the remote object exists and
+ * its transport isn't changed, but a copy is recommended for usage of
+ * the data.
*
+ * @param out pointer to the array
+ * @param size the number of remote heads
* @param remote the remote
- * @param list_cb function to call with each ref discovered at the remote
- * @param payload additional data to pass to the callback
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * @return 0 on success, or an error code
*/
-GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
+GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote);
/**
* Download and index the packfile
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 147b3fdcb..81ebf4dc9 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -203,13 +203,13 @@ struct git_transport {
int direction,
int flags);
- /* This function may be called after a successful call to connect(). The
- * provided callback is invoked for each ref discovered on the remote
- * end. */
+ /* This function may be called after a successful call to
+ * connect(). The array returned is owned by the transport and
+ * is guranteed until the next call of a transport function. */
int (*ls)(
- git_transport *transport,
- git_headlist_cb list_cb,
- void *payload);
+ const git_remote_head ***out,
+ size_t *size,
+ git_transport *transport);
/* Executes the push whose context is in the git_push object. */
int (*push)(git_transport *transport, git_push *push);
diff --git a/src/blame.c b/src/blame.c
index 01d3fc03c..219a6bfe4 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -75,6 +75,9 @@ static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
hunk->orig_path);
git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
+ newhunk->boundary = hunk->boundary;
+ newhunk->final_signature = git_signature_dup(hunk->final_signature);
+ newhunk->orig_signature = git_signature_dup(hunk->orig_signature);
return newhunk;
}
@@ -381,9 +384,13 @@ static int buffer_hunk_cb(
wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start;
blame->current_diff_line = wedge_line;
- /* If this hunk doesn't start between existing hunks, split a hunk up so it does */
blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
- if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
+ if (!blame->current_hunk) {
+ /* Line added at the end of the file */
+ blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path);
+ git_vector_insert(&blame->hunks, blame->current_hunk);
+ } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
+ /* If this hunk doesn't start between existing hunks, split a hunk up so it does */
blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
wedge_line - blame->current_hunk->orig_start_line_number, true);
}
@@ -404,13 +411,6 @@ static int buffer_line_cb(
GIT_UNUSED(hunk);
GIT_UNUSED(line);
-#ifdef DO_DEBUG
- {
- char *str = git__substrdup(content, content_len);
- git__free(str);
- }
-#endif
-
if (line->origin == GIT_DIFF_LINE_ADDITION) {
if (hunk_is_bufferblame(blame->current_hunk) &&
hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
diff --git a/src/clone.c b/src/clone.c
index 657243945..23aacd478 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -176,25 +176,20 @@ static int update_head_to_new_branch(
return error;
}
-static int get_head_callback(git_remote_head *head, void *payload)
-{
- git_remote_head **destination = (git_remote_head **)payload;
-
- /* Save the first entry, and terminate the enumeration */
- *destination = head;
- return 1;
-}
-
static int update_head_to_remote(git_repository *repo, git_remote *remote)
{
int retcode = -1;
+ size_t refs_len;
git_refspec dummy_spec;
- git_remote_head *remote_head;
+ const git_remote_head *remote_head, **refs;
struct head_info head_info;
git_buf remote_master_name = GIT_BUF_INIT;
+ if (git_remote_ls(&refs, &refs_len, remote) < 0)
+ return -1;
+
/* Did we just clone an empty repository? */
- if (remote->refs.length == 0) {
+ if (refs_len == 0) {
return setup_tracking_config(
repo,
"master",
@@ -202,12 +197,8 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
GIT_REFS_HEADS_MASTER_FILE);
}
- /* Get the remote's HEAD. This is always the first ref in remote->refs. */
- remote_head = NULL;
-
- if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
- return -1;
-
+ /* Get the remote's HEAD. This is always the first ref in the list. */
+ remote_head = refs[0];
assert(remote_head);
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
@@ -349,7 +340,7 @@ static bool should_checkout(
int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
{
int error = 0, old_fetchhead;
- size_t nspecs;
+ git_strarray refspecs;
assert(repo && remote);
@@ -358,6 +349,10 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_
return -1;
}
+
+ if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
+ return error;
+
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
return error;
@@ -378,9 +373,13 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_
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);
+ /* Go back to the original refspecs */
+ if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) {
+ git_strarray_free(&refspecs);
+ return -1;
+ }
+
+ git_strarray_free(&refspecs);
return error;
}
diff --git a/src/config_file.c b/src/config_file.c
index 0bd4e4ece..15c8de49c 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1178,6 +1178,24 @@ static int write_section(git_filebuf *file, const char *key)
return result;
}
+static const char *quotes_for_value(const char *value)
+{
+ const char *ptr;
+
+ if (value[0] == ' ' || value[0] == '\0')
+ return "\"";
+
+ for (ptr = value; *ptr; ++ptr) {
+ if (*ptr == ';' || *ptr == '#')
+ return "\"";
+ }
+
+ if (ptr[-1] == ' ')
+ return "\"";
+
+ return "";
+}
+
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
@@ -1302,7 +1320,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
/* Then replace the variable. If the value is NULL, it
* means we want to delete it, so don't write anything. */
if (value != NULL) {
- git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+ const char *q = quotes_for_value(value);
+ git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
}
/*
@@ -1343,6 +1362,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
if (preg_replaced) {
git_filebuf_printf(&file, "\n%s", write_start);
} else {
+ const char *q;
+
git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
/* And now if we just need to add a variable */
@@ -1362,7 +1383,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
git_filebuf_write(&file, "\n", 1);
- git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+ q = quotes_for_value(value);
+ git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
}
}
diff --git a/src/fetch.c b/src/fetch.c
index 5d97913e8..276591821 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -19,55 +19,47 @@
#include "repository.h"
#include "refs.h"
-struct filter_payload {
- git_remote *remote;
- const git_refspec *spec, *tagspec;
- git_odb *odb;
- int found_head;
-};
-
-static int filter_ref__cb(git_remote_head *head, void *payload)
+static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec)
{
- struct filter_payload *p = payload;
int match = 0;
if (!git_reference_is_valid_name(head->name))
return 0;
- if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
- p->found_head = 1;
- else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
+ if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
/*
* If tagopt is --tags, then we only use the default
* tags refspec and ignore the remote's
*/
- if (git_refspec_src_matches(p->tagspec, head->name))
+ if (git_refspec_src_matches(tagspec, head->name))
match = 1;
else
return 0;
- } else if (git_remote__matching_refspec(p->remote, head->name))
+ } else if (git_remote__matching_refspec(remote, head->name))
match = 1;
if (!match)
return 0;
/* If we have the object, mark it so we don't ask for it */
- if (git_odb_exists(p->odb, &head->oid))
+ if (git_odb_exists(odb, &head->oid))
head->local = 1;
else
- p->remote->need_pack = 1;
+ remote->need_pack = 1;
- return git_vector_insert(&p->remote->refs, head);
+ return git_vector_insert(&remote->refs, head);
}
static int filter_wants(git_remote *remote)
{
- struct filter_payload p;
- git_refspec tagspec;
- int error = -1;
+ git_remote_head **heads;
+ git_refspec tagspec, head;
+ int error = 0;
+ git_odb *odb;
+ size_t i, heads_len;
git_vector_clear(&remote->refs);
- if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+ if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0)
return error;
/*
@@ -76,14 +68,27 @@ static int filter_wants(git_remote *remote)
* not interested in any particular branch but just the remote's
* HEAD, which will be stored in FETCH_HEAD after the fetch.
*/
- p.tagspec = &tagspec;
- p.found_head = 0;
- p.remote = remote;
+ if (remote->active_refspecs.length == 0) {
+ if ((error = git_refspec__parse(&head, "HEAD", true)) < 0)
+ goto cleanup;
- if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
+ error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs);
+ git_refspec__free(&head);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
+ goto cleanup;
+
+ if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
goto cleanup;
- error = git_remote_ls(remote, filter_ref__cb, &p);
+ for (i = 0; i < heads_len; i++) {
+ if ((error = maybe_want(remote, heads[i], odb, &tagspec)) < 0)
+ break;
+ }
cleanup:
git_refspec__free(&tagspec);
@@ -106,7 +111,7 @@ int git_fetch_negotiate(git_remote *remote)
}
/* Don't try to negotiate when we don't want anything */
- if (remote->refs.length == 0 || !remote->need_pack)
+ if (!remote->need_pack)
return 0;
/*
diff --git a/src/fetchhead.c b/src/fetchhead.c
index 9672623ff..67089d13d 100644
--- a/src/fetchhead.c
+++ b/src/fetchhead.c
@@ -74,6 +74,7 @@ static int fetchhead_ref_write(
{
char oid[GIT_OID_HEXSZ + 1];
const char *type, *name;
+ int head = 0;
assert(file && fetchhead_ref);
@@ -87,11 +88,16 @@ static int fetchhead_ref_write(
GIT_REFS_TAGS_DIR) == 0) {
type = "tag ";
name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
+ } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
+ head = 1;
} else {
type = "";
name = fetchhead_ref->ref_name;
}
+ if (head)
+ return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
+
return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
oid,
(fetchhead_ref->is_merge) ? "" : "not-for-merge",
diff --git a/src/indexer.c b/src/indexer.c
index 90fb52187..df1ce7cfb 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -37,6 +37,7 @@ struct git_indexer {
struct git_pack_header hdr;
struct git_pack_file *pack;
git_filebuf pack_file;
+ unsigned int mode;
git_off_t off;
git_off_t entry_start;
git_packfile_stream stream;
@@ -119,6 +120,7 @@ static int objects_cmp(const void *a, const void *b)
int git_indexer_new(
git_indexer **out,
const char *prefix,
+ unsigned int mode,
git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_payload)
@@ -133,6 +135,7 @@ int git_indexer_new(
idx->odb = odb;
idx->progress_cb = progress_cb;
idx->progress_payload = progress_payload;
+ idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
git_hash_ctx_init(&idx->trailer);
error = git_buf_joinpath(&path, prefix, suff);
@@ -141,7 +144,7 @@ int git_indexer_new(
error = git_filebuf_open(&idx->pack_file, path.ptr,
GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
- GIT_PACK_FILE_MODE);
+ idx->mode);
git_buf_free(&path);
if (error < 0)
goto cleanup;
@@ -905,7 +908,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
return -1;
if (git_filebuf_open(&index_file, filename.ptr,
- GIT_FILEBUF_HASH_CONTENTS, GIT_PACK_FILE_MODE) < 0)
+ GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
goto on_error;
/* Write out the header */
diff --git a/src/merge.c b/src/merge.c
index 2cbf531f9..115867971 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -1649,7 +1649,7 @@ static int write_merge_head(
goto cleanup;
for (i = 0; i < heads_len; i++) {
- if ((error = git_filebuf_printf(&file, "%s\n", &heads[i]->oid_str)) < 0)
+ if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->oid_str)) < 0)
goto cleanup;
}
@@ -2240,6 +2240,8 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
int error = 0;
+ GIT_UNUSED(index_new);
+
*conflicts = 0;
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
diff --git a/src/netops.c b/src/netops.c
index 7e13f12e7..15ed0fc26 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -32,6 +32,7 @@
#include "netops.h"
#include "posix.h"
#include "buffer.h"
+#include "http_parser.h"
#ifdef GIT_WIN32
static void net_set_error(const char *str)
@@ -582,7 +583,7 @@ int gitno_connection_data_from_url(
const char *service_suffix)
{
int error = -1;
- const char *default_port = NULL;
+ const char *default_port = NULL, *path_search_start = NULL;
char *original_host = NULL;
/* service_suffix is optional */
@@ -594,22 +595,18 @@ int gitno_connection_data_from_url(
gitno_connection_data_free_ptrs(data);
if (!git__prefixcmp(url, prefix_http)) {
- url = url + strlen(prefix_http);
+ path_search_start = 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);
+ } else if (!git__prefixcmp(url, prefix_https)) {
+ path_search_start = url + strlen(prefix_https);
default_port = "443";
data->use_ssl = true;
- }
-
- if (url[0] == '/')
+ } else if (url[0] == '/')
default_port = data->use_ssl ? "443" : "80";
if (!default_port) {
@@ -618,18 +615,19 @@ int gitno_connection_data_from_url(
}
error = gitno_extract_url_parts(
- &data->host, &data->port, &data->user, &data->pass,
+ &data->host, &data->port, &data->path, &data->user, &data->pass,
url, default_port);
if (url[0] == '/') {
/* Relative redirect; reuse original host name and port */
+ path_search_start = url;
git__free(data->host);
data->host = original_host;
original_host = NULL;
}
if (!error) {
- const char *path = strchr(url, '/');
+ const char *path = strchr(path_search_start, '/');
size_t pathlen = strlen(path);
size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
@@ -660,55 +658,74 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d)
git__free(d->pass); d->pass = NULL;
}
+#define hex2c(c) ((c | 32) % 39 - 9)
+static char* unescape(char *str)
+{
+ int x, y;
+ int len = (int)strlen(str);
+
+ for (x=y=0; str[y]; ++x, ++y) {
+ if ((str[x] = str[y]) == '%') {
+ if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
+ str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
+ y += 2;
+ }
+ }
+ }
+ str[x] = '\0';
+ return str;
+}
+
int gitno_extract_url_parts(
char **host,
char **port,
+ char **path,
char **username,
char **password,
const char *url,
const char *default_port)
{
- char *colon, *slash, *at, *end;
- const char *start;
-
- /*
- * ==> [user[:pass]@]hostname.tld[:port]/resource
- */
-
- colon = strchr(url, ':');
- slash = strchr(url, '/');
- at = strchr(url, '@');
+ struct http_parser_url u = {0};
+ const char *_host, *_port, *_path, *_userinfo;
- if (!slash ||
- (colon && (slash < colon))) {
- giterr_set(GITERR_NET, "Malformed URL");
+ if (http_parser_parse_url(url, strlen(url), false, &u)) {
+ giterr_set(GITERR_NET, "Malformed URL '%s'", url);
return GIT_EINVALIDSPEC;
}
- start = url;
- if (at && at < slash) {
- start = at+1;
- *username = git__substrdup(url, at - url);
- }
+ _host = url+u.field_data[UF_HOST].off;
+ _port = url+u.field_data[UF_PORT].off;
+ _path = url+u.field_data[UF_PATH].off;
+ _userinfo = url+u.field_data[UF_USERINFO].off;
- if (colon && colon < at) {
- git__free(*username);
- *username = git__substrdup(url, colon-url);
- *password = git__substrdup(colon+1, at-colon-1);
- colon = strchr(at, ':');
+ if (u.field_set & (1 << UF_HOST)) {
+ *host = git__substrdup(_host, u.field_data[UF_HOST].len);
+ GITERR_CHECK_ALLOC(*host);
}
- if (colon == NULL) {
+ if (u.field_set & (1 << UF_PORT))
+ *port = git__substrdup(_port, u.field_data[UF_PORT].len);
+ else
*port = git__strdup(default_port);
- } else {
- *port = git__substrdup(colon + 1, slash - colon - 1);
- }
GITERR_CHECK_ALLOC(*port);
- end = colon == NULL ? slash : colon;
+ if (u.field_set & (1 << UF_PATH)) {
+ *path = git__substrdup(_path, u.field_data[UF_PATH].len);
+ GITERR_CHECK_ALLOC(*path);
+ }
- *host = git__substrdup(start, end - start);
- GITERR_CHECK_ALLOC(*host);
+ if (u.field_set & (1 << UF_USERINFO)) {
+ const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
+ if (colon) {
+ *username = unescape(git__substrdup(_userinfo, colon - _userinfo));
+ *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
+ GITERR_CHECK_ALLOC(*password);
+ } else {
+ *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
+ }
+ GITERR_CHECK_ALLOC(*username);
+
+ }
return 0;
}
diff --git a/src/netops.h b/src/netops.h
index 5c105d6e3..666d66b12 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -92,6 +92,7 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *data);
int gitno_extract_url_parts(
char **host,
char **port,
+ char **path,
char **username,
char **password,
const char *url,
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 12f4591ec..fd2ca0fd8 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -558,7 +558,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
GITERR_CHECK_ALLOC(writepack);
if (git_indexer_new(&writepack->indexer,
- backend->pack_folder, odb, progress_cb, progress_payload) < 0) {
+ backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) {
git__free(writepack);
return -1;
}
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 9ea7c659a..2d62507f2 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -272,7 +272,7 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po)
git_buf zbuf = GIT_BUF_INIT;
git_otype type;
unsigned char hdr[10];
- unsigned int hdr_len;
+ size_t hdr_len;
unsigned long size;
void *data;
@@ -560,6 +560,7 @@ static int write_pack(git_packbuilder *pb,
git_buf buf = GIT_BUF_INIT;
enum write_one_status status;
struct git_pack_header ph;
+ git_oid entry_oid;
unsigned int i = 0;
int error = 0;
@@ -596,10 +597,10 @@ static int write_pack(git_packbuilder *pb,
} while (pb->nr_remaining && i < pb->nr_objects);
- if ((error = git_hash_final(&pb->pack_oid, &pb->ctx)) < 0)
+ if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0)
goto done;
- error = cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
+ error = cb(entry_oid.id, GIT_OID_RAWSZ, data);
done:
git__free(write_order);
@@ -1247,6 +1248,7 @@ static int write_cb(void *buf, size_t len, void *payload)
int git_packbuilder_write(
git_packbuilder *pb,
const char *path,
+ unsigned int mode,
git_transfer_progress_callback progress_cb,
void *progress_cb_payload)
{
@@ -1257,7 +1259,7 @@ int git_packbuilder_write(
PREPARE_PACK;
if (git_indexer_new(
- &indexer, path, pb->odb, progress_cb, progress_cb_payload) < 0)
+ &indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0)
return -1;
ctx.indexer = indexer;
@@ -1269,12 +1271,19 @@ int git_packbuilder_write(
return -1;
}
+ git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer));
+
git_indexer_free(indexer);
return 0;
}
#undef PREPARE_PACK
+const git_oid *git_packbuilder_hash(git_packbuilder *pb)
+{
+ return &pb->pack_oid;
+}
+
static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload)
{
struct tree_walk_context *ctx = payload;
diff --git a/src/push.c b/src/push.c
index a799db8d0..3c9d5bb35 100644
--- a/src/push.c
+++ b/src/push.c
@@ -616,16 +616,22 @@ on_error:
return error;
}
-static int cb_filter_refs(git_remote_head *ref, void *data)
-{
- git_remote *remote = (git_remote *) data;
- return git_vector_insert(&remote->refs, ref);
-}
-
static int filter_refs(git_remote *remote)
{
+ const git_remote_head **heads;
+ size_t heads_len, i;
+
git_vector_clear(&remote->refs);
- return git_remote_ls(remote, cb_filter_refs, remote);
+
+ if (git_remote_ls(&heads, &heads_len, remote) < 0)
+ return -1;
+
+ for (i = 0; i < heads_len; i++) {
+ if (git_vector_insert(&remote->refs, (void *)heads[i]) < 0)
+ return -1;
+ }
+
+ return 0;
}
int git_push_finish(git_push *push)
diff --git a/src/refspec.c b/src/refspec.c
index 492c6ed3f..a97340071 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -12,6 +12,7 @@
#include "util.h"
#include "posix.h"
#include "refs.h"
+#include "vector.h"
int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
{
@@ -287,3 +288,70 @@ git_direction git_refspec_direction(const git_refspec *spec)
return spec->push;
}
+
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs)
+{
+ git_buf buf = GIT_BUF_INIT;
+ size_t j, pos;
+ git_remote_head key;
+
+ const char* formatters[] = {
+ GIT_REFS_DIR "%s",
+ GIT_REFS_TAGS_DIR "%s",
+ GIT_REFS_HEADS_DIR "%s",
+ NULL
+ };
+
+ git_refspec *cur = git__calloc(1, sizeof(git_refspec));
+ GITERR_CHECK_ALLOC(cur);
+
+ cur->force = spec->force;
+ cur->push = spec->push;
+ cur->pattern = spec->pattern;
+ cur->matching = spec->matching;
+ cur->string = git__strdup(spec->string);
+
+ /* shorthand on the lhs */
+ if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
+ for (j = 0; formatters[j]; j++) {
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
+ return -1;
+
+ key.name = (char *) git_buf_cstr(&buf);
+ if (!git_vector_search(&pos, refs, &key)) {
+ /* we found something to match the shorthand, set src to that */
+ cur->src = git_buf_detach(&buf);
+ }
+ }
+ }
+
+ /* No shorthands found, copy over the name */
+ if (cur->src == NULL && spec->src != NULL) {
+ cur->src = git__strdup(spec->src);
+ GITERR_CHECK_ALLOC(cur->src);
+ }
+
+ if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
+ /* if it starts with "remotes" then we just prepend "refs/" */
+ if (!git__prefixcmp(spec->dst, "remotes/")) {
+ git_buf_puts(&buf, GIT_REFS_DIR);
+ } else {
+ git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
+ }
+
+ if (git_buf_puts(&buf, spec->dst) < 0)
+ return -1;
+
+ cur->dst = git_buf_detach(&buf);
+ }
+
+ git_buf_free(&buf);
+
+ if (cur->dst == NULL && spec->dst != NULL) {
+ cur->dst = git__strdup(spec->dst);
+ GITERR_CHECK_ALLOC(cur->dst);
+ }
+
+ return git_vector_insert(out, cur);
+}
diff --git a/src/refspec.h b/src/refspec.h
index 44d484c7b..51b7bfee9 100644
--- a/src/refspec.h
+++ b/src/refspec.h
@@ -9,6 +9,7 @@
#include "git2/refspec.h"
#include "buffer.h"
+#include "vector.h"
struct git_refspec {
char *string;
@@ -17,7 +18,6 @@ struct git_refspec {
unsigned int force :1,
push : 1,
pattern :1,
- dwim :1,
matching :1;
};
@@ -63,4 +63,10 @@ int git_refspec__serialize(git_buf *out, const git_refspec *refspec);
*/
int git_refspec_is_wildcard(const git_refspec *spec);
+/**
+ * DWIM `spec` with `refs` existing on the remote, append the dwim'ed
+ * result in `out`.
+ */
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs);
+
#endif
diff --git a/src/remote.c b/src/remote.c
index b97cc9e79..c0f35e5ea 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -19,6 +19,8 @@
#include "refspec.h"
#include "fetchhead.h"
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
+
static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
{
git_refspec *spec;
@@ -318,7 +320,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
GITERR_CHECK_ALLOC(remote->name);
if ((git_vector_init(&remote->refs, 32, NULL) < 0) ||
- (git_vector_init(&remote->refspecs, 2, NULL))) {
+ (git_vector_init(&remote->refspecs, 2, NULL) < 0) ||
+ (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) {
error = -1;
goto cleanup;
}
@@ -377,6 +380,10 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if (download_tags_value(remote, config) < 0)
goto cleanup;
+ /* Move the data over to where the matching functions can find them */
+ if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
+ goto cleanup;
+
*out = remote;
cleanup:
@@ -643,11 +650,11 @@ on_error:
return -1;
}
-int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
+int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
{
assert(remote);
- return remote->transport->ls(remote->transport, list_cb, payload);
+ return remote->transport->ls(out, size, remote->transport);
}
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
@@ -707,67 +714,31 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
return 0;
}
-static int store_refs(git_remote_head *head, void *payload)
-{
- git_vector *refs = (git_vector *)payload;
-
- return git_vector_insert(refs, head);
-}
-
-static int dwim_refspecs(git_vector *refspecs, git_vector *refs)
+/* DWIM `refspecs` based on `refs` and append the output to `out` */
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
{
- git_buf buf = GIT_BUF_INIT;
+ size_t i;
git_refspec *spec;
- size_t i, j, pos;
- git_remote_head key;
-
- const char* formatters[] = {
- GIT_REFS_DIR "%s",
- GIT_REFS_TAGS_DIR "%s",
- GIT_REFS_HEADS_DIR "%s",
- NULL
- };
git_vector_foreach(refspecs, i, spec) {
- if (spec->dwim)
- continue;
-
- /* shorthand on the lhs */
- if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
- for (j = 0; formatters[j]; j++) {
- git_buf_clear(&buf);
- if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
- return -1;
-
- key.name = (char *) git_buf_cstr(&buf);
- if (!git_vector_search(&pos, refs, &key)) {
- /* we found something to match the shorthand, set src to that */
- git__free(spec->src);
- spec->src = git_buf_detach(&buf);
- }
- }
- }
-
- if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
- /* if it starts with "remotes" then we just prepend "refs/" */
- if (!git__prefixcmp(spec->dst, "remotes/")) {
- git_buf_puts(&buf, GIT_REFS_DIR);
- } else {
- git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
- }
+ if (git_refspec__dwim_one(out, spec, refs) < 0)
+ return -1;
+ }
- if (git_buf_puts(&buf, spec->dst) < 0)
- return -1;
+ return 0;
+}
- git__free(spec->dst);
- spec->dst = git_buf_detach(&buf);
- }
+static void free_refspecs(git_vector *vec)
+{
+ size_t i;
+ git_refspec *spec;
- spec->dwim = 1;
+ git_vector_foreach(vec, i, spec) {
+ git_refspec__free(spec);
+ git__free(spec);
}
- git_buf_free(&buf);
- return 0;
+ git_vector_clear(vec);
}
static int remote_head_cmp(const void *_a, const void *_b)
@@ -778,6 +749,25 @@ static int remote_head_cmp(const void *_a, const void *_b)
return git__strcmp_cb(a->name, b->name);
}
+static int ls_to_vector(git_vector *out, git_remote *remote)
+{
+ git_remote_head **heads;
+ size_t heads_len, i;
+
+ if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
+ return -1;
+
+ if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
+ return -1;
+
+ for (i = 0; i < heads_len; i++) {
+ if (git_vector_insert(out, heads[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
int git_remote_download(git_remote *remote)
{
int error;
@@ -785,15 +775,14 @@ int git_remote_download(git_remote *remote)
assert(remote);
- if (git_vector_init(&refs, 16, remote_head_cmp) < 0)
+ if (ls_to_vector(&refs, remote) < 0)
return -1;
- if (git_remote_ls(remote, store_refs, &refs) < 0) {
- return -1;
- }
+ free_refspecs(&remote->active_refspecs);
- error = dwim_refspecs(&remote->refspecs, &refs);
+ error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs);
git_vector_free(&refs);
+
if (error < 0)
return -1;
@@ -1043,10 +1032,8 @@ int git_remote_update_tips(git_remote *remote)
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1;
- if (git_vector_init(&refs, 16, NULL) < 0)
- return -1;
- if ((error = git_remote_ls(remote, store_refs, &refs)) < 0)
+ if ((error = ls_to_vector(&refs, remote)) < 0)
goto out;
if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
@@ -1054,7 +1041,7 @@ int git_remote_update_tips(git_remote *remote)
goto out;
}
- git_vector_foreach(&remote->refspecs, i, spec) {
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
if (spec->push)
continue;
@@ -1063,8 +1050,8 @@ int git_remote_update_tips(git_remote *remote)
}
out:
- git_refspec__free(&tagspec);
git_vector_free(&refs);
+ git_refspec__free(&tagspec);
return error;
}
@@ -1097,9 +1084,6 @@ void git_remote_disconnect(git_remote *remote)
void git_remote_free(git_remote *remote)
{
- git_refspec *spec;
- size_t i;
-
if (remote == NULL)
return;
@@ -1112,12 +1096,12 @@ void git_remote_free(git_remote *remote)
git_vector_free(&remote->refs);
- git_vector_foreach(&remote->refspecs, i, spec) {
- git_refspec__free(spec);
- git__free(spec);
- }
+ free_refspecs(&remote->refspecs);
git_vector_free(&remote->refspecs);
+ free_refspecs(&remote->active_refspecs);
+ git_vector_free(&remote->active_refspecs);
+
git__free(remote->url);
git__free(remote->pushurl);
git__free(remote->name);
@@ -1526,7 +1510,7 @@ git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refnam
git_refspec *spec;
size_t i;
- git_vector_foreach(&remote->refspecs, i, spec) {
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
if (spec->push)
continue;
@@ -1542,7 +1526,7 @@ git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *re
git_refspec *spec;
size_t i;
- git_vector_foreach(&remote->refspecs, i, spec) {
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
if (spec->push)
continue;
@@ -1565,14 +1549,68 @@ void git_remote_clear_refspecs(git_remote *remote)
git_vector_clear(&remote->refspecs);
}
+static int add_and_dwim(git_remote *remote, const char *str, int push)
+{
+ git_refspec *spec;
+ git_vector *vec;
+
+ if (add_refspec(remote, str, !push) < 0)
+ return -1;
+
+ vec = &remote->refspecs;
+ spec = git_vector_get(vec, vec->length - 1);
+ return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs);
+}
+
int git_remote_add_fetch(git_remote *remote, const char *refspec)
{
- return add_refspec(remote, refspec, true);
+ return add_and_dwim(remote, refspec, false);
}
int git_remote_add_push(git_remote *remote, const char *refspec)
{
- return add_refspec(remote, refspec, false);
+ return add_and_dwim(remote, refspec, true);
+}
+
+static int set_refspecs(git_remote *remote, git_strarray *array, int push)
+{
+ git_vector *vec = &remote->refspecs;
+ git_refspec *spec;
+ size_t i;
+
+ /* Start by removing any refspecs of the same type */
+ for (i = 0; i < vec->length; i++) {
+ spec = git_vector_get(vec, i);
+ if (spec->push != push)
+ continue;
+
+ git_refspec__free(spec);
+ git__free(spec);
+ git_vector_remove(vec, i);
+ i--;
+ }
+
+ /* And now we add the new ones */
+
+ for (i = 0; i < array->count; i++) {
+ if (add_refspec(remote, array->strings[i], !push) < 0)
+ return -1;
+ }
+
+ free_refspecs(&remote->active_refspecs);
+ git_vector_clear(&remote->active_refspecs);
+
+ return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs);
+}
+
+int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array)
+{
+ return set_refspecs(remote, array, false);
+}
+
+int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array)
+{
+ return set_refspecs(remote, array, true);
}
static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
@@ -1630,18 +1668,3 @@ const git_refspec *git_remote_get_refspec(git_remote *remote, size_t n)
{
return git_vector_get(&remote->refspecs, n);
}
-
-int git_remote_remove_refspec(git_remote *remote, size_t n)
-{
- git_refspec *spec;
-
- assert(remote);
-
- spec = git_vector_get(&remote->refspecs, n);
- if (spec) {
- git_refspec__free(spec);
- git__free(spec);
- }
-
- return git_vector_remove(&remote->refspecs, n);
-}
diff --git a/src/remote.h b/src/remote.h
index 269584d96..33e4d68f8 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -21,6 +21,7 @@ struct git_remote {
char *pushurl;
git_vector refs;
git_vector refspecs;
+ git_vector active_refspecs;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
diff --git a/src/signature.c b/src/signature.c
index 52ca2b375..ec51a42e9 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -84,8 +84,12 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
git_signature *git_signature_dup(const git_signature *sig)
{
- git_signature *new = git__calloc(1, sizeof(git_signature));
+ git_signature *new;
+ if (sig == NULL)
+ return NULL;
+
+ new = git__calloc(1, sizeof(git_signature));
if (new == NULL)
return NULL;
diff --git a/src/transports/git.c b/src/transports/git.c
index 79a9e7dd4..5dcd4eff7 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -179,39 +179,33 @@ static int _git_uploadpack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- char *host=NULL, *port=NULL, *user=NULL, *pass=NULL;
+ char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+ const char *stream_url = url;
git_stream *s;
+ int error = -1;
*stream = NULL;
-
if (!git__prefixcmp(url, prefix_git))
- url += strlen(prefix_git);
+ stream_url += strlen(prefix_git);
- if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0)
+ if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0)
return -1;
s = (git_stream *)*stream;
- if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
- goto on_error;
-
- if (gitno_connect(&s->socket, host, port, 0) < 0)
- goto on_error;
-
- t->current_stream = s;
- git__free(host);
- git__free(port);
- git__free(user);
- git__free(pass);
- return 0;
+ if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
+ if (!(error = gitno_connect(&s->socket, host, port, 0)))
+ t->current_stream = s;
-on_error:
- if (*stream)
+ git__free(host);
+ git__free(port);
+ git__free(path);
+ git__free(user);
+ git__free(pass);
+ } else if (*stream)
git_stream_free(*stream);
- git__free(host);
- git__free(port);
- return -1;
+ return error;
}
static int _git_uploadpack(
@@ -235,39 +229,33 @@ static int _git_receivepack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- char *host=NULL, *port=NULL, *user=NULL, *pass=NULL;
+ char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+ const char *stream_url = url;
git_stream *s;
+ int error;
*stream = NULL;
-
if (!git__prefixcmp(url, prefix_git))
- url += strlen(prefix_git);
+ stream_url += strlen(prefix_git);
- if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0)
+ if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0)
return -1;
s = (git_stream *)*stream;
- if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
- goto on_error;
-
- if (gitno_connect(&s->socket, host, port, 0) < 0)
- goto on_error;
-
- t->current_stream = s;
- git__free(host);
- git__free(port);
- git__free(user);
- git__free(pass);
- return 0;
+ if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
+ if (!(error = gitno_connect(&s->socket, host, port, 0)))
+ t->current_stream = s;
-on_error:
- if (*stream)
+ git__free(host);
+ git__free(port);
+ git__free(path);
+ git__free(user);
+ git__free(pass);
+ } else if (*stream)
git_stream_free(*stream);
- git__free(host);
- git__free(port);
- return -1;
+ return error;
}
static int _git_receivepack(
diff --git a/src/transports/local.c b/src/transports/local.c
index 3163d2eac..4502f0202 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -213,21 +213,17 @@ static int local_connect(
return 0;
}
-static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
- unsigned int i;
- git_remote_head *head = NULL;
if (!t->have_refs) {
giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
return -1;
}
- git_vector_foreach(&t->refs, i, head) {
- if (list_cb(head, payload))
- return GIT_EUSER;
- }
+ *out = (const git_remote_head **) t->refs.contents;
+ *size = t->refs.length;
return 0;
}
diff --git a/src/transports/smart.c b/src/transports/smart.c
index a681d5f40..5242beb65 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -63,6 +63,24 @@ static int git_smart__set_callbacks(
return 0;
}
+int git_smart__update_heads(transport_smart *t)
+{
+ size_t i;
+ git_pkt *pkt;
+
+ git_vector_clear(&t->heads);
+ git_vector_foreach(&t->refs, i, pkt) {
+ git_pkt_ref *ref = (git_pkt_ref *) pkt;
+ if (pkt->type != GIT_PKT_REF)
+ continue;
+
+ if (git_vector_insert(&t->heads, &ref->head) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
static int git_smart__connect(
git_transport *transport,
const char *url,
@@ -140,6 +158,9 @@ static int git_smart__connect(
git_pkt_free((git_pkt *)first);
}
+ /* Keep a list of heads for _ls */
+ git_smart__update_heads(t);
+
if (t->rpc && git_smart__reset_stream(t, false) < 0)
return -1;
@@ -149,28 +170,17 @@ static int git_smart__connect(
return 0;
}
-static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = (transport_smart *)transport;
- unsigned int i;
- git_pkt *p = NULL;
if (!t->have_refs) {
giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
return -1;
}
- git_vector_foreach(&t->refs, i, p) {
- git_pkt_ref *pkt = NULL;
-
- if (p->type != GIT_PKT_REF)
- continue;
-
- pkt = (git_pkt_ref *)p;
-
- if (list_cb(&pkt->head, payload))
- return GIT_EUSER;
- }
+ *out = (const git_remote_head **) t->heads.contents;
+ *size = t->heads.length;
return 0;
}
@@ -293,6 +303,7 @@ static void git_smart__free(git_transport *transport)
/* Free the subtransport */
t->wrapped->free(t->wrapped);
+ git_vector_free(&t->heads);
git_vector_foreach(refs, i, p)
git_pkt_free(p);
@@ -340,6 +351,11 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
return -1;
}
+ if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) {
+ git__free(t);
+ return -1;
+ }
+
if (definition->callback(&t->wrapped, &t->parent) < 0) {
git__free(t);
return -1;
diff --git a/src/transports/smart.h b/src/transports/smart.h
index 5232e54de..32f0be7f2 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -140,6 +140,7 @@ typedef struct {
git_smart_subtransport_stream *current_stream;
transport_smart_caps caps;
git_vector refs;
+ git_vector heads;
git_vector common;
git_atomic cancelled;
packetsize_cb packetsize_cb;
@@ -173,6 +174,8 @@ int git_smart__download_pack(
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
+int git_smart__update_heads(transport_smart *t);
+
/* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_buffer_flush(git_buf *buf);
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 4e9e112f4..7288a4820 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -269,7 +269,7 @@ static int wait_while_ack(gitno_buffer *buf)
return 0;
}
-int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count)
+int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
{
transport_smart *t = (transport_smart *)transport;
gitno_buffer *buf = &t->buffer;
@@ -279,19 +279,20 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
unsigned int i;
git_oid oid;
- /* No own logic, do our thing */
- if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
return error;
if ((error = fetch_setup_walk(&walk, repo)) < 0)
goto on_error;
+
/*
- * We don't support any kind of ACK extensions, so the negotiation
- * boils down to sending what we have and listening for an ACK
- * every once in a while.
+ * Our support for ACK extensions is simply to parse them. On
+ * the first ACK we will accept that as enough common
+ * objects. We give up if we haven't found an answer in the
+ * first 256 we send.
*/
i = 0;
- while (true) {
+ while (i < 256) {
error = git_revwalk_next(&oid, walk);
if (error < 0) {
@@ -349,7 +350,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int i;
- if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, i, pkt) {
@@ -369,7 +370,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int i;
- if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, i, pkt) {
@@ -943,8 +944,13 @@ int git_smart__push(git_transport *transport, git_push *push)
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) {
error = update_refs_from_report(&t->refs, &push->specs, &push->status);
+ if (error < 0)
+ goto done;
+
+ error = git_smart__update_heads(t);
+ }
done:
git_buf_free(&pktline);
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 4e2834b49..4a905e3c9 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -317,7 +317,7 @@ static int _git_ssh_setup_conn(
const char *cmd,
git_smart_subtransport_stream **stream)
{
- char *host=NULL, *port=NULL, *user=NULL, *pass=NULL;
+ char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *default_port="22";
ssh_stream *s;
LIBSSH2_SESSION* session=NULL;
@@ -330,8 +330,7 @@ static int _git_ssh_setup_conn(
s = (ssh_stream *)*stream;
if (!git__prefixcmp(url, prefix_ssh)) {
- url = url + strlen(prefix_ssh);
- if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
+ if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0)
goto on_error;
} else {
if (git_ssh_extract_url_parts(&host, &user, url) < 0)
@@ -390,6 +389,7 @@ static int _git_ssh_setup_conn(
t->current_stream = s;
git__free(host);
git__free(port);
+ git__free(path);
git__free(user);
git__free(pass);
diff --git a/tests-clar/blame/buffer.c b/tests-clar/blame/buffer.c
index 69b2d5440..912ee9846 100644
--- a/tests-clar/blame/buffer.c
+++ b/tests-clar/blame/buffer.c
@@ -19,6 +19,8 @@ void test_blame_buffer__cleanup(void)
void test_blame_buffer__added_line(void)
{
+ const git_blame_hunk *hunk;
+
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
@@ -39,6 +41,10 @@ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "000000", "b.txt");
+
+ hunk = git_blame_get_hunk_byline(g_bufferblame, 16);
+ cl_assert(hunk);
+ cl_assert_equal_s("Ben Straub", hunk->final_signature->name);
}
void test_blame_buffer__deleted_line(void)
@@ -128,3 +134,33 @@ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, 0, "00000000", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, 0, "63d671eb", "b.txt");
}
+
+void test_blame_buffer__add_lines_at_end(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+abc\n\
+def\n";
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+
+ cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 16, 2, 0, "00000000", "b.txt");
+}
diff --git a/tests-clar/config/write.c b/tests-clar/config/write.c
index 309fef65a..15f750dc0 100644
--- a/tests-clar/config/write.c
+++ b/tests-clar/config/write.c
@@ -229,6 +229,37 @@ void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
git_config_free(cfg);
}
+void test_config_write__add_value_which_needs_quotes(void)
+{
+ git_config *cfg;
+ const char* str1;
+ const char* str2;
+ const char* str3;
+ const char* str4;
+ const char* str5;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something"));
+ cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something "));
+ cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing"));
+ cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing"));
+ cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace"));
+ cl_assert_equal_s(" Something", str1);
+ cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace"));
+ cl_assert_equal_s("Something ", str2);
+ cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1"));
+ cl_assert_equal_s("some#thing", str3);
+ cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2"));
+ cl_assert_equal_s("some;thing", str4);
+ cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote"));
+ cl_assert_equal_s(" some\"thing", str5);
+ git_config_free(cfg);
+}
+
void test_config_write__can_set_a_value_to_NULL(void)
{
git_repository *repository;
diff --git a/tests-clar/merge/workdir/setup.c b/tests-clar/merge/workdir/setup.c
index 463dee06e..870d55ef2 100644
--- a/tests-clar/merge/workdir/setup.c
+++ b/tests-clar/merge/workdir/setup.c
@@ -1041,7 +1041,8 @@ void test_merge_workdir_setup__removed_after_failure(void)
cl_git_rewritefile("merge-resolve/new-in-octo1.txt",
"Conflicting file!\n\nMerge will fail!\n");
- cl_git_fail(git_merge(&result, repo, &their_heads[0], 1, &opts));
+ cl_git_fail(git_merge(
+ &result, repo, (const git_merge_head **)&their_heads[0], 1, &opts));
cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE));
cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE));
diff --git a/tests-clar/merge/workdir/trivial.c b/tests-clar/merge/workdir/trivial.c
index d20d89879..9f9566243 100644
--- a/tests-clar/merge/workdir/trivial.c
+++ b/tests-clar/merge/workdir/trivial.c
@@ -62,7 +62,7 @@ static int merge_trivial(const char *ours, const char *theirs, bool automerge)
return 0;
}
-static int merge_trivial_conflict_entrycount(void)
+static size_t merge_trivial_conflict_entrycount(void)
{
const git_index_entry *entry;
size_t count = 0;
diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c
index 6d658a2e4..309142925 100644
--- a/tests-clar/network/remote/local.c
+++ b/tests-clar/network/remote/local.c
@@ -26,26 +26,6 @@ void test_network_remote_local__cleanup(void)
cl_fixture_cleanup("remotelocal");
}
-static int count_ref__cb(git_remote_head *head, void *payload)
-{
- int *count = (int *)payload;
-
- (void)head;
- (*count)++;
-
- return 0;
-}
-
-static int ensure_peeled__cb(git_remote_head *head, void *payload)
-{
- GIT_UNUSED(payload);
-
- if(strcmp(head->name, "refs/tags/test^{}") != 0)
- return 0;
-
- return git_oid_streq(&head->oid, "e90810b8df3e80c413d903f631643c716887138d");
-}
-
static void connect_to_local_repository(const char *local_repository)
{
git_buf_sets(&file_path_buf, cl_git_path_url(local_repository));
@@ -65,39 +45,42 @@ void test_network_remote_local__connected(void)
void test_network_remote_local__retrieve_advertised_references(void)
{
- int how_many_refs = 0;
+ const git_remote_head **refs;
+ size_t refs_len;
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
- cl_assert_equal_i(how_many_refs, 28);
+ cl_assert_equal_i(refs_len, 28);
}
void test_network_remote_local__retrieve_advertised_references_after_disconnect(void)
{
- int how_many_refs = 0;
+ const git_remote_head **refs;
+ size_t refs_len;
connect_to_local_repository(cl_fixture("testrepo.git"));
git_remote_disconnect(remote);
- cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
- cl_assert_equal_i(how_many_refs, 28);
+ cl_assert_equal_i(refs_len, 28);
}
void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
{
- int how_many_refs = 0;
+ const git_remote_head **refs;
+ size_t refs_len;
cl_fixture_sandbox("testrepo.git");
cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git"));
connect_to_local_repository("spaced testrepo.git");
- cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
- cl_assert_equal_i(how_many_refs, 28);
+ cl_assert_equal_i(refs_len, 28);
git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
remote = NULL;
@@ -107,9 +90,17 @@ void test_network_remote_local__retrieve_advertised_references_from_spaced_repos
void test_network_remote_local__nested_tags_are_completely_peeled(void)
{
+ const git_remote_head **refs;
+ size_t refs_len, i;
+
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL));
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ for (i = 0; i < refs_len; i++) {
+ if (!strcmp(refs[i]->name, "refs/tags/test^{}"))
+ cl_git_pass(git_oid_streq(&refs[i]->oid, "e90810b8df3e80c413d903f631643c716887138d"));
+ }
}
void test_network_remote_local__shorthand_fetch_refspec0(void)
diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c
index 15e841b35..2a9c2f69f 100644
--- a/tests-clar/network/urlparse.c
+++ b/tests-clar/network/urlparse.c
@@ -1,12 +1,12 @@
#include "clar_libgit2.h"
#include "netops.h"
-char *host, *port, *user, *pass;
-gitno_connection_data conndata;
+static char *host, *port, *path, *user, *pass;
+static gitno_connection_data conndata;
void test_network_urlparse__initialize(void)
{
- host = port = user = pass = NULL;
+ host = port = path = user = pass = NULL;
memset(&conndata, 0, sizeof(conndata));
}
@@ -15,6 +15,7 @@ void test_network_urlparse__cleanup(void)
#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
FREE_AND_NULL(host);
FREE_AND_NULL(port);
+ FREE_AND_NULL(path);
FREE_AND_NULL(user);
FREE_AND_NULL(pass);
@@ -23,27 +24,33 @@ void test_network_urlparse__cleanup(void)
void test_network_urlparse__trivial(void)
{
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "example.com/resource", "8080"));
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "http://example.com/resource", "8080"));
cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/resource");
cl_assert_equal_p(user, NULL);
cl_assert_equal_p(pass, NULL);
}
-void test_network_urlparse__bad_url(void)
+void test_network_urlparse__encoded_password(void)
{
- cl_git_fail_with(gitno_extract_url_parts(&host, &port, &user, &pass,
- "github.com/git://github.com/foo/bar.git.git", "443"),
- GIT_EINVALIDSPEC);
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user:pass%2fis%40bad@hostname.com:1234/", "1"));
+ cl_assert_equal_s(host, "hostname.com");
+ cl_assert_equal_s(port, "1234");
+ cl_assert_equal_s(path, "/");
+ cl_assert_equal_s(user, "user");
+ cl_assert_equal_s(pass, "pass/is@bad");
}
void test_network_urlparse__user(void)
{
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user@example.com/resource", "8080"));
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user@example.com/resource", "8080"));
cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user");
cl_assert_equal_p(pass, NULL);
}
@@ -51,10 +58,11 @@ void test_network_urlparse__user(void)
void test_network_urlparse__user_pass(void)
{
/* user:pass@hostname.tld/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user:pass@example.com/resource", "8080"));
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user:pass@example.com/resource", "8080"));
cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass");
}
@@ -62,10 +70,11 @@ void test_network_urlparse__user_pass(void)
void test_network_urlparse__port(void)
{
/* hostname.tld:port/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "example.com:9191/resource", "8080"));
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://example.com:9191/resource", "8080"));
cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "9191");
+ cl_assert_equal_s(path, "/resource");
cl_assert_equal_p(user, NULL);
cl_assert_equal_p(pass, NULL);
}
@@ -73,10 +82,11 @@ void test_network_urlparse__port(void)
void test_network_urlparse__user_port(void)
{
/* user@hostname.tld:port/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user@example.com:9191/resource", "8080"));
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user@example.com:9191/resource", "8080"));
cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "9191");
+ cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user");
cl_assert_equal_p(pass, NULL);
}
@@ -84,10 +94,11 @@ void test_network_urlparse__user_port(void)
void test_network_urlparse__user_pass_port(void)
{
/* user:pass@hostname.tld:port/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user:pass@example.com:9191/resource", "8080"));
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user:pass@example.com:9191/resource", "8080"));
cl_assert_equal_s(host, "example.com");
cl_assert_equal_s(port, "9191");
+ cl_assert_equal_s(path, "/resource");
cl_assert_equal_s(user, "user");
cl_assert_equal_s(pass, "pass");
}
@@ -116,6 +127,18 @@ void test_network_urlparse__connection_data_ssl(void)
cl_assert_equal_i(conndata.use_ssl, true);
}
+void test_network_urlparse__encoded_username_password(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@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_s(conndata.user, "user/name");
+ cl_assert_equal_s(conndata.pass, "pass@word%zyx%v");
+ cl_assert_equal_i(conndata.use_ssl, true);
+}
+
void test_network_urlparse__connection_data_cross_host_redirect(void)
{
conndata.host = git__strdup("bar.com");
diff --git a/tests-clar/online/fetch.c b/tests-clar/online/fetch.c
index df1b2e288..5153a7ae0 100644
--- a/tests-clar/online/fetch.c
+++ b/tests-clar/online/fetch.c
@@ -152,29 +152,20 @@ void test_online_fetch__can_cancel(void)
git_remote_free(remote);
}
-int ls_cb(git_remote_head *rhead, void *payload)
-{
- int *nr = payload;
- GIT_UNUSED(rhead);
-
- (*nr)++;
-
- return 0;
-}
-
void test_online_fetch__ls_disconnected(void)
{
+ const git_remote_head **refs;
+ size_t refs_len_before, refs_len_after;
git_remote *remote;
- int nr_before = 0, nr_after = 0;
cl_git_pass(git_remote_create(&remote, _repo, "test",
"http://github.com/libgit2/TestGitRepository.git"));
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_ls(remote, ls_cb, &nr_before));
+ cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote));
git_remote_disconnect(remote);
- cl_git_pass(git_remote_ls(remote, ls_cb, &nr_after));
+ cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote));
- cl_assert_equal_i(nr_before, nr_after);
+ cl_assert_equal_i(refs_len_before, refs_len_after);
git_remote_free(remote);
}
diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c
index d9ffe8aa9..aeb1ab47d 100644
--- a/tests-clar/online/push.c
+++ b/tests-clar/online/push.c
@@ -155,12 +155,11 @@ static void do_verify_push_status(git_push *push, const push_status expected[],
*/
static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
{
- git_vector actual_refs = GIT_VECTOR_INIT;
-
- git_remote_ls(remote, record_ref_cb, &actual_refs);
- verify_remote_refs(&actual_refs, expected_refs, expected_refs_len);
+ const git_remote_head **actual_refs;
+ size_t actual_refs_len;
- git_vector_free(&actual_refs);
+ git_remote_ls(&actual_refs, &actual_refs_len, remote);
+ verify_remote_refs(actual_refs, actual_refs_len, expected_refs, expected_refs_len);
}
/**
@@ -257,7 +256,8 @@ failed:
void test_online_push__initialize(void)
{
git_vector delete_specs = GIT_VECTOR_INIT;
- size_t i;
+ const git_remote_head **heads;
+ size_t i, heads_len;
char *curr_del_spec;
_repo = cl_git_sandbox_init("push_src");
@@ -314,7 +314,8 @@ void test_online_push__initialize(void)
* order to delete the remote branch pointed to by HEAD (usually master).
* See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
*/
- cl_git_pass(git_remote_ls(_remote, delete_ref_cb, &delete_specs));
+ cl_git_pass(git_remote_ls(&heads, &heads_len, _remote));
+ cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len));
if (delete_specs.length) {
git_push *push;
diff --git a/tests-clar/online/push_util.c b/tests-clar/online/push_util.c
index 2e457844d..038c144db 100644
--- a/tests-clar/online/push_util.c
+++ b/tests-clar/online/push_util.c
@@ -44,20 +44,23 @@ int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *
return 0;
}
-int delete_ref_cb(git_remote_head *head, void *payload)
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len)
{
- git_vector *delete_specs = (git_vector *)payload;
git_buf del_spec = GIT_BUF_INIT;
+ size_t i;
- /* Ignore malformed ref names (which also saves us from tag^{} */
- if (!git_reference_is_valid_name(head->name))
- return 0;
-
- /* Create a refspec that deletes a branch in the remote */
- if (strcmp(head->name, "refs/heads/master")) {
- cl_git_pass(git_buf_putc(&del_spec, ':'));
- cl_git_pass(git_buf_puts(&del_spec, head->name));
- cl_git_pass(git_vector_insert(delete_specs, git_buf_detach(&del_spec)));
+ for (i = 0; i < heads_len; i++) {
+ const git_remote_head *head = heads[i];
+ /* Ignore malformed ref names (which also saves us from tag^{} */
+ if (!git_reference_is_valid_name(head->name))
+ return 0;
+
+ /* Create a refspec that deletes a branch in the remote */
+ if (strcmp(head->name, "refs/heads/master")) {
+ cl_git_pass(git_buf_putc(&del_spec, ':'));
+ cl_git_pass(git_buf_puts(&del_spec, head->name));
+ cl_git_pass(git_vector_insert(out, git_buf_detach(&del_spec)));
+ }
}
return 0;
@@ -69,26 +72,28 @@ int record_ref_cb(git_remote_head *head, void *payload)
return git_vector_insert(refs, head);
}
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len)
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len)
{
size_t i, j = 0;
git_buf msg = GIT_BUF_INIT;
- git_remote_head *actual;
+ const git_remote_head *actual;
char *oid_str;
bool master_present = false;
/* We don't care whether "master" is present on the other end or not */
- git_vector_foreach(actual_refs, i, actual) {
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
if (!strcmp(actual->name, "refs/heads/master")) {
master_present = true;
break;
}
}
- if (expected_refs_len + (master_present ? 1 : 0) != actual_refs->length)
+ if (expected_refs_len + (master_present ? 1 : 0) != actual_refs_len)
goto failed;
- git_vector_foreach(actual_refs, i, actual) {
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
if (master_present && !strcmp(actual->name, "refs/heads/master"))
continue;
@@ -111,7 +116,8 @@ failed:
}
git_buf_puts(&msg, "\nACTUAL:\n");
- git_vector_foreach(actual_refs, i, actual) {
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
if (master_present && !strcmp(actual->name, "refs/heads/master"))
continue;
diff --git a/tests-clar/online/push_util.h b/tests-clar/online/push_util.h
index 64f02cf2f..a7207c49e 100644
--- a/tests-clar/online/push_util.h
+++ b/tests-clar/online/push_util.h
@@ -41,12 +41,13 @@ void record_callbacks_data_clear(record_callbacks_data *data);
int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
/**
- * Callback for git_remote_list that adds refspecs to delete each ref
+ * Create a set of refspecs that deletes each of the inputs
*
- * @param head a ref on the remote
- * @param payload a git_push instance
+ * @param out the vector in which to store the refspecs
+ * @param heads the remote heads
+ * @param heads_len the size of the array
*/
-int delete_ref_cb(git_remote_head *head, void *payload);
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len);
/**
* Callback for git_remote_list that adds refspecs to vector
@@ -60,10 +61,11 @@ int record_ref_cb(git_remote_head *head, void *payload);
* Verifies that refs on remote stored by record_ref_cb match the expected
* names, oids, and order.
*
- * @param actual_refs actual refs stored by record_ref_cb()
+ * @param actual_refs actual refs in the remote
+ * @param actual_refs_len length of actual_refs
* @param expected_refs expected remote refs
* @param expected_refs_len length of expected_refs
*/
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len);
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len);
#endif /* INCLUDE_cl_push_util_h__ */
diff --git a/tests-clar/pack/indexer.c b/tests-clar/pack/indexer.c
index d7953300f..07963a9e7 100644
--- a/tests-clar/pack/indexer.c
+++ b/tests-clar/pack/indexer.c
@@ -48,7 +48,7 @@ void test_pack_indexer__out_of_order(void)
git_indexer *idx;
git_transfer_progress stats;
- cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL));
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats));
cl_git_pass(git_indexer_commit(idx, &stats));
@@ -75,7 +75,7 @@ void test_pack_indexer__fix_thin(void)
git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
cl_assert(!git_oid_cmp(&id, &should_id));
- cl_git_pass(git_indexer_new(&idx, ".", odb, NULL, NULL));
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
cl_git_pass(git_indexer_commit(idx, &stats));
@@ -108,7 +108,7 @@ void test_pack_indexer__fix_thin(void)
cl_git_pass(p_stat(name, &st));
- cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL));
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
read = p_read(fd, buffer, sizeof(buffer));
cl_assert(read != -1);
p_close(fd);
diff --git a/tests-clar/pack/packbuilder.c b/tests-clar/pack/packbuilder.c
index ac6f731e1..54d1e8022 100644
--- a/tests-clar/pack/packbuilder.c
+++ b/tests-clar/pack/packbuilder.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "fileops.h"
+#include "pack.h"
#include "hash.h"
#include "iterator.h"
#include "vector.h"
@@ -92,7 +93,7 @@ void test_pack_packbuilder__create_pack(void)
seed_packbuilder();
- cl_git_pass(git_indexer_new(&_indexer, ".", NULL, NULL, NULL));
+ cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL, NULL));
cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
cl_git_pass(git_indexer_commit(_indexer, &stats));
@@ -128,6 +129,61 @@ void test_pack_packbuilder__create_pack(void)
cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
}
+void test_pack_packbuilder__get_hash(void)
+{
+ char hex[41]; hex[40] = '\0';
+
+ seed_packbuilder();
+
+ git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL);
+ git_oid_fmt(hex, git_packbuilder_hash(_packbuilder));
+
+ cl_assert_equal_s(hex, "80e61eb315239ef3c53033e37fee43b744d57122");
+}
+
+static void test_write_pack_permission(mode_t given, mode_t expected)
+{
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+ seed_packbuilder();
+
+ git_packbuilder_write(_packbuilder, ".", given, NULL, NULL);
+
+ /* Windows does not return group/user bits from stat,
+ * files are never executable.
+ */
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ mask = p_umask(0);
+ p_umask(mask);
+
+ cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.idx", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+ cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.pack", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+}
+
+void test_pack_packbuilder__permissions_standard(void)
+{
+ test_write_pack_permission(0, GIT_PACK_FILE_MODE);
+}
+
+void test_pack_packbuilder__permissions_readonly(void)
+{
+ test_write_pack_permission(0444, 0444);
+}
+
+void test_pack_packbuilder__permissions_readwrite(void)
+{
+ test_write_pack_permission(0666, 0666);
+}
+
static git_transfer_progress stats;
static int foreach_cb(void *buf, size_t len, void *payload)
{
@@ -141,7 +197,7 @@ void test_pack_packbuilder__foreach(void)
git_indexer *idx;
seed_packbuilder();
- cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL));
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
cl_git_pass(git_indexer_commit(idx, &stats));
git_indexer_free(idx);
diff --git a/tests-clar/repo/config.c b/tests-clar/repo/config.c
index 11abd42bc..e77acc8c5 100644
--- a/tests-clar/repo/config.c
+++ b/tests-clar/repo/config.c
@@ -2,7 +2,7 @@
#include "fileops.h"
#include <ctype.h>
-git_buf path = GIT_BUF_INIT;
+static git_buf path = GIT_BUF_INIT;
void test_repo_config__initialize(void)
{