summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <cmn@dwim.me>2014-09-16 01:25:53 +0200
committerCarlos Martín Nieto <cmn@dwim.me>2014-09-16 17:02:28 +0200
commit0fef38999abc74b8237971f96295f461631d9d1d (patch)
tree6f26d1bd650140f1c4add4bf944263cd8e058d70
parentbf8756d6a2c42dc77b8a2de814a12e2ceb4487fd (diff)
parent910cd2daa6af0f3af97d283eb4c6a0452688d067 (diff)
downloadlibgit2-0fef38999abc74b8237971f96295f461631d9d1d.tar.gz
Merge remote-tracking branch 'upstream/master' into cmn/host-cert-info
-rw-r--r--.editorconfig14
-rw-r--r--CHANGELOG.md3
-rw-r--r--README.md2
-rw-r--r--examples/tag.c2
-rw-r--r--include/git2/merge.h16
-rw-r--r--include/git2/oidarray.h40
-rw-r--r--include/git2/remote.h13
-rw-r--r--include/git2/repository.h3
-rw-r--r--include/git2/submodule.h18
-rwxr-xr-xscript/cibuild.sh2
-rw-r--r--src/blame.c1
-rw-r--r--src/clone.c49
-rw-r--r--src/merge.c54
-rw-r--r--src/oidarray.c21
-rw-r--r--src/oidarray.h18
-rw-r--r--src/pack.c3
-rw-r--r--src/path.c55
-rw-r--r--src/path.h11
-rw-r--r--src/remote.c14
-rw-r--r--src/repository.c32
-rw-r--r--src/revparse.c33
-rw-r--r--src/signature.c4
-rw-r--r--src/stash.c3
-rw-r--r--src/submodule.c106
-rw-r--r--src/transport.c36
-rw-r--r--src/transports/smart_protocol.c2
-rw-r--r--src/transports/ssh.c8
-rw-r--r--tests/commit/signature.c4
-rw-r--r--tests/fetchhead/fetchhead_data.h15
-rw-r--r--tests/network/remote/defaultbranch.c58
-rw-r--r--tests/network/remote/remotes.c80
-rw-r--r--tests/online/clone.c9
-rw-r--r--tests/online/fetchhead.c12
-rw-r--r--tests/online/push.c3
-rw-r--r--tests/path/core.c55
-rw-r--r--tests/repo/init.c78
-rw-r--r--tests/revwalk/mergebase.c18
-rw-r--r--tests/submodule/add.c20
-rw-r--r--tests/submodule/repository_init.c40
39 files changed, 796 insertions, 159 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..be59274e8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+; Check http://editorconfig.org/ for more informations
+; Top-most EditorConfig file
+root = true
+
+; tab indentation
+[*]
+indent_style = tab
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+; 4-column space indentation
+[*.md]
+indent_style = space
+indent_size = 4
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97c873dc2..4664c4d41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -51,3 +51,6 @@ v0.21 + 1
* Add support for refspecs with the asterisk in the middle of a
pattern.
+
+* Introduce git_merge_bases() and the git_oidarray type to expose all
+ merge bases between two commits.
diff --git a/README.md b/README.md
index 0020a7762..d0dbf9386 100644
--- a/README.md
+++ b/README.md
@@ -195,6 +195,8 @@ Here are the bindings to libgit2 that are currently available:
* git2r <https://github.com/ropensci/git2r>
* Ruby
* Rugged <https://github.com/libgit2/rugged>
+* Rust
+ * git2-rs <https://github.com/alexcrichton/git2-rs>
* Vala
* libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
diff --git a/examples/tag.c b/examples/tag.c
index ebb8e37b2..1d254aee5 100644
--- a/examples/tag.c
+++ b/examples/tag.c
@@ -236,7 +236,7 @@ static void action_create_tag(tag_state *state)
git_signature_free(tagger);
}
-static void print_usage()
+static void print_usage(void)
{
fprintf(stderr, "usage: see `git help tag`\n");
exit(1);
diff --git a/include/git2/merge.h b/include/git2/merge.h
index 9eb14ccb1..bd5ebc1bd 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
+#include "oidarray.h"
#include "checkout.h"
#include "index.h"
@@ -321,6 +322,21 @@ GIT_EXTERN(int) git_merge_base(
const git_oid *two);
/**
+ * Find merge bases between two commits
+ *
+ * @param out array in which to store the resulting ids
+ * @param repo the repository where the commits exist
+ * @param one one of the commits
+ * @param two the other commit
+ * @return 0 on success, GIT_ENOTFOUND if not found or error code
+ */
+GIT_EXTERN(int) git_merge_bases(
+ git_oidarray *out,
+ git_repository *repo,
+ const git_oid *one,
+ const git_oid *two);
+
+/**
* Find a merge base given a list of commits
*
* @param out the OID of a merge base considering all the commits
diff --git a/include/git2/oidarray.h b/include/git2/oidarray.h
new file mode 100644
index 000000000..0b3204597
--- /dev/null
+++ b/include/git2/oidarray.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_oidarray_h__
+#define INCLUDE_git_oidarray_h__
+
+#include "common.h"
+#include "oid.h"
+
+GIT_BEGIN_DECL
+
+/** Array of object ids */
+typedef struct git_oidarray {
+ git_oid *ids;
+ size_t count;
+} git_oidarray;
+
+/**
+ * Free the OID array
+ *
+ * This method must (and must only) be called on `git_oidarray`
+ * objects where the array is allocated by the library. Not doing so,
+ * will result in a memory leak.
+ *
+ * This does not free the `git_oidarray` itself, since the library will
+ * never allocate that object directly itself (it is more commonly embedded
+ * inside another struct or created on the stack).
+ *
+ * @param array git_oidarray from which to free oid data
+ */
+GIT_EXTERN(void) git_oidarray_free(git_oidarray *array);
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
diff --git a/include/git2/remote.h b/include/git2/remote.h
index d2cc3e8e7..055f5e517 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -384,15 +384,12 @@ GIT_EXTERN(int) git_remote_fetch(
const char *reflog_message);
/**
- * Return whether a string is a valid remote URL
*
- * @param url the url to check
- * @return 1 if the url is valid, 0 otherwise
- */
-GIT_EXTERN(int) git_remote_valid_url(const char *url);
-
-/**
- * Return whether the passed URL is supported by this version of the library.
+ * Return whether the library supports a particular URL scheme
+ *
+ * Both the built-in and externally-registered transport lists are
+ * searched for a transport which supports the scheme of the given
+ * URL.
*
* @param url the url to check
* @return 1 if the url is supported, 0 otherwise
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 18e515cb0..268782648 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -196,6 +196,8 @@ GIT_EXTERN(int) git_repository_init(
* looking the "template_path" from the options if set, or the
* `init.templatedir` global config if not, or falling back on
* "/usr/share/git-core/templates" if it exists.
+ * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is
+ * specified, use relative paths for the gitdir and core.worktree.
*/
typedef enum {
GIT_REPOSITORY_INIT_BARE = (1u << 0),
@@ -204,6 +206,7 @@ typedef enum {
GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6),
} git_repository_init_flag_t;
/**
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 864d1c58c..616890df6 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -471,6 +471,24 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules(
GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
/**
+ * Set up the subrepository for a submodule in preparation for clone.
+ *
+ * This function can be called to init and set up a submodule
+ * repository from a submodule in preparation to clone it from
+ * its remote.
+ *
+ * @param out Output pointer to the created git repository.
+ * @param sm The submodule to create a new subrepository from.
+ * @param use_gitlink Should the workdir contain a gitlink to
+ * the repo in .git/modules vs. repo directly in workdir.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink);
+
+/**
* Copy submodule remote info into submodule repo.
*
* This copies the information about the submodules URL into the checked out
diff --git a/script/cibuild.sh b/script/cibuild.sh
index c7c341ce5..dda4ead4a 100755
--- a/script/cibuild.sh
+++ b/script/cibuild.sh
@@ -44,7 +44,5 @@ export GITTEST_REMOTE_SSH_PASSPHRASE=""
if [ -e ./libgit2_clar ]; then
./libgit2_clar -sonline::push -sonline::clone::cred_callback -sonline::clone::ssh_cert &&
- rm -rf $HOME/_temp/test.git &&
- git init --bare $HOME/_temp/test.git && # create an empty one
./libgit2_clar -sonline::clone::ssh_with_paths
fi
diff --git a/src/blame.c b/src/blame.c
index eb977c287..2cc5e552b 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -316,7 +316,6 @@ static int blame_internal(git_blame *blame)
ent->suspect = o;
blame->ent = ent;
- blame->path = blame->path;
git_blame__like_git(blame, blame->options.flags);
diff --git a/src/clone.c b/src/clone.c
index 7835be1b1..43b839003 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -144,9 +144,9 @@ static int update_head_to_remote(
const git_signature *signature,
const char *reflog_message)
{
- int error = 0, found_branch = 0;
+ int error = 0;
size_t refs_len;
- git_refspec dummy_spec, *refspec;
+ git_refspec *refspec;
const git_remote_head *remote_head, **refs;
const git_oid *remote_head_id;
git_buf remote_master_name = GIT_BUF_INIT;
@@ -155,28 +155,30 @@ static int update_head_to_remote(
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
return error;
- /* Did we just clone an empty repository? */
- if (refs_len == 0)
+ /* We cloned an empty repository or one with an unborn HEAD */
+ if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
return setup_tracking_config(
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
- error = git_remote_default_branch(&branch, remote);
- if (error == GIT_ENOTFOUND) {
- git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE);
- } else {
- found_branch = 1;
- }
-
- /* Get the remote's HEAD. This is always the first ref in the list. */
+ /* We know we have HEAD, let's see where it points */
remote_head = refs[0];
assert(remote_head);
remote_head_id = &remote_head->oid;
+
+ error = git_remote_default_branch(&branch, remote);
+ if (error == GIT_ENOTFOUND) {
+ error = git_repository_set_head_detached(
+ repo, remote_head_id, signature, reflog_message);
+ goto cleanup;
+ }
+
refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
if (refspec == NULL) {
- memset(&dummy_spec, 0, sizeof(git_refspec));
- refspec = &dummy_spec;
+ giterr_set(GITERR_NET, "the remote's default branch does not fit the refspec configuration");
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
}
/* Determine the remote tracking reference name from the local master */
@@ -184,21 +186,18 @@ static int update_head_to_remote(
&remote_master_name,
refspec,
git_buf_cstr(&branch))) < 0)
- return error;
+ goto cleanup;
- if (found_branch) {
- error = update_head_to_new_branch(
- repo,
- remote_head_id,
- git_buf_cstr(&branch),
- signature, reflog_message);
- } else {
- error = git_repository_set_head_detached(
- repo, remote_head_id, signature, reflog_message);
- }
+ error = update_head_to_new_branch(
+ repo,
+ remote_head_id,
+ git_buf_cstr(&branch),
+ signature, reflog_message);
+cleanup:
git_buf_free(&remote_master_name);
git_buf_free(&branch);
+
return error;
}
diff --git a/src/merge.c b/src/merge.c
index add7eab64..926a600cc 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -27,6 +27,7 @@
#include "index.h"
#include "filebuf.h"
#include "config.h"
+#include "oidarray.h"
#include "git2/types.h"
#include "git2/repository.h"
@@ -39,6 +40,7 @@
#include "git2/signature.h"
#include "git2/config.h"
#include "git2/tree.h"
+#include "git2/oidarray.h"
#include "git2/sys/index.h"
#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
@@ -139,7 +141,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co
return 0;
}
-int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
+static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two)
{
git_revwalk *walk;
git_vector list;
@@ -173,13 +175,63 @@ int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const
return GIT_ENOTFOUND;
}
+ *out = result;
+ *walk_out = walk;
+
+ return 0;
+
+on_error:
+ git_revwalk_free(walk);
+ return -1;
+
+}
+
+int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ int error;
+ git_revwalk *walk;
+ git_commit_list *result;
+
+ if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
+ return error;
+
git_oid_cpy(out, &result->item->oid);
git_commit_list_free(&result);
git_revwalk_free(walk);
return 0;
+}
+
+int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ int error;
+ git_revwalk *walk;
+ git_commit_list *result, *list;
+ git_array_oid_t array;
+
+ git_array_init(array);
+
+ if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
+ return error;
+
+ list = result;
+ while (list) {
+ git_oid *id = git_array_alloc(array);
+ if (id == NULL)
+ goto on_error;
+
+ git_oid_cpy(id, &list->item->oid);
+ list = list->next;
+ }
+
+ git_oidarray__from_array(out, &array);
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
on_error:
+ git_commit_list_free(&result);
git_revwalk_free(walk);
return -1;
}
diff --git a/src/oidarray.c b/src/oidarray.c
new file mode 100644
index 000000000..1d51a2958
--- /dev/null
+++ b/src/oidarray.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/oidarray.h"
+#include "oidarray.h"
+#include "array.h"
+
+void git_oidarray_free(git_oidarray *arr)
+{
+ git__free(arr->ids);
+}
+
+void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array)
+{
+ arr->count = array->size;
+ arr->ids = array->ptr;
+}
diff --git a/src/oidarray.h b/src/oidarray.h
new file mode 100644
index 000000000..a7215ae6c
--- /dev/null
+++ b/src/oidarray.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_oidarray_h__
+#define INCLUDE_oidarray_h__
+
+#include "common.h"
+#include "git2/oidarray.h"
+#include "array.h"
+
+typedef git_array_t(git_oid) git_array_oid_t;
+
+extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array);
+
+#endif
diff --git a/src/pack.c b/src/pack.c
index b05aa91f1..7c1cfe03e 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -648,9 +648,6 @@ int git_packfile_unpack(
base_type = elem->type;
}
- if (error < 0)
- goto cleanup;
-
switch (base_type) {
case GIT_OBJ_COMMIT:
case GIT_OBJ_TREE:
diff --git a/src/path.c b/src/path.c
index 77f8d8858..d29b992fe 100644
--- a/src/path.c
+++ b/src/path.c
@@ -750,6 +750,61 @@ int git_path_cmp(
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
+int git_path_make_relative(git_buf *path, const char *parent)
+{
+ const char *p, *q, *p_dirsep, *q_dirsep;
+ size_t plen = path->size, newlen, depth = 1, i;
+
+ for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
+ if (*p == '/' && *q == '/') {
+ p_dirsep = p;
+ q_dirsep = q;
+ }
+ else if (*p != *q)
+ break;
+ }
+
+ /* need at least 1 common path segment */
+ if ((p_dirsep == path->ptr || q_dirsep == parent) &&
+ (*p_dirsep != '/' || *q_dirsep != '/')) {
+ giterr_set(GITERR_INVALID,
+ "%s is not a parent of %s", parent, path->ptr);
+ return GIT_ENOTFOUND;
+ }
+
+ if (*p == '/' && !*q)
+ p++;
+ else if (!*p && *q == '/')
+ q++;
+ else if (!*p && !*q)
+ return git_buf_clear(path), 0;
+ else {
+ p = p_dirsep + 1;
+ q = q_dirsep + 1;
+ }
+
+ plen -= (p - path->ptr);
+
+ if (!*q)
+ return git_buf_set(path, p, plen);
+
+ for (; (q = strchr(q, '/')) && *(q + 1); q++)
+ depth++;
+
+ newlen = (depth * 3) + plen;
+
+ if (git_buf_try_grow(path, newlen + 1, 1, 0) < 0)
+ return -1;
+
+ memmove(path->ptr + (depth * 3), p, plen + 1);
+
+ for (i = 0; i < depth; i++)
+ memcpy(path->ptr + (i * 3), "../", 3);
+
+ path->size = newlen;
+ return 0;
+}
+
bool git_path_has_non_ascii(const char *path, size_t pathlen)
{
const uint8_t *scan = (const uint8_t *)path, *end;
diff --git a/src/path.h b/src/path.h
index 46d6efe93..d0a9de707 100644
--- a/src/path.h
+++ b/src/path.h
@@ -197,6 +197,17 @@ extern bool git_path_contains(git_buf *dir, const char *item);
extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
/**
+ * Make the path relative to the given parent path.
+ *
+ * @param path The path to make relative
+ * @param parent The parent path to make path relative to
+ * @return 0 if path was made relative, GIT_ENOTFOUND
+ * if there was not common root between the paths,
+ * or <0.
+ */
+extern int git_path_make_relative(git_buf *path, const char *parent);
+
+/**
* Check if the given path contains the given file.
*
* @param dir Directory path that might contain file
diff --git a/src/remote.c b/src/remote.c
index 46a610c3a..dfad946d5 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -1058,16 +1058,20 @@ static int update_tips_for_spec(
if (autotag && !git_odb_exists(odb, &head->oid))
continue;
- if (git_vector_insert(&update_heads, head) < 0)
+ if (!autotag && git_vector_insert(&update_heads, head) < 0)
goto on_error;
error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
if (error < 0 && error != GIT_ENOTFOUND)
goto on_error;
- if (error == GIT_ENOTFOUND)
+ if (error == GIT_ENOTFOUND) {
memset(&old, 0, GIT_OID_RAWSZ);
+ if (autotag && git_vector_insert(&update_heads, head) < 0)
+ goto on_error;
+ }
+
if (!git_oid__cmp(&old, &head->oid))
continue;
@@ -1942,6 +1946,9 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
if (heads_len == 0)
return GIT_ENOTFOUND;
+ if (strcmp(heads[0]->name, GIT_HEAD_FILE))
+ return GIT_ENOTFOUND;
+
git_buf_sanitize(out);
/* the first one must be HEAD so if that has the symref info, we're done */
if (heads[0]->symref_target)
@@ -1958,6 +1965,9 @@ int git_remote_default_branch(git_buf *out, git_remote *remote)
if (git_oid_cmp(head_id, &heads[i]->oid))
continue;
+ if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
+ continue;
+
if (!guess) {
guess = heads[i];
continue;
diff --git a/src/repository.c b/src/repository.c
index 4f7a5feab..d86d8905a 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -994,7 +994,7 @@ static int repo_init_config(
uint32_t mode)
{
int error = 0;
- git_buf cfg_path = GIT_BUF_INIT;
+ git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT;
git_config *config = NULL;
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
@@ -1019,9 +1019,16 @@ static int repo_init_config(
if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
- if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
- SET_REPO_CONFIG(string, "core.worktree", work_dir);
- else if (is_reinit) {
+ if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ if ((error = git_buf_sets(&worktree_path, work_dir)) < 0)
+ goto cleanup;
+
+ if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK))
+ if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0)
+ goto cleanup;
+
+ SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr);
+ } else if (is_reinit) {
if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear();
}
@@ -1038,6 +1045,7 @@ static int repo_init_config(
cleanup:
git_buf_free(&cfg_path);
+ git_buf_free(&worktree_path);
git_config_free(config);
return error;
@@ -1126,10 +1134,11 @@ static int repo_write_template(
}
static int repo_write_gitlink(
- const char *in_dir, const char *to_repo)
+ const char *in_dir, const char *to_repo, bool use_relative_path)
{
int error;
git_buf buf = GIT_BUF_INIT;
+ git_buf path_to_repo = GIT_BUF_INIT;
struct stat st;
git_path_dirname_r(&buf, to_repo);
@@ -1157,13 +1166,20 @@ static int repo_write_gitlink(
git_buf_clear(&buf);
- error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
+ error = git_buf_sets(&path_to_repo, to_repo);
+
+ if (!error && use_relative_path)
+ error = git_path_make_relative(&path_to_repo, in_dir);
+
+ if (!error)
+ error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr);
if (!error)
error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
cleanup:
git_buf_free(&buf);
+ git_buf_free(&path_to_repo);
return error;
}
@@ -1207,7 +1223,7 @@ static int repo_init_structure(
if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
{
- if (repo_write_gitlink(work_dir, repo_dir) < 0)
+ if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0)
return -1;
}
@@ -1635,7 +1651,7 @@ int git_repository_set_workdir(
if (git_repository_config__weakptr(&config, repo) < 0)
return -1;
- error = repo_write_gitlink(path.ptr, git_repository_path(repo));
+ error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
/* passthrough error means gitlink is unnecessary */
if (error == GIT_PASSTHROUGH)
diff --git a/src/revparse.c b/src/revparse.c
index 60872e187..e0ec3941d 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -205,7 +205,6 @@ cleanup:
static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier)
{
git_reflog *reflog;
- int error = -1;
size_t numentries;
const git_reflog_entry *entry;
bool search_by_pos = (identifier <= 100000000);
@@ -216,21 +215,11 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
numentries = git_reflog_entrycount(reflog);
if (search_by_pos) {
- if (numentries < identifier + 1) {
- giterr_set(
- GITERR_REFERENCE,
- "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
- git_reference_name(ref), numentries, identifier);
-
- error = GIT_ENOTFOUND;
- goto cleanup;
- }
+ if (numentries < identifier + 1)
+ goto notfound;
entry = git_reflog_entry_byindex(reflog, identifier);
git_oid_cpy(oid, git_reflog_entry_id_new(entry));
- error = 0;
- goto cleanup;
-
} else {
size_t i;
git_time commit_time;
@@ -243,16 +232,24 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
continue;
git_oid_cpy(oid, git_reflog_entry_id_new(entry));
- error = 0;
- goto cleanup;
+ break;
}
- error = GIT_ENOTFOUND;
+ if (i == numentries)
+ goto notfound;
}
-cleanup:
git_reflog_free(reflog);
- return error;
+ return 0;
+
+notfound:
+ giterr_set(
+ GITERR_REFERENCE,
+ "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
+ git_reference_name(ref), numentries, identifier);
+
+ git_reflog_free(reflog);
+ return GIT_ENOTFOUND;
}
static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
diff --git a/src/signature.c b/src/signature.c
index 2545b7519..2a16b484a 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -70,9 +70,9 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
if (p->name == NULL || p->email == NULL)
return -1; /* oom */
- if (p->name[0] == '\0') {
+ if (p->name[0] == '\0' || p->email[0] == '\0') {
git_signature_free(p);
- return signature_error("Signature cannot have an empty name");
+ return signature_error("Signature cannot have an empty name or email");
}
p->when.time = time;
diff --git a/src/stash.c b/src/stash.c
index 86e0a627c..22f756e35 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -634,7 +634,8 @@ int git_stash_drop(
entry = git_reflog_entry_byindex(reflog, 0);
git_reference_free(stash);
- if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL) < 0))
+ error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL);
+ if (error < 0)
goto cleanup;
/* We need to undo the writing that we just did */
diff --git a/src/submodule.c b/src/submodule.c
index b1291df8e..ccc8ad117 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -306,6 +306,56 @@ void git_submodule_cache_free(git_repository *repo)
submodule_cache_free(cache);
}
+static int submodule_repo_init(
+ git_repository **out,
+ git_repository *parent_repo,
+ const char *path,
+ const char *url,
+ bool use_gitlink)
+{
+ int error = 0;
+ git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT;
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *subrepo = NULL;
+
+ error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
+ initopt.origin_url = url;
+
+ /* init submodule repository and add origin remote as needed */
+
+ /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
+ * gitlink in the sub-repo workdir directory to that repository
+ *
+ * Old style: sub-repo goes directly into repo/<name>/.git/
+ */
+ if (use_gitlink) {
+ error = git_buf_join3(
+ &repodir, '/', git_repository_path(parent_repo), "modules", path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = workdir.ptr;
+ initopt.flags |=
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+ } else
+ error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
+
+cleanup:
+ git_buf_free(&workdir);
+ git_buf_free(&repodir);
+
+ *out = subrepo;
+
+ return error;
+}
+
int git_submodule_add_setup(
git_submodule **out,
git_repository *repo,
@@ -317,7 +367,6 @@ int git_submodule_add_setup(
git_config_backend *mods = NULL;
git_submodule *sm = NULL;
git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
- git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
git_repository *subrepo = NULL;
assert(repo && url && path);
@@ -371,41 +420,14 @@ int git_submodule_add_setup(
if (error < 0)
goto cleanup;
- /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
- * gitlink in the sub-repo workdir directory to that repository
- *
- * Old style: sub-repo goes directly into repo/<name>/.git/
+ /* if the repo does not already exist, then init a new repo and add it.
+ * Otherwise, just add the existing repo.
*/
-
- initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
- GIT_REPOSITORY_INIT_NO_REINIT;
- initopt.origin_url = real_url.ptr;
-
- if (git_path_exists(name.ptr) &&
- git_path_contains(&name, DOT_GIT))
- {
- /* repo appears to already exist - reinit? */
- }
- else if (use_gitlink) {
- git_buf repodir = GIT_BUF_INIT;
-
- error = git_buf_join3(
- &repodir, '/', git_repository_path(repo), "modules", path);
- if (error < 0)
+ if (!(git_path_exists(name.ptr) &&
+ git_path_contains(&name, DOT_GIT))) {
+ if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
goto cleanup;
-
- initopt.workdir_path = name.ptr;
- initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
-
- error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
-
- git_buf_free(&repodir);
}
- else {
- error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
- }
- if (error < 0)
- goto cleanup;
/* add submodule to hash and "reload" it */
@@ -437,6 +459,23 @@ cleanup:
return error;
}
+int git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink)
+{
+ int error;
+ git_repository *sub_repo = NULL;
+
+ assert(out && sm);
+
+ error = submodule_repo_init(&sub_repo, sm->repo, sm->path, sm->url, use_gitlink);
+
+ *out = sub_repo;
+
+ return error;
+}
+
int git_submodule_add_finalize(git_submodule *sm)
{
int error;
@@ -1897,6 +1936,7 @@ static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
}
+
static void submodule_get_wd_status(
unsigned int *status,
git_submodule *sm,
diff --git a/src/transport.c b/src/transport.c
index d390e1422..d42c92684 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -25,16 +25,13 @@ static git_smart_subtransport_definition ssh_subtransport_definition = { git_sma
#endif
static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
-#ifdef GIT_SSH
-static transport_definition ssh_transport_definition = { "ssh://", git_transport_smart, &ssh_subtransport_definition };
-#else
-static transport_definition dummy_transport_definition = { NULL, git_transport_dummy, NULL };
-#endif
static transport_definition transports[] = {
{ "git://", git_transport_smart, &git_subtransport_definition },
{ "http://", git_transport_smart, &http_subtransport_definition },
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
{ "https://", git_transport_smart, &http_subtransport_definition },
+#endif
{ "file://", git_transport_local, NULL },
#ifdef GIT_SSH
{ "ssh://", git_transport_smart, &ssh_subtransport_definition },
@@ -95,11 +92,6 @@ static int transport_find_fn(
if (!definition && strrchr(url, ':')) {
// re-search transports again with ssh:// as url so that we can find a third party ssh transport
definition = transport_find_by_url("ssh://");
-#ifndef GIT_SSH
- if (!definition) {
- definition = &dummy_transport_definition;
- }
-#endif
}
#ifndef GIT_WIN32
@@ -121,15 +113,6 @@ static int transport_find_fn(
* Public API *
**************/
-int git_transport_dummy(git_transport **transport, git_remote *owner, void *param)
-{
- GIT_UNUSED(transport);
- GIT_UNUSED(owner);
- GIT_UNUSED(param);
- giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
- return -1;
-}
-
int git_transport_new(git_transport **out, git_remote *owner, const char *url)
{
git_transport_cb fn;
@@ -229,24 +212,13 @@ done:
return error;
}
-/* from remote.h */
-int git_remote_valid_url(const char *url)
-{
- git_transport_cb fn;
- void *param;
-
- return !transport_find_fn(&fn, url, &param);
-}
-
int git_remote_supported_url(const char* url)
{
git_transport_cb fn;
void *param;
- if (transport_find_fn(&fn, url, &param) < 0)
- return 0;
-
- return fn != &git_transport_dummy;
+ /* The only error we expect is ENOTFOUND */
+ return !transport_find_fn(&fn, url, &param);
}
int git_transport_init(git_transport *opts, unsigned int version)
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 82891165f..5ca5065ac 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -314,7 +314,7 @@ static int wait_while_ack(gitno_buffer *buf)
break;
if (pkt->type == GIT_PKT_ACK &&
- (pkt->status != GIT_ACK_CONTINUE ||
+ (pkt->status != GIT_ACK_CONTINUE &&
pkt->status != GIT_ACK_COMMON)) {
git__free(pkt);
return 0;
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 8ea4a25d7..a0f6dd722 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -292,6 +292,10 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) {
}
shutdown:
+
+ if (rc != LIBSSH2_ERROR_NONE)
+ ssh_error(session, "error authenticating");
+
libssh2_agent_disconnect(agent);
libssh2_agent_free(agent);
@@ -305,6 +309,7 @@ static int _git_ssh_authenticate_session(
int rc;
do {
+ giterr_clear();
switch (cred->credtype) {
case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
@@ -361,7 +366,8 @@ static int _git_ssh_authenticate_session(
return GIT_EAUTH;
if (rc != LIBSSH2_ERROR_NONE) {
- ssh_error(session, "Failed to authenticate SSH session");
+ if (!giterr_last())
+ ssh_error(session, "Failed to authenticate SSH session");
return -1;
}
diff --git a/tests/commit/signature.c b/tests/commit/signature.c
index e9dcfab41..41a74b999 100644
--- a/tests/commit/signature.c
+++ b/tests/commit/signature.c
@@ -56,8 +56,8 @@ void test_commit_signature__create_empties(void)
cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
- cl_git_pass(try_build_signature("nulltoken", "", 1234567890, 60));
- cl_git_pass(try_build_signature("nulltoken", " ", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60));
}
void test_commit_signature__create_one_char(void)
diff --git a/tests/fetchhead/fetchhead_data.h b/tests/fetchhead/fetchhead_data.h
index 94402abd5..c75b65b90 100644
--- a/tests/fetchhead/fetchhead_data.h
+++ b/tests/fetchhead/fetchhead_data.h
@@ -16,6 +16,11 @@
"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_WILDCARD_DATA2 \
+ "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" \
+
#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" \
@@ -25,6 +30,16 @@
"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_DATA2 \
+ "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" \
+
+#define FETCH_HEAD_NO_MERGE_DATA3 \
+ "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" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
#define FETCH_HEAD_EXPLICIT_DATA \
"0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n"
diff --git a/tests/network/remote/defaultbranch.c b/tests/network/remote/defaultbranch.c
index fa3a329db..243369fa2 100644
--- a/tests/network/remote/defaultbranch.c
+++ b/tests/network/remote/defaultbranch.c
@@ -48,3 +48,61 @@ void test_network_remote_defaultbranch__master_on_detached(void)
cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
assert_default_branch("refs/heads/master");
}
+
+void test_network_remote_defaultbranch__no_default_branch(void)
+{
+ git_remote *remote_b;
+ const git_remote_head **heads;
+ size_t len;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b)));
+ cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_ls(&heads, &len, remote_b));
+ cl_assert_equal_i(0, len);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, remote_b));
+
+ git_remote_free(remote_b);
+}
+
+void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void)
+{
+ git_oid id, id_cloned;
+ git_reference *ref;
+ git_buf buf = GIT_BUF_INIT;
+ git_repository *cloned_repo;
+
+ cl_git_pass(git_reference_name_to_id(&id, g_repo_a, "HEAD"));
+ cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
+ cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/master"));
+ cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/not-good"));
+ cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH));
+ cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote));
+
+ cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL));
+
+ cl_assert(git_repository_head_detached(cloned_repo));
+ cl_git_pass(git_reference_name_to_id(&id_cloned, g_repo_a, "HEAD"));
+ cl_assert(git_oid_equal(&id, &id_cloned));
+
+ git_repository_free(cloned_repo);
+}
+
+void test_network_remote_defaultbranch__unborn_HEAD_with_branches(void)
+{
+ git_reference *ref;
+ git_repository *cloned_repo;
+
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo_a, "HEAD", "refs/heads/i-dont-exist", 1, NULL, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./semi-empty", NULL));
+
+ cl_assert(git_repository_head_unborn(cloned_repo));
+
+ git_repository_free(cloned_repo);
+}
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
index 21c57119a..d176774ea 100644
--- a/tests/network/remote/remotes.c
+++ b/tests/network/remote/remotes.c
@@ -91,26 +91,24 @@ void test_network_remote_remotes__error_when_no_push_available(void)
git_remote_free(r);
}
-void test_network_remote_remotes__parsing_ssh_remote(void)
+void test_network_remote_remotes__supported_urls(void)
{
- cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") );
-}
-
-void test_network_remote_remotes__parsing_local_path_fails_if_path_not_found(void)
-{
- cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") );
-}
+ int ssh_supported = 0, https_supported = 0;
-void test_network_remote_remotes__supported_transport_methods_are_supported(void)
-{
- cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
-}
+#ifdef GIT_SSH
+ ssh_supported = 1;
+#endif
-void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void)
-{
-#ifndef GIT_SSH
- cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") );
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+ https_supported = 1;
#endif
+
+ cl_assert(git_remote_supported_url("git://github.com/libgit2/libgit2"));
+ cl_assert(git_remote_supported_url("http://github.com/libgit2/libgit2"));
+
+ cl_assert_equal_i(ssh_supported, git_remote_supported_url("git@github.com:libgit2/libgit2.git"));
+ cl_assert_equal_i(ssh_supported, git_remote_supported_url("ssh://git@github.com/libgit2/libgit2.git"));
+ cl_assert_equal_i(https_supported, git_remote_supported_url("https://github.com/libgit2/libgit2.git"));
}
void test_network_remote_remotes__refspec_parsing(void)
@@ -511,3 +509,53 @@ void test_network_remote_remotes__query_refspecs(void)
git_remote_free(remote);
}
+
+static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+ char *fetch_refspecs[] = {
+ "refs/heads/first-merge:refs/remotes/origin/first-merge",
+ };
+ git_strarray fetch_refspecs_strarray = {
+ fetch_refspecs,
+ 1,
+ };
+
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_remote_create(out, repo, name, url));
+ cl_git_pass(git_remote_set_fetch_refspecs(*out, &fetch_refspecs_strarray));
+
+ return 0;
+}
+
+void test_network_remote_remotes__single_branch(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+ git_strarray refs;
+ size_t i, count = 0;
+
+ opts.remote_cb = remote_single_branch;
+ opts.checkout_branch = "first-merge";
+
+ cl_git_pass(git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./single-branch", &opts));
+ cl_git_pass(git_reference_list(&refs, repo));
+
+ for (i = 0; i < refs.count; i++) {
+ if (!git__prefixcmp(refs.strings[i], "refs/heads/"))
+ count++;
+ }
+ cl_assert_equal_i(1, count);
+
+ git_repository_free(repo);
+}
+
+void test_network_remote_remotes__restricted_refspecs(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+
+ opts.remote_cb = remote_single_branch;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts));
+}
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 2e51364f6..f88a4d611 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -384,6 +384,9 @@ void test_online_clone__ssh_auth_methods(void)
{
int with_user;
+#ifndef GIT_SSH
+ clar__skip();
+#endif
g_options.remote_callbacks.credentials = check_ssh_auth_methods;
g_options.remote_callbacks.payload = &with_user;
@@ -436,6 +439,9 @@ void test_online_clone__ssh_with_paths(void)
const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+#ifndef GIT_SSH
+ clar__skip();
+#endif
if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0)
clar__skip();
@@ -459,6 +465,9 @@ static int cred_foo_bar(git_cred **cred, const char *url, const char *username_f
void test_online_clone__ssh_cannot_change_username(void)
{
+#ifndef GIT_SSH
+ clar__skip();
+#endif
g_options.remote_callbacks.credentials = cred_foo_bar;
cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 0b3f20db1..3f27e1331 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -67,6 +67,11 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet
void test_online_fetchhead__wildcard_spec(void)
{
fetchhead_test_clone();
+ fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA2);
+ cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
+ cl_git_pass(git_tag_delete(g_repo, "blob"));
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
}
@@ -87,5 +92,12 @@ void test_online_fetchhead__no_merges(void)
cl_git_pass(git_config_delete_entry(config, "branch.master.merge"));
git_config_free(config);
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA2);
+ cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
+ cl_git_pass(git_tag_delete(g_repo, "blob"));
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3);
}
diff --git a/tests/online/push.c b/tests/online/push.c
index 4d88bdf7f..70ec705fe 100644
--- a/tests/online/push.c
+++ b/tests/online/push.c
@@ -864,7 +864,6 @@ void test_online_push__notes(void)
push_status exp_stats[] = { { "refs/notes/commits", 1 } };
expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
const char *specs_del[] = { ":refs/notes/commits" };
- expected_ref exp_refs_del[] = { };
git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
@@ -882,7 +881,7 @@ void test_online_push__notes(void)
do_push(specs_del, ARRAY_SIZE(specs_del),
exp_stats, 1,
- exp_refs_del, ARRAY_SIZE(exp_refs_del), 0, 0, 0);
+ NULL, 0, 0, 0, 0);
git_signature_free(signature);
}
diff --git a/tests/path/core.c b/tests/path/core.c
new file mode 100644
index 000000000..be63e309b
--- /dev/null
+++ b/tests/path/core.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+#include "path.h"
+
+static void test_make_relative(
+ const char *expected_path,
+ const char *path,
+ const char *parent,
+ int expected_status)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_buf_puts(&buf, path);
+ cl_assert_equal_i(expected_status, git_path_make_relative(&buf, parent));
+ cl_assert_equal_s(expected_path, buf.ptr);
+ git_buf_free(&buf);
+}
+
+void test_path_core__make_relative(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to", 0);
+ test_make_relative("bar/foo.c", "/path/to/bar/foo.c", "/path/to", 0);
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0);
+
+ test_make_relative("", "/path/to", "/path/to", 0);
+ test_make_relative("", "/path/to", "/path/to/", 0);
+
+ test_make_relative("../", "/path/to", "/path/to/foo", 0);
+
+ test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar", 0);
+ test_make_relative("../bar/foo.c", "/path/to/bar/foo.c", "/path/to/baz", 0);
+
+ test_make_relative("../../foo.c", "/path/to/foo.c", "/path/to/foo/bar", 0);
+ test_make_relative("../../foo/bar.c", "/path/to/foo/bar.c", "/path/to/bar/foo", 0);
+
+ test_make_relative("../../foo.c", "/foo.c", "/bar/foo", 0);
+
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0);
+ test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar/", 0);
+
+ test_make_relative("foo.c", "d:/path/to/foo.c", "d:/path/to", 0);
+
+ test_make_relative("../foo", "/foo", "/bar", 0);
+ test_make_relative("path/to/foo.c", "/path/to/foo.c", "/", 0);
+ test_make_relative("../foo", "path/to/foo", "path/to/bar", 0);
+
+ test_make_relative("/path/to/foo.c", "/path/to/foo.c", "d:/path/to", GIT_ENOTFOUND);
+ test_make_relative("d:/path/to/foo.c", "d:/path/to/foo.c", "/path/to", GIT_ENOTFOUND);
+
+ test_make_relative("/path/to/foo.c", "/path/to/foo.c", "not-a-rooted-path", GIT_ENOTFOUND);
+ test_make_relative("not-a-rooted-path", "not-a-rooted-path", "/path/to", GIT_ENOTFOUND);
+
+ test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND);
+ test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND);
+}
diff --git a/tests/repo/init.c b/tests/repo/init.c
index aea383c64..999afd625 100644
--- a/tests/repo/init.c
+++ b/tests/repo/init.c
@@ -367,6 +367,84 @@ void test_repo_init__extended_1(void)
cl_fixture_cleanup("root");
}
+void test_repo_init__relative_gitdir(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+
+ opts.workdir_path = "../c_wd";
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../c_wd/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_buf_free(&dot_git_content);
+ git_config_free(cfg);
+ cleanup_repository("root");
+}
+
+void test_repo_init__relative_gitdir_2(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+ git_buf full_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_path_prettify(&full_path, ".", NULL));
+ cl_git_pass(git_buf_joinpath(&full_path, full_path.ptr, "root/b/c_wd"));
+
+ opts.workdir_path = full_path.ptr;
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../c_wd/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_buf_free(&dot_git_content);
+ git_config_free(cfg);
+ cleanup_repository("root");
+}
+
#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
static void assert_hooks_match(
diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c
index 8f1f6ef21..677e1a1b6 100644
--- a/tests/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -135,6 +135,24 @@ void test_revwalk_mergebase__prefer_youngest_merge_base(void)
cl_assert_equal_oid(&expected, &result);
}
+void test_revwalk_mergebase__multiple_merge_bases(void)
+{
+ git_oid one, two, expected1, expected2;
+ git_oidarray result = {NULL, 0};
+
+ cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f "));
+ cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+ cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+
+ cl_git_pass(git_merge_bases(&result, _repo, &one, &two));
+ cl_assert_equal_i(2, result.count);
+ cl_assert_equal_oid(&expected1, &result.ids[0]);
+ cl_assert_equal_oid(&expected2, &result.ids[1]);
+
+ git_oidarray_free(&result);
+}
+
void test_revwalk_mergebase__no_off_by_one_missing(void)
{
git_oid result, one, two;
diff --git a/tests/submodule/add.c b/tests/submodule/add.c
index 9fdc7cc57..10717809e 100644
--- a/tests/submodule/add.c
+++ b/tests/submodule/add.c
@@ -2,6 +2,7 @@
#include "posix.h"
#include "path.h"
#include "submodule_helpers.h"
+#include "fileops.h"
static git_repository *g_repo = NULL;
@@ -29,6 +30,10 @@ static void assert_submodule_url(const char* name, const char *url)
void test_submodule_add__url_absolute(void)
{
git_submodule *sm;
+ git_config *cfg;
+ git_repository *repo;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
g_repo = setup_fixture_submod2();
@@ -51,6 +56,21 @@ void test_submodule_add__url_absolute(void)
cl_assert(git_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD"));
assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git");
+ cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2"));
+
+ /* Verify worktree path is relative */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../../../sm_libgit2/", worktree_path);
+
+ /* Verify gitdir path is relative */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr);
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ git_buf_free(&dot_git_content);
+
/* add a submodule not using a gitlink */
cl_git_pass(
diff --git a/tests/submodule/repository_init.c b/tests/submodule/repository_init.c
new file mode 100644
index 000000000..24b47aff8
--- /dev/null
+++ b/tests/submodule/repository_init.c
@@ -0,0 +1,40 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_repository_init__basic(void)
+{
+ git_submodule *sm;
+ git_repository *repo;
+ git_config *cfg;
+ const char *worktree_path;
+ git_buf dot_git_content = GIT_BUF_INIT;
+
+ g_repo = setup_fixture_submod2();
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_git_pass(git_submodule_repo_init(&repo, sm, 1));
+
+ /* Verify worktree */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree"));
+ cl_assert_equal_s("../../../sm_gitmodules_only/", worktree_path);
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr);
+
+ cl_assert(git_path_isfile("submod2/" "sm_gitmodules_only" "/.git"));
+
+ cl_assert(git_path_isdir("submod2/.git/modules"));
+ cl_assert(git_path_isdir("submod2/.git/modules/" "sm_gitmodules_only"));
+ cl_assert(git_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD"));
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ git_buf_free(&dot_git_content);
+}