summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--examples/general.c3
-rw-r--r--include/git2/branch.h25
-rw-r--r--include/git2/clone.h2
-rw-r--r--include/git2/diff.h92
-rw-r--r--include/git2/net.h2
-rw-r--r--include/git2/refspec.h20
-rw-r--r--include/git2/transport.h2
-rw-r--r--src/branch.c81
-rw-r--r--src/checkout.c2
-rw-r--r--src/clone.c1
-rw-r--r--src/diff.c50
-rw-r--r--src/index.c3
-rw-r--r--src/path.c2
-rw-r--r--src/pathspec.c17
-rw-r--r--src/pathspec.h12
-rw-r--r--src/push.c18
-rw-r--r--src/refspec.c30
-rw-r--r--src/transports/smart.c9
-rw-r--r--src/transports/smart_protocol.c123
-rw-r--r--src/vector.h2
-rw-r--r--src/win32/posix_w32.c21
-rw-r--r--tests-clar/clar_libgit2.c2
-rw-r--r--tests-clar/core/path.c2
-rw-r--r--tests-clar/diff/diff_helpers.h5
-rw-r--r--tests-clar/diff/workdir.c163
-rw-r--r--tests-clar/fetchhead/fetchhead_data.h14
-rw-r--r--tests-clar/fetchhead/nonetwork.c2
-rw-r--r--tests-clar/network/remotes.c11
-rw-r--r--tests-clar/online/fetchhead.c2
-rw-r--r--tests-clar/refs/branches/remote.c119
-rw-r--r--tests-clar/refs/rename.c66
-rw-r--r--tests-clar/repo/init.c40
-rw-r--r--tests-clar/repo/message.c16
-rw-r--r--tests-clar/threads/basic.c17
35 files changed, 812 insertions, 165 deletions
diff --git a/README.md b/README.md
index 91e0ce4bb..ab1d05ec8 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,6 @@ release its source code.
* Archives: <http://librelist.com/browser/libgit2/>
* Website: <http://libgit2.github.com>
* API documentation: <http://libgit2.github.com/libgit2>
-* Usage guide: <http://libgit2.github.com/api.html>
What It Can Do
==================================
diff --git a/examples/general.c b/examples/general.c
index 9ea264ec6..a042be011 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -8,7 +8,7 @@
// new functionality.
//
// If you're trying to write something in C using [libgit2][lg], you will also want
-// to check out the generated [API documentation][ap] and the [Usage Guide][ug]. We've
+// to check out the generated [API documentation][ap]. We've
// tried to link to the relevant sections of the API docs in each section in this file.
//
// **libgit2** only implements the core plumbing functions, not really the higher
@@ -17,7 +17,6 @@
//
// [lg]: http://libgit2.github.com
// [ap]: http://libgit2.github.com/libgit2
-// [ug]: http://libgit2.github.com/api.html
// [pg]: http://progit.org/book/ch9-0.html
// ### Includes
diff --git a/include/git2/branch.h b/include/git2/branch.h
index 54a1ab118..3c7fb443c 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -210,6 +210,31 @@ GIT_EXTERN(int) git_branch_tracking_name(
GIT_EXTERN(int) git_branch_is_head(
git_reference *branch);
+/**
+ * Return the name of remote that the remote tracking branch belongs to.
+ *
+ * @param remote_name_out The user-allocated buffer which will be
+ * filled with the name of the remote. Pass NULL if you just want to
+ * get the needed size of the name of the remote as the output value.
+ *
+ * @param buffer_size Size of the `out` buffer in bytes.
+ *
+ * @param repo The repository where the branch lives.
+ *
+ * @param branch The reference to the remote tracking branch.
+ *
+ * @return Number of characters in the reference name
+ * including the trailing NUL byte; GIT_ENOTFOUND
+ * when no remote matching remote was gound,
+ * GIT_EAMBIGUOUS when the branch maps to several remotes,
+ * otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_remote_name(
+ char *remote_name_out,
+ size_t buffer_size,
+ git_repository *repo,
+ git_reference *branch);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/clone.h b/include/git2/clone.h
index b54676874..e7205d744 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -56,7 +56,7 @@ GIT_BEGIN_DECL
* - `remote_callbacks` may be used to specify custom progress callbacks for
* the origin remote before the fetch is initiated.
* - `remote_autotag` may be used to specify the autotag setting before the
- * initial fetch.
+ * initial fetch. The default is GIT_REMOTE_DOWNLOAD_TAGS_ALL.
* - `checkout_branch` gives the name of the branch to checkout. NULL means
* use the remote's HEAD.
*/
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 81c41df04..3a88902ad 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -123,40 +123,6 @@ typedef enum {
} git_diff_option_t;
/**
- * Structure describing options about how the diff should be executed.
- *
- * Setting all values of the structure to zero will yield the default
- * values. Similarly, passing NULL for the options structure will
- * give the defaults. The default values are marked below.
- *
- * - `flags` is a combination of the `git_diff_option_t` values above
- * - `context_lines` is the number of unchanged lines that define the
- * boundary of a hunk (and to display before and after)
- * - `interhunk_lines` is the maximum number of unchanged lines between
- * hunk boundaries before the hunks will be merged into a one.
- * - `old_prefix` is the virtual "directory" to prefix to old file names
- * in hunk headers (default "a")
- * - `new_prefix` is the virtual "directory" to prefix to new file names
- * in hunk headers (default "b")
- * - `pathspec` is an array of paths / fnmatch patterns to constrain diff
- * - `max_size` is a file size (in bytes) above which a blob will be marked
- * as binary automatically; pass a negative value to disable.
- */
-typedef struct {
- unsigned int version; /**< version for the struct */
- uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
- uint16_t context_lines; /**< defaults to 3 */
- uint16_t interhunk_lines; /**< defaults to 0 */
- const char *old_prefix; /**< defaults to "a" */
- const char *new_prefix; /**< defaults to "b" */
- git_strarray pathspec; /**< defaults to include all paths */
- git_off_t max_size; /**< defaults to 512MB */
-} git_diff_options;
-
-#define GIT_DIFF_OPTIONS_VERSION 1
-#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION}
-
-/**
* The diff list object that contains all individual file deltas.
*
* This is an opaque structure which will be allocated by one of the diff
@@ -266,6 +232,64 @@ typedef struct {
} git_diff_delta;
/**
+ * Diff notification callback function.
+ *
+ * The callback will be called for each file, just before the `git_delta_t`
+ * gets inserted into the diff list.
+ *
+ * When the callback:
+ * - returns < 0, the diff process will be aborted.
+ * - returns > 0, the delta will not be inserted into the diff list, but the
+ * diff process continues.
+ * - returns 0, the delta is inserted into the diff list, and the diff process
+ * continues.
+ */
+typedef int (*git_diff_notify_cb)(
+ const git_diff_list *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload);
+
+/**
+ * Structure describing options about how the diff should be executed.
+ *
+ * Setting all values of the structure to zero will yield the default
+ * values. Similarly, passing NULL for the options structure will
+ * give the defaults. The default values are marked below.
+ *
+ * - `flags` is a combination of the `git_diff_option_t` values above
+ * - `context_lines` is the number of unchanged lines that define the
+ * boundary of a hunk (and to display before and after)
+ * - `interhunk_lines` is the maximum number of unchanged lines between
+ * hunk boundaries before the hunks will be merged into a one.
+ * - `old_prefix` is the virtual "directory" to prefix to old file names
+ * in hunk headers (default "a")
+ * - `new_prefix` is the virtual "directory" to prefix to new file names
+ * in hunk headers (default "b")
+ * - `pathspec` is an array of paths / fnmatch patterns to constrain diff
+ * - `max_size` is a file size (in bytes) above which a blob will be marked
+ * as binary automatically; pass a negative value to disable.
+ * - `notify_cb` is an optional callback function, notifying the consumer of
+ * which files are being examined as the diff is generated
+ * - `notify_payload` is the payload data to pass to the `notify_cb` function
+ */
+typedef struct {
+ unsigned int version; /**< version for the struct */
+ uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
+ uint16_t context_lines; /**< defaults to 3 */
+ uint16_t interhunk_lines; /**< defaults to 0 */
+ const char *old_prefix; /**< defaults to "a" */
+ const char *new_prefix; /**< defaults to "b" */
+ git_strarray pathspec; /**< defaults to include all paths */
+ git_off_t max_size; /**< defaults to 512MB */
+ git_diff_notify_cb notify_cb;
+ void *notify_payload;
+} git_diff_options;
+
+#define GIT_DIFF_OPTIONS_VERSION 1
+#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION}
+
+/**
* When iterating over a diff, callback that will be made per file.
*
* @param delta A pointer to the delta data for the file
diff --git a/include/git2/net.h b/include/git2/net.h
index 6e3525f5d..e70ba1f71 100644
--- a/include/git2/net.h
+++ b/include/git2/net.h
@@ -37,7 +37,7 @@ typedef enum {
* Remote head description, given out on `ls` calls.
*/
struct git_remote_head {
- int local:1; /* available locally */
+ int local; /* available locally */
git_oid oid;
git_oid loid;
char *name;
diff --git a/include/git2/refspec.h b/include/git2/refspec.h
index ee06f8eca..ec7830b7c 100644
--- a/include/git2/refspec.h
+++ b/include/git2/refspec.h
@@ -53,6 +53,15 @@ GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
/**
+ * Check if a refspec's destination descriptor matches a reference
+ *
+ * @param refspec the refspec
+ * @param refname the name of the reference to check
+ * @return 1 if the refspec matches, 0 otherwise
+ */
+GIT_EXTERN(int) git_refspec_dst_matches(const git_refspec *refspec, const char *refname);
+
+/**
* Transform a reference to its target following the refspec's rules
*
* @param out where to store the target name
@@ -63,6 +72,17 @@ GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *
*/
GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
+/**
+ * Transform a target reference to its source reference following the refspec's rules
+ *
+ * @param out where to store the source reference name
+ * @param outlen the size of the `out` buffer
+ * @param spec the refspec
+ * @param name the name of the reference to transform
+ * @return 0, GIT_EBUFS or another error
+ */
+GIT_EXTERN(int) git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name);
+
GIT_END_DECL
#endif
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 469b43f72..783ea51bf 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -300,7 +300,7 @@ typedef struct git_smart_subtransport_definition {
/* True if the protocol is stateless; false otherwise. For example,
* http:// is stateless, but git:// is not. */
- unsigned rpc : 1;
+ unsigned rpc;
} git_smart_subtransport_definition;
/* Smart transport subtransports that come with libgit2 */
diff --git a/src/branch.c b/src/branch.c
index 975a43dbd..11ecbe9a1 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -319,6 +319,87 @@ cleanup:
return error;
}
+int git_branch_remote_name(
+ char *remote_name_out,
+ size_t buffer_size,
+ git_repository *repo,
+ git_reference *branch)
+{
+ git_strarray remote_list = {0};
+ size_t i, remote_name_size;
+ git_remote *remote;
+ const git_refspec *fetchspec;
+ int error = 0;
+ char *remote_name = NULL;
+
+ assert(branch);
+
+ if (remote_name_out && buffer_size)
+ *remote_name_out = '\0';
+
+ /* Verify that this is a remote branch */
+ if (!git_reference_is_remote(branch)) {
+ giterr_set(GITERR_INVALID,
+ "Reference '%s' is not a remote branch.", branch->name);
+ error = GIT_ERROR;
+ goto cleanup;
+ }
+
+ /* Get the remotes */
+ if ((error = git_remote_list(&remote_list, repo)) < 0)
+ goto cleanup;
+
+ /* Find matching remotes */
+ for (i = 0; i < remote_list.count; i++) {
+ if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
+ goto cleanup;
+
+ fetchspec = git_remote_fetchspec(remote);
+
+ /* Defensivly check that we have a fetchspec */
+ if (fetchspec &&
+ git_refspec_dst_matches(fetchspec, branch->name)) {
+ /* If we have not already set out yet, then set
+ * it to the matching remote name. Otherwise
+ * multiple remotes match this reference, and it
+ * is ambiguous. */
+ if (!remote_name) {
+ remote_name = remote_list.strings[i];
+ } else {
+ git_remote_free(remote);
+ error = GIT_EAMBIGUOUS;
+ goto cleanup;
+ }
+ }
+
+ git_remote_free(remote);
+ }
+
+ if (remote_name) {
+ remote_name_size = strlen(remote_name) + 1;
+ error = (int) remote_name_size;
+
+ if (remote_name_out) {
+ if(remote_name_size > buffer_size) {
+ giterr_set(
+ GITERR_INVALID,
+ "Buffer too short to hold the remote name.");
+ error = GIT_ERROR;
+ goto cleanup;
+ }
+
+ memcpy(remote_name_out, remote_name, remote_name_size);
+ }
+ } else {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+cleanup:
+ git_strarray_free(&remote_list);
+ return error;
+}
+
int git_branch_tracking_name(
char *tracking_branch_name_out,
size_t buffer_size,
diff --git a/src/checkout.c b/src/checkout.c
index 40f5732ed..0ce283beb 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -224,7 +224,7 @@ static int checkout_action_wd_only(
if (!git_pathspec_match_path(
pathspec, wd->path,
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
- git_iterator_ignore_case(workdir)))
+ git_iterator_ignore_case(workdir), NULL))
return 0;
/* check if item is tracked in the index but not in the checkout diff */
diff --git a/src/clone.c b/src/clone.c
index 333bf2148..4b72b833c 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -417,6 +417,7 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s
/* Provide defaults for null pointers */
if (!dst->remote_name) dst->remote_name = "origin";
+ if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
}
int git_clone(
diff --git a/src/diff.c b/src/diff.c
index 4b60935f0..d9bc32a37 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -41,12 +41,26 @@ static git_diff_delta *diff_delta__alloc(
return delta;
}
+static int diff_notify(
+ const git_diff_list *diff,
+ const git_diff_delta *delta,
+ const char *matched_pathspec)
+{
+ if (!diff->opts.notify_cb)
+ return 0;
+
+ return diff->opts.notify_cb(
+ diff, delta, matched_pathspec, diff->opts.notify_payload);
+}
+
static int diff_delta__from_one(
git_diff_list *diff,
git_delta_t status,
const git_index_entry *entry)
{
git_diff_delta *delta;
+ const char *matched_pathspec;
+ int notify_res;
if (status == GIT_DELTA_IGNORED &&
(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
@@ -59,7 +73,7 @@ static int diff_delta__from_one(
if (!git_pathspec_match_path(
&diff->pathspec, entry->path,
(diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) != 0,
- (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0))
+ (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0, &matched_pathspec))
return 0;
delta = diff_delta__alloc(diff, status, entry->path);
@@ -84,12 +98,16 @@ static int diff_delta__from_one(
!git_oid_iszero(&delta->new_file.oid))
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
- if (git_vector_insert(&diff->deltas, delta) < 0) {
+ notify_res = diff_notify(diff, delta, matched_pathspec);
+
+ if (notify_res)
+ git__free(delta);
+ else if (git_vector_insert(&diff->deltas, delta) < 0) {
git__free(delta);
return -1;
}
- return 0;
+ return notify_res < 0 ? GIT_EUSER : 0;
}
static int diff_delta__from_two(
@@ -99,9 +117,11 @@ static int diff_delta__from_two(
uint32_t old_mode,
const git_index_entry *new_entry,
uint32_t new_mode,
- git_oid *new_oid)
+ git_oid *new_oid,
+ const char *matched_pathspec)
{
git_diff_delta *delta;
+ int notify_res;
if (status == GIT_DELTA_UNMODIFIED &&
(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
@@ -138,12 +158,16 @@ static int diff_delta__from_two(
if (new_oid || !git_oid_iszero(&new_entry->oid))
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
- if (git_vector_insert(&diff->deltas, delta) < 0) {
+ notify_res = diff_notify(diff, delta, matched_pathspec);
+
+ if (notify_res)
+ git__free(delta);
+ else if (git_vector_insert(&diff->deltas, delta) < 0) {
git__free(delta);
return -1;
}
- return 0;
+ return notify_res < 0 ? GIT_EUSER : 0;
}
static git_diff_delta *diff_delta__last_for_item(
@@ -419,13 +443,14 @@ static int maybe_modified(
unsigned int omode = oitem->mode;
unsigned int nmode = nitem->mode;
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
+ const char *matched_pathspec;
GIT_UNUSED(old_iter);
if (!git_pathspec_match_path(
&diff->pathspec, oitem->path,
(diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) != 0,
- (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0))
+ (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0, &matched_pathspec))
return 0;
/* on platforms with no symlinks, preserve mode of existing symlinks */
@@ -526,7 +551,7 @@ static int maybe_modified(
}
return diff_delta__from_two(
- diff, status, oitem, omode, nitem, nmode, use_noid);
+ diff, status, oitem, omode, nitem, nmode, use_noid, matched_pathspec);
}
static bool entry_is_prefixed(
@@ -747,10 +772,11 @@ int git_diff__from_iterators(
else {
assert(oitem && nitem && cmp == 0);
- if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
- git_iterator_advance(old_iter, &oitem) < 0 ||
- git_iterator_advance(new_iter, &nitem) < 0)
- goto fail;
+ if (maybe_modified(
+ old_iter, oitem, new_iter, nitem, diff) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
}
}
diff --git a/src/index.c b/src/index.c
index 353f57c2b..25156d08f 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1710,7 +1710,8 @@ int git_index_read_tree_match(
goto cleanup;
while (entry != NULL) {
- if (git_pathspec_match_path(&pathspec, entry->path, false, false) &&
+ if (git_pathspec_match_path(
+ &pathspec, entry->path, false, false, NULL) &&
(error = git_index_add(index, entry)) < 0)
goto cleanup;
diff --git a/src/path.c b/src/path.c
index 5de58cce7..263cf9e7c 100644
--- a/src/path.c
+++ b/src/path.c
@@ -400,7 +400,7 @@ int git_path_fromurl(git_buf *local_path_out, const char *file_url)
if (offset >= len || file_url[offset] == '/')
return error_invalid_local_file_uri(file_url);
-#ifndef _MSC_VER
+#ifndef GIT_WIN32
offset--; /* A *nix absolute path starts with a forward slash */
#endif
diff --git a/src/pathspec.c b/src/pathspec.c
index 2bde3ba5f..732180248 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -106,14 +106,21 @@ void git_pathspec_free(git_vector *vspec)
/* match a path against the vectorized pathspec */
bool git_pathspec_match_path(
- git_vector *vspec, const char *path, bool disable_fnmatch, bool casefold)
+ git_vector *vspec,
+ const char *path,
+ bool disable_fnmatch,
+ bool casefold,
+ const char **matched_pathspec)
{
- unsigned int i;
+ size_t i;
git_attr_fnmatch *match;
int fnmatch_flags = 0;
int (*use_strcmp)(const char *, const char *);
int (*use_strncmp)(const char *, const char *, size_t);
+ if (matched_pathspec)
+ *matched_pathspec = NULL;
+
if (!vspec || !vspec->length)
return true;
@@ -143,8 +150,12 @@ bool git_pathspec_match_path(
path[match->length] == '/')
result = 0;
- if (result == 0)
+ if (result == 0) {
+ if (matched_pathspec)
+ *matched_pathspec = match->pattern;
+
return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+ }
}
return false;
diff --git a/src/pathspec.h b/src/pathspec.h
index dde63c7d0..c44561520 100644
--- a/src/pathspec.h
+++ b/src/pathspec.h
@@ -25,8 +25,16 @@ extern int git_pathspec_init(
/* free data from the pathspec vector */
extern void git_pathspec_free(git_vector *vspec);
-/* match a path against the vectorized pathspec */
+/*
+ * Match a path against the vectorized pathspec.
+ * The matched pathspec is passed back into the `matched_pathspec` parameter,
+ * unless it is passed as NULL by the caller.
+ */
extern bool git_pathspec_match_path(
- git_vector *vspec, const char *path, bool disable_fnmatch, bool casefold);
+ git_vector *vspec,
+ const char *path,
+ bool disable_fnmatch,
+ bool casefold,
+ const char **matched_pathspec);
#endif
diff --git a/src/push.c b/src/push.c
index ddfe5ec07..64aaead6e 100644
--- a/src/push.c
+++ b/src/push.c
@@ -14,6 +14,20 @@
#include "vector.h"
#include "push.h"
+static int push_spec_rref_cmp(const void *a, const void *b)
+{
+ const push_spec *push_spec_a = a, *push_spec_b = b;
+
+ return strcmp(push_spec_a->rref, push_spec_b->rref);
+}
+
+static int push_status_ref_cmp(const void *a, const void *b)
+{
+ const push_status *push_status_a = a, *push_status_b = b;
+
+ return strcmp(push_status_a->ref, push_status_b->ref);
+}
+
int git_push_new(git_push **out, git_remote *remote)
{
git_push *p;
@@ -27,12 +41,12 @@ int git_push_new(git_push **out, git_remote *remote)
p->remote = remote;
p->report_status = 1;
- if (git_vector_init(&p->specs, 0, NULL) < 0) {
+ if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) {
git__free(p);
return -1;
}
- if (git_vector_init(&p->status, 0, NULL) < 0) {
+ if (git_vector_init(&p->status, 0, push_status_ref_cmp) < 0) {
git_vector_free(&p->specs);
git__free(p);
return -1;
diff --git a/src/refspec.c b/src/refspec.c
index bd69f58ae..a51b0cfab 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -159,11 +159,19 @@ int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
return (p_fnmatch(refspec->src, refname, 0) == 0);
}
-int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
+int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
+{
+ if (refspec == NULL || refspec->dst == NULL)
+ return false;
+
+ return (p_fnmatch(refspec->dst, refname, 0) == 0);
+}
+
+static int refspec_transform_internal(char *out, size_t outlen, const char *from, const char *to, const char *name)
{
size_t baselen, namelen;
- baselen = strlen(spec->dst);
+ baselen = strlen(to);
if (outlen <= baselen) {
giterr_set(GITERR_INVALID, "Reference name too long");
return GIT_EBUFS;
@@ -173,8 +181,8 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
* No '*' at the end means that it's mapped to one specific local
* branch, so no actual transformation is needed.
*/
- if (spec->dst[baselen - 1] != '*') {
- memcpy(out, spec->dst, baselen + 1); /* include '\0' */
+ if (to[baselen - 1] != '*') {
+ memcpy(out, to, baselen + 1); /* include '\0' */
return 0;
}
@@ -182,7 +190,7 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
baselen--;
/* skip the prefix, -1 is for the '*' */
- name += strlen(spec->src) - 1;
+ name += strlen(from) - 1;
namelen = strlen(name);
@@ -191,12 +199,22 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
return GIT_EBUFS;
}
- memcpy(out, spec->dst, baselen);
+ memcpy(out, to, baselen);
memcpy(out + baselen, name, namelen + 1);
return 0;
}
+int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
+{
+ return refspec_transform_internal(out, outlen, spec->src, spec->dst, name);
+}
+
+int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name)
+{
+ return refspec_transform_internal(out, outlen, spec->dst, spec->src, name);
+}
+
static int refspec_transform(git_buf *out, const char *from, const char *to, const char *name)
{
if (git_buf_sets(out, to) < 0)
diff --git a/src/transports/smart.c b/src/transports/smart.c
index af6fec535..e820488f6 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -294,6 +294,13 @@ static void git_smart__free(git_transport *transport)
git__free(t);
}
+static int ref_name_cmp(const void *a, const void *b)
+{
+ const git_pkt_ref *ref_a = a, *ref_b = b;
+
+ return strcmp(ref_a->head.name, ref_b->head.name);
+}
+
int git_transport_smart(git_transport **out, git_remote *owner, void *param)
{
transport_smart *t;
@@ -321,7 +328,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->owner = owner;
t->rpc = definition->rpc;
- if (git_vector_init(&t->refs, 16, NULL) < 0) {
+ if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) {
git__free(t);
return -1;
}
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 184b21a0b..0fae086cb 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -628,6 +628,110 @@ static int parse_report(gitno_buffer *buf, git_push *push)
}
}
+static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
+{
+ git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
+ GITERR_CHECK_ALLOC(added);
+
+ added->type = GIT_PKT_REF;
+ git_oid_cpy(&added->head.oid, &push_spec->loid);
+ added->head.name = git__strdup(push_spec->rref);
+
+ if (!added->head.name ||
+ git_vector_insert(refs, added) < 0) {
+ git_pkt_free((git_pkt *)added);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int update_refs_from_report(
+ git_vector *refs,
+ git_vector *push_specs,
+ git_vector *push_report)
+{
+ git_pkt_ref *ref;
+ push_spec *push_spec;
+ push_status *push_status;
+ size_t i, j, refs_len;
+ int cmp;
+
+ /* For each push spec we sent to the server, we should have
+ * gotten back a status packet in the push report */
+ if (push_specs->length != push_report->length) {
+ giterr_set(GITERR_NET, "report-status: protocol error");
+ return -1;
+ }
+
+ /* We require that push_specs be sorted with push_spec_rref_cmp,
+ * and that push_report be sorted with push_status_ref_cmp */
+ git_vector_sort(push_specs);
+ git_vector_sort(push_report);
+
+ git_vector_foreach(push_specs, i, push_spec) {
+ push_status = git_vector_get(push_report, i);
+
+ /* For each push spec we sent to the server, we should have
+ * gotten back a status packet in the push report which matches */
+ if (strcmp(push_spec->rref, push_status->ref)) {
+ giterr_set(GITERR_NET, "report-status: protocol error");
+ return -1;
+ }
+ }
+
+ /* We require that refs be sorted with ref_name_cmp */
+ git_vector_sort(refs);
+ i = j = 0;
+ refs_len = refs->length;
+
+ /* Merge join push_specs with refs */
+ while (i < push_specs->length && j < refs_len) {
+ push_spec = git_vector_get(push_specs, i);
+ push_status = git_vector_get(push_report, i);
+ ref = git_vector_get(refs, j);
+
+ cmp = strcmp(push_spec->rref, ref->head.name);
+
+ /* Iterate appropriately */
+ if (cmp <= 0) i++;
+ if (cmp >= 0) j++;
+
+ /* Add case */
+ if (cmp < 0 &&
+ !push_status->msg &&
+ add_ref_from_push_spec(refs, push_spec) < 0)
+ return -1;
+
+ /* Update case, delete case */
+ if (cmp == 0 &&
+ !push_status->msg)
+ git_oid_cpy(&ref->head.oid, &push_spec->loid);
+ }
+
+ for (; i < push_specs->length; i++) {
+ push_spec = git_vector_get(push_specs, i);
+ push_status = git_vector_get(push_report, i);
+
+ /* Add case */
+ if (!push_status->msg &&
+ add_ref_from_push_spec(refs, push_spec) < 0)
+ return -1;
+ }
+
+ /* Remove any refs which we updated to have a zero OID. */
+ git_vector_rforeach(refs, i, ref) {
+ if (git_oid_iszero(&ref->head.oid)) {
+ git_vector_remove(refs, i);
+ git_pkt_free((git_pkt *)ref);
+ }
+ }
+
+ git_vector_sort(refs);
+
+ return 0;
+}
+
static int stream_thunk(void *buf, size_t size, void *data)
{
git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
@@ -640,7 +744,6 @@ int git_smart__push(git_transport *transport, git_push *push)
transport_smart *t = (transport_smart *)transport;
git_smart_subtransport_stream *s;
git_buf pktline = GIT_BUF_INIT;
- char *url = NULL;
int error = -1;
#ifdef PUSH_DEBUG
@@ -678,25 +781,13 @@ int git_smart__push(git_transport *transport, git_push *push)
else if (parse_report(&t->buffer, push) < 0)
goto on_error;
- /* If we updated at least one ref, then we need to re-acquire the list of
- * refs so the caller can call git_remote_update_tips afterward. TODO: Use
- * the data from the push report to do this without another network call */
- if (push->specs.length) {
- git_cred_acquire_cb cred_cb = t->cred_acquire_cb;
- void *cred_payload = t->cred_acquire_payload;
- int flags = t->flags;
-
- url = git__strdup(t->url);
-
- if (!url || t->parent.close(&t->parent) < 0 ||
- t->parent.connect(&t->parent, url, cred_cb, cred_payload, GIT_DIRECTION_PUSH, flags))
- goto on_error;
- }
+ if (push->status.length &&
+ update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
+ goto on_error;
error = 0;
on_error:
- git__free(url);
git_buf_free(&pktline);
return error;
diff --git a/src/vector.h b/src/vector.h
index 690e4af9c..e2f729b83 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -64,7 +64,7 @@ GIT_INLINE(void *) git_vector_last(const git_vector *v)
for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
#define git_vector_rforeach(v, iter, elem) \
- for ((iter) = (v)->length; (iter) > 0 && ((elem) = (v)->contents[(iter)-1], 1); (iter)-- )
+ for ((iter) = (v)->length - 1; (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )
int git_vector_insert(git_vector *v, void *element);
int git_vector_insert_sorted(git_vector *v, void *element,
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 0c23e2959..f533eaa5e 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -100,6 +100,21 @@ static int do_lstat(
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+ /* Windows symlinks have zero file size, call readlink to determine
+ * the length of the path pointed to, which we expect everywhere else
+ */
+ if (S_ISLNK(fMode)) {
+ char target[GIT_WIN_PATH];
+ int readlink_result;
+
+ readlink_result = p_readlink(file_name, target, GIT_WIN_PATH);
+
+ if (readlink_result == -1)
+ return -1;
+
+ buf->st_size = strlen(target);
+ }
+
return 0;
}
@@ -157,10 +172,10 @@ int p_readlink(const char *link, char *target, size_t target_len)
* it is not available in platforms older than Vista
*/
if (pGetFinalPath == NULL) {
- HINSTANCE library = LoadLibrary("kernel32");
+ HMODULE module = GetModuleHandle("kernel32");
- if (library != NULL)
- pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");
+ if (module != NULL)
+ pGetFinalPath = (fpath_func)GetProcAddress(module, "GetFinalPathNameByHandleW");
if (pGetFinalPath == NULL) {
giterr_set(GITERR_OS,
diff --git a/tests-clar/clar_libgit2.c b/tests-clar/clar_libgit2.c
index 88ffb2bca..63efd5954 100644
--- a/tests-clar/clar_libgit2.c
+++ b/tests-clar/clar_libgit2.c
@@ -238,7 +238,7 @@ const char* cl_git_path_url(const char *path)
cl_git_pass(git_path_prettify_dir(&path_buf, path, NULL));
cl_git_pass(git_buf_puts(&url_buf, "file://"));
-#ifdef _MSC_VER
+#ifdef GIT_WIN32
/*
* A FILE uri matches the following format: file://[host]/path
* where "host" can be empty and "path" is an absolute path to the resource.
diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c
index 894e81f3d..407770baa 100644
--- a/tests-clar/core/path.c
+++ b/tests-clar/core/path.c
@@ -324,7 +324,7 @@ static void check_fromurl(const char *expected_result, const char *input, int sh
git_buf_free(&buf);
}
-#ifdef _MSC_VER
+#ifdef GIT_WIN32
#define ABS_PATH_MARKER ""
#else
#define ABS_PATH_MARKER "/"
diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h
index 49c265285..a43847b79 100644
--- a/tests-clar/diff/diff_helpers.h
+++ b/tests-clar/diff/diff_helpers.h
@@ -20,6 +20,11 @@ typedef struct {
int line_dels;
} diff_expects;
+typedef struct {
+ const char *path;
+ const char *matched_pathspec;
+} notify_expected;
+
extern int diff_file_cb(
const git_diff_delta *delta,
float progress,
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
index 21da63954..7e8915c4b 100644
--- a/tests-clar/diff/workdir.c
+++ b/tests-clar/diff/workdir.c
@@ -307,6 +307,169 @@ void test_diff_workdir__to_index_with_pathspec(void)
git_diff_list_free(diff);
}
+static int assert_called_notifications(
+ const git_diff_list *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ bool found = false;
+ notify_expected *exp = (notify_expected*)payload;
+ notify_expected *e;;
+
+ GIT_UNUSED(diff_so_far);
+
+ for (e = exp; e->path != NULL; e++) {
+ if (strcmp(e->path, delta_to_add->new_file.path))
+ continue;
+
+ cl_assert_equal_s(e->matched_pathspec, matched_pathspec);
+
+ found = true;
+ break;
+ }
+
+ cl_assert(found);
+ return 0;
+}
+
+void test_diff_workdir__to_index_notify(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+
+ char *searched_pathspecs_solo[] = {
+ "*_deleted",
+ };
+ notify_expected expected_matched_pathspecs_solo[] = {
+ { "file_deleted", "*_deleted" },
+ { "staged_changes_file_deleted", "*_deleted" },
+ { NULL, NULL }
+ };
+
+ char *searched_pathspecs_multiple[] = {
+ "staged_changes_cant_find_me",
+ "subdir/modified_cant_find_me",
+ "subdir/*",
+ "staged*"
+ };
+ notify_expected expected_matched_pathspecs_multiple[] = {
+ { "staged_changes_file_deleted", "staged*" },
+ { "staged_changes_modified_file", "staged*" },
+ { "staged_delete_modified_file", "staged*" },
+ { "staged_new_file_deleted_file", "staged*" },
+ { "staged_new_file_modified_file", "staged*" },
+ { "subdir/deleted_file", "subdir/*" },
+ { "subdir/modified_file", "subdir/*" },
+ { "subdir/new_file", "subdir/*" },
+ { NULL, NULL }
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = assert_called_notifications;
+ opts.pathspec.strings = searched_pathspecs_solo;
+ opts.pathspec.count = 1;
+
+ opts.notify_payload = &expected_matched_pathspecs_solo;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(2, exp.files);
+
+ git_diff_list_free(diff);
+
+ opts.pathspec.strings = searched_pathspecs_multiple;
+ opts.pathspec.count = 4;
+ opts.notify_payload = &expected_matched_pathspecs_multiple;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(8, exp.files);
+
+ git_diff_list_free(diff);
+}
+
+static int abort_diff(
+ const git_diff_list *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(delta_to_add);
+ GIT_UNUSED(matched_pathspec);
+ GIT_UNUSED(payload);
+
+ return -42;
+}
+
+void test_diff_workdir__to_index_notify_can_be_aborted_by_callback(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_list *diff = NULL;
+ char *pathspec = NULL;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = abort_diff;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ pathspec = "file_deleted";
+ cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ pathspec = "staged_changes_modified_file";
+ cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+}
+
+static int filter_all(
+ const git_diff_list *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(delta_to_add);
+ GIT_UNUSED(matched_pathspec);
+ GIT_UNUSED(payload);
+
+ return 42;
+}
+
+void test_diff_workdir__to_index_notify_can_be_used_as_filtering_function(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_list *diff = NULL;
+ char *pathspec = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = filter_all;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ pathspec = "*_deleted";
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+
+ git_diff_list_free(diff);
+}
+
+
void test_diff_workdir__filemode_changes(void)
{
git_config *cfg;
diff --git a/tests-clar/fetchhead/fetchhead_data.h b/tests-clar/fetchhead/fetchhead_data.h
index 71f67be25..294c9fb01 100644
--- a/tests-clar/fetchhead/fetchhead_data.h
+++ b/tests-clar/fetchhead/fetchhead_data.h
@@ -1,5 +1,5 @@
-#define FETCH_HEAD_WILDCARD_DATA \
+#define FETCH_HEAD_WILDCARD_DATA_LOCAL \
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
@@ -7,13 +7,23 @@
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n"
+#define FETCH_HEAD_WILDCARD_DATA \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
+ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n"
+
#define FETCH_HEAD_NO_MERGE_DATA \
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \
- "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n"
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
+ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n"
#define FETCH_HEAD_EXPLICIT_DATA \
diff --git a/tests-clar/fetchhead/nonetwork.c b/tests-clar/fetchhead/nonetwork.c
index b8cb69e68..ef30679f9 100644
--- a/tests-clar/fetchhead/nonetwork.c
+++ b/tests-clar/fetchhead/nonetwork.c
@@ -92,7 +92,7 @@ void test_fetchhead_nonetwork__write(void)
cl_git_pass(git_futils_readbuffer(&fetchhead_buf,
"./test1/.git/FETCH_HEAD"));
- equals = (strcmp(fetchhead_buf.ptr, FETCH_HEAD_WILDCARD_DATA) == 0);
+ equals = (strcmp(fetchhead_buf.ptr, FETCH_HEAD_WILDCARD_DATA_LOCAL) == 0);
git_buf_free(&fetchhead_buf);
diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c
index b138d8c10..51d6c946f 100644
--- a/tests-clar/network/remotes.c
+++ b/tests-clar/network/remotes.c
@@ -173,13 +173,20 @@ void test_network_remotes__fnmatch(void)
void test_network_remotes__transform(void)
{
- char ref[1024];
+ char ref[1024] = {0};
- memset(ref, 0x0, sizeof(ref));
cl_git_pass(git_refspec_transform(ref, sizeof(ref), _refspec, "refs/heads/master"));
cl_assert_equal_s(ref, "refs/remotes/test/master");
}
+void test_network_remotes__transform_destination_to_source(void)
+{
+ char ref[1024] = {0};
+
+ cl_git_pass(git_refspec_rtransform(ref, sizeof(ref), _refspec, "refs/remotes/test/master"));
+ cl_assert_equal_s(ref, "refs/heads/master");
+}
+
void test_network_remotes__transform_r(void)
{
git_buf buf = GIT_BUF_INIT;
diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c
index f89270741..84a2177ea 100644
--- a/tests-clar/online/fetchhead.c
+++ b/tests-clar/online/fetchhead.c
@@ -81,7 +81,7 @@ void test_online_fetchhead__no_merges(void)
cl_git_pass(git_repository_config(&config, g_repo));
cl_git_pass(git_config_set_string(config, "branch.master.remote", NULL));
cl_git_pass(git_config_set_string(config, "branch.master.merge", NULL));
- git_config_free(config);
+ git_config_free(config);
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
}
diff --git a/tests-clar/refs/branches/remote.c b/tests-clar/refs/branches/remote.c
new file mode 100644
index 000000000..be355af46
--- /dev/null
+++ b/tests-clar/refs/branches/remote.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+#include "remote.h"
+
+static git_repository *g_repo;
+
+static const char *current_master_tip = "099fabac3a9ea935598528c27f866e34089c2eff";
+
+void test_refs_branches_remote__initialize(void)
+{
+ git_oid id;
+
+ g_repo = cl_git_sandbox_init("testrepo");
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create test/master */
+ git_reference_create(NULL, g_repo, "refs/remotes/test/master", &id, 1);
+}
+
+void test_refs_branches_remote__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_remote__can_get_remote_for_branch(void)
+{
+ git_reference *ref;
+ const char *name;
+ char *expectedRemoteName = "test";
+ int expectedRemoteNameLength = strlen(expectedRemoteName) + 1;
+ char remotename[1024] = {0};
+
+ cl_git_pass(git_branch_lookup(&ref, g_repo, "test/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name, ref));
+ cl_assert_equal_s("test/master", name);
+
+ cl_assert_equal_i(expectedRemoteNameLength,
+ git_branch_remote_name(NULL, 0, g_repo, ref));
+ cl_assert_equal_i(expectedRemoteNameLength,
+ git_branch_remote_name(remotename, expectedRemoteNameLength, g_repo, ref));
+ cl_assert_equal_s("test", remotename);
+
+ git_reference_free(ref);
+}
+
+void test_refs_branches_remote__insufficient_buffer_returns_error(void)
+{
+ git_reference *ref;
+ const char *name;
+ char *expectedRemoteName = "test";
+ int expectedRemoteNameLength = strlen(expectedRemoteName) + 1;
+ char remotename[1024] = {0};
+
+ cl_git_pass(git_branch_lookup(&ref, g_repo, "test/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name, ref));
+ cl_assert_equal_s("test/master", name);
+
+ cl_assert_equal_i(expectedRemoteNameLength,
+ git_branch_remote_name(NULL, 0, g_repo, ref));
+ cl_git_fail_with(GIT_ERROR,
+ git_branch_remote_name(remotename, expectedRemoteNameLength - 1, g_repo, ref));
+
+ git_reference_free(ref);
+}
+
+void test_refs_branches_remote__no_matching_remote_returns_error(void)
+{
+ git_reference *ref;
+ const char *name;
+ git_oid id;
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create nonexistent/master */
+ git_reference_create(NULL, g_repo, "refs/remotes/nonexistent/master", &id, 1);
+
+ cl_git_pass(git_branch_lookup(&ref, g_repo,"nonexistent/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name, ref));
+ cl_assert_equal_s("nonexistent/master", name);
+
+ cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, ref), GIT_ENOTFOUND);
+ git_reference_free(ref);
+}
+
+void test_refs_branches_remote__local_remote_returns_error(void)
+{
+ git_reference *ref;
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,g_repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_name(&name, ref));
+ cl_assert_equal_s("master",name);
+
+ cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, ref), GIT_ERROR);
+ git_reference_free(ref);
+}
+
+void test_refs_branches_remote__ambiguous_remote_returns_error(void)
+{
+ git_reference *ref;
+ const char *name;
+ git_remote *remote;
+
+ /* Create the remote */
+ cl_git_pass(git_remote_create(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2"));
+
+ /* Update the remote fetch spec */
+ cl_git_pass(git_remote_set_fetchspec(remote, "refs/heads/*:refs/remotes/test/*"));
+ cl_git_pass(git_remote_save(remote));
+
+ git_remote_free(remote);
+
+ cl_git_pass(git_branch_lookup(&ref,g_repo, "test/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name, ref));
+ cl_assert_equal_s("test/master", name);
+
+ cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, ref), GIT_EAMBIGUOUS);
+ git_reference_free(ref);
+}
diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c
index bfdef15fa..5c1e8a798 100644
--- a/tests-clar/refs/rename.c
+++ b/tests-clar/refs/rename.c
@@ -19,19 +19,19 @@ static git_repository *g_repo;
void test_refs_rename__initialize(void)
{
- g_repo = cl_git_sandbox_init("testrepo");
+ g_repo = cl_git_sandbox_init("testrepo");
}
void test_refs_rename__cleanup(void)
{
- cl_git_sandbox_cleanup();
+ cl_git_sandbox_cleanup();
}
void test_refs_rename__loose(void)
{
- // rename a loose reference
+ // rename a loose reference
git_reference *looked_up_ref, *another_looked_up_ref;
git_buf temp_path = GIT_BUF_INIT;
const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";
@@ -72,7 +72,7 @@ void test_refs_rename__loose(void)
void test_refs_rename__packed(void)
{
- // rename a packed reference (should make it loose)
+ // rename a packed reference (should make it loose)
git_reference *looked_up_ref, *another_looked_up_ref;
git_buf temp_path = GIT_BUF_INIT;
const char *brand_new_name = "refs/heads/brand_new_name";
@@ -113,7 +113,7 @@ void test_refs_rename__packed(void)
void test_refs_rename__packed_doesnt_pack_others(void)
{
- // renaming a packed reference does not pack another reference which happens to be in both loose and pack state
+ // renaming a packed reference does not pack another reference which happens to be in both loose and pack state
git_reference *looked_up_ref, *another_looked_up_ref;
git_buf temp_path = GIT_BUF_INIT;
const char *brand_new_name = "refs/heads/brand_new_name";
@@ -154,7 +154,7 @@ void test_refs_rename__packed_doesnt_pack_others(void)
void test_refs_rename__name_collision(void)
{
- // can not rename a reference with the name of an existing reference
+ // can not rename a reference with the name of an existing reference
git_reference *looked_up_ref;
/* An existing reference... */
@@ -173,7 +173,7 @@ void test_refs_rename__name_collision(void)
void test_refs_rename__invalid_name(void)
{
- // can not rename a reference with an invalid name
+ // can not rename a reference with an invalid name
git_reference *looked_up_ref;
/* An existing oid reference... */
@@ -199,7 +199,7 @@ void test_refs_rename__invalid_name(void)
void test_refs_rename__force_loose_packed(void)
{
- // can force-rename a packed reference with the name of an existing loose and packed reference
+ // can force-rename a packed reference with the name of an existing loose and packed reference
git_reference *looked_up_ref;
git_oid oid;
@@ -223,7 +223,7 @@ void test_refs_rename__force_loose_packed(void)
void test_refs_rename__force_loose(void)
{
- // can force-rename a loose reference with the name of an existing loose reference
+ // can force-rename a loose reference with the name of an existing loose reference
git_reference *looked_up_ref;
git_oid oid;
@@ -232,7 +232,7 @@ void test_refs_rename__force_loose(void)
git_oid_cpy(&oid, git_reference_target(looked_up_ref));
/* Can be force-renamed to the name of another existing reference. */
- cl_git_pass(git_reference_rename(looked_up_ref, "refs/heads/test", 1));
+ cl_git_pass(git_reference_rename(looked_up_ref, "refs/heads/test", 1));
git_reference_free(looked_up_ref);
/* Check we actually renamed it */
@@ -250,7 +250,7 @@ void test_refs_rename__force_loose(void)
void test_refs_rename__overwrite(void)
{
- // can not overwrite name of existing reference
+ // can not overwrite name of existing reference
git_reference *ref, *ref_one, *ref_one_new, *ref_two;
git_oid id;
@@ -281,7 +281,7 @@ void test_refs_rename__overwrite(void)
void test_refs_rename__prefix(void)
{
- // can be renamed to a new name prefixed with the old name
+ // can be renamed to a new name prefixed with the old name
git_reference *ref, *ref_two, *looked_up_ref;
git_oid id;
@@ -313,33 +313,33 @@ void test_refs_rename__prefix(void)
void test_refs_rename__move_up(void)
{
- // can move a reference to a upper reference hierarchy
- git_reference *ref, *ref_two, *looked_up_ref;
- git_oid id;
+ // can move a reference to a upper reference hierarchy
+ git_reference *ref, *ref_two, *looked_up_ref;
+ git_oid id;
- cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
- cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
- git_oid_cpy(&id, git_reference_target(ref));
+ git_oid_cpy(&id, git_reference_target(ref));
- /* Create loose references */
- cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0));
- git_reference_free(ref_two);
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0));
+ git_reference_free(ref_two);
- /* An existing reference... */
- cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
- /* Can be renamed upward the reference tree. */
- cl_git_pass(git_reference_rename(looked_up_ref, ref_two_name, 0));
- git_reference_free(looked_up_ref);
+ /* Can be renamed upward the reference tree. */
+ cl_git_pass(git_reference_rename(looked_up_ref, ref_two_name, 0));
+ git_reference_free(looked_up_ref);
- /* Check we actually renamed it */
- cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
- cl_assert_equal_s(looked_up_ref->name, ref_two_name);
- git_reference_free(looked_up_ref);
- cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
- git_reference_free(ref);
- git_reference_free(looked_up_ref);
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name);
+ git_reference_free(looked_up_ref);
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ git_reference_free(ref);
+ git_reference_free(looked_up_ref);
}
void test_refs_rename__propagate_eexists(void)
diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c
index 9ddb39545..97a5ff62a 100644
--- a/tests-clar/repo/init.c
+++ b/tests-clar/repo/init.c
@@ -263,36 +263,36 @@ void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
void test_repo_init__reinit_overwrites_filemode(void)
{
- git_config *config;
- int expected, current_value;
+ git_config *config;
+ int expected, current_value;
#ifdef GIT_WIN32
- expected = false;
+ expected = false;
#else
- expected = true;
+ expected = true;
#endif
- /* Init a new repo */
- cl_set_cleanup(&cleanup_repository, "overwrite.git");
- cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
+ /* Init a new repo */
+ cl_set_cleanup(&cleanup_repository, "overwrite.git");
+ cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
- /* Change the "core.filemode" config value to something unlikely */
- git_repository_config(&config, _repo);
- git_config_set_bool(config, "core.filemode", !expected);
- git_config_free(config);
- git_repository_free(_repo);
- _repo = NULL;
+ /* Change the "core.filemode" config value to something unlikely */
+ git_repository_config(&config, _repo);
+ git_config_set_bool(config, "core.filemode", !expected);
+ git_config_free(config);
+ git_repository_free(_repo);
+ _repo = NULL;
- /* Reinit the repository */
- cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
- git_repository_config(&config, _repo);
+ /* Reinit the repository */
+ cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
+ git_repository_config(&config, _repo);
- /* Ensure the "core.filemode" config value has been reset */
- cl_git_pass(git_config_get_bool(&current_value, config, "core.filemode"));
- cl_assert_equal_i(expected, current_value);
+ /* Ensure the "core.filemode" config value has been reset */
+ cl_git_pass(git_config_get_bool(&current_value, config, "core.filemode"));
+ cl_assert_equal_i(expected, current_value);
- git_config_free(config);
+ git_config_free(config);
}
void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
diff --git a/tests-clar/repo/message.c b/tests-clar/repo/message.c
index 4a6f13b9d..59487d51b 100644
--- a/tests-clar/repo/message.c
+++ b/tests-clar/repo/message.c
@@ -31,16 +31,16 @@ void test_repo_message__message(void)
ssize_t len;
cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), "MERGE_MSG"));
- cl_git_mkfile(git_buf_cstr(&_path), expected);
+ cl_git_mkfile(git_buf_cstr(&_path), expected);
- len = git_repository_message(NULL, 0, _repo);
- cl_assert(len > 0);
- _actual = git__malloc(len + 1);
- cl_assert(_actual != NULL);
+ len = git_repository_message(NULL, 0, _repo);
+ cl_assert(len > 0);
+ _actual = git__malloc(len + 1);
+ cl_assert(_actual != NULL);
- cl_assert(git_repository_message(_actual, len, _repo) > 0);
- _actual[len] = '\0';
- cl_assert_equal_s(expected, _actual);
+ cl_assert(git_repository_message(_actual, len, _repo) > 0);
+ _actual[len] = '\0';
+ cl_assert_equal_s(expected, _actual);
cl_git_pass(p_unlink(git_buf_cstr(&_path)));
cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo));
diff --git a/tests-clar/threads/basic.c b/tests-clar/threads/basic.c
index 2b1c36808..a15c53140 100644
--- a/tests-clar/threads/basic.c
+++ b/tests-clar/threads/basic.c
@@ -5,16 +5,19 @@
static git_repository *g_repo;
-void test_threads_basic__initialize(void) {
- g_repo = cl_git_sandbox_init("testrepo");
+void test_threads_basic__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
}
-void test_threads_basic__cleanup(void) {
- cl_git_sandbox_cleanup();
+void test_threads_basic__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
}
-void test_threads_basic__cache(void) {
- // run several threads polling the cache at the same time
- cl_assert(1 == 1);
+void test_threads_basic__cache(void)
+{
+ // run several threads polling the cache at the same time
+ cl_assert(1 == 1);
}