summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md14
-rw-r--r--README.md2
-rw-r--r--examples/network/fetch.c2
-rw-r--r--include/git2.h1
-rw-r--r--include/git2/merge.h15
-rw-r--r--include/git2/reflog.h2
-rw-r--r--include/git2/remote.h8
-rw-r--r--include/git2/sys/hashsig.h (renamed from src/hashsig.h)18
-rw-r--r--include/git2/sys/refdb_backend.h13
-rw-r--r--include/git2/transaction.h111
-rw-r--r--include/git2/types.h3
-rw-r--r--src/clone.c4
-rw-r--r--src/config_file.c2
-rw-r--r--src/diff_tform.c2
-rw-r--r--src/filter.c13
-rw-r--r--src/hashsig.c2
-rw-r--r--src/merge.c79
-rw-r--r--src/path.c5
-rw-r--r--src/refdb.c19
-rw-r--r--src/refdb.h3
-rw-r--r--src/refdb_fs.c118
-rw-r--r--src/reflog.c2
-rw-r--r--src/refs.c6
-rw-r--r--src/refs.h2
-rw-r--r--src/remote.c169
-rw-r--r--src/remote.h2
-rw-r--r--src/repository.c4
-rw-r--r--src/signature.c24
-rw-r--r--src/signature.h2
-rw-r--r--src/stash.c30
-rw-r--r--src/transaction.c352
-rw-r--r--tests/config/stress.c12
-rw-r--r--tests/core/buffer.c2
-rw-r--r--tests/fetchhead/nonetwork.c2
-rw-r--r--tests/merge/trees/treediff.c2
-rw-r--r--tests/network/fetchlocal.c4
-rw-r--r--tests/network/remote/local.c114
-rw-r--r--tests/online/fetch.c10
-rw-r--r--tests/online/fetchhead.c8
-rw-r--r--tests/online/push.c2
-rw-r--r--tests/refs/transactions.c110
-rw-r--r--tests/resources/config/config126
-rw-r--r--tests/revwalk/mergebase.c24
-rw-r--r--tests/stash/save.c4
44 files changed, 1190 insertions, 139 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b23e07d93..44fa00d51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,11 @@ v0.21 + 1
* Use a map for the treebuilder, making insertion O(1)
+* Introduce reference transactions, which allow multiple references to
+ be locked at the same time and updates be queued. This also allows
+ us to safely update a reflog with arbitrary contents, as we need to
+ do for stash.
+
* LF -> CRLF filter refuses to handle mixed-EOL files
* LF -> CRLF filter now runs when * text = auto (with Git for Windows 1.9.4)
@@ -56,5 +61,14 @@ v0.21 + 1
* Add support for refspecs with the asterisk in the middle of a
pattern.
+* Fetching now performs opportunistic updates. To achieve this, we
+ introduce a difference between active and passive refspecs, which
+ make git_remote_download and git_remote_fetch to take a list of
+ resfpecs to be the active list, similarly to how git fetch accepts a
+ list on the command-line.
+
* Introduce git_merge_bases() and the git_oidarray type to expose all
merge bases between two commits.
+
+* Introduce git_merge_bases_many() to expose all merge bases between
+ multiple commits.
diff --git a/README.md b/README.md
index a8f546c4b..b315eed1a 100644
--- a/README.md
+++ b/README.md
@@ -212,7 +212,7 @@ workflow, the libgit2 [coding conventions](CONVENTIONS.md), and out list of
License
==================================
-`libgit2` is under GPL2 **with linking exemption**. This means you can link to
+`libgit2` is under GPL2 **with linking exception**. This means you can link to
and use the library from any program, proprietary or open source; paid or
gratis. However, you cannot modify libgit2 and distribute it without
supplying the source.
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 8d882095b..ab78fc9bd 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -36,7 +36,7 @@ static void *download(void *ptr)
// Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you
// inform the user about progress.
- if (git_remote_download(data->remote) < 0) {
+ if (git_remote_download(data->remote, NULL) < 0) {
data->ret = -1;
goto exit;
}
diff --git a/include/git2.h b/include/git2.h
index 6713b4961..baa7fcaf8 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -33,6 +33,7 @@
#include "git2/notes.h"
#include "git2/object.h"
#include "git2/odb.h"
+#include "git2/odb_backend.h"
#include "git2/oid.h"
#include "git2/pack.h"
#include "git2/patch.h"
diff --git a/include/git2/merge.h b/include/git2/merge.h
index bd5ebc1bd..ed1b9a30f 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -352,6 +352,21 @@ GIT_EXTERN(int) git_merge_base_many(
const git_oid input_array[]);
/**
+ * Find all merge bases given a list of commits
+ *
+ * @param out array in which to store the resulting ids
+ * @param repo the repository where the commits exist
+ * @param length The number of commits in the provided `input_array`
+ * @param input_array oids of the commits
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_bases_many(
+ git_oidarray *out,
+ git_repository *repo,
+ size_t length,
+ const git_oid input_array[]);
+
+/**
* Find a merge base in preparation for an octopus merge
*
* @param out the OID of a merge base considering all the commits
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index ac42a231c..c949a28f0 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -102,7 +102,7 @@ GIT_EXTERN(size_t) git_reflog_entrycount(git_reflog *reflog);
* equal to 0 (zero) and less than `git_reflog_entrycount()`.
* @return the entry; NULL if not found
*/
-GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, size_t idx);
+GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(const git_reflog *reflog, size_t idx);
/**
* Remove an entry from the reflog by its index
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 055f5e517..b714d3469 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -306,9 +306,12 @@ GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_r
* The .idx file will be created and both it and the packfile with be
* renamed to their final name.
*
+ * @param remote the remote
+ * @param refspecs the refspecs to use for this negotiation and
+ * download. Use NULL to use the base refspecs
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_remote_download(git_remote *remote);
+GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs);
/**
* Check whether the remote is connected
@@ -373,6 +376,8 @@ GIT_EXTERN(int) git_remote_update_tips(
* disconnect and update the remote-tracking branches.
*
* @param remote the remote to fetch from
+ * @param refspecs the refspecs to use for this fetch. Pass NULL to
+ * use the base refspecs.
* @param signature The identity to use when updating reflogs
* @param reflog_message The message to insert into the reflogs. If NULL, the
* default is "fetch"
@@ -380,6 +385,7 @@ GIT_EXTERN(int) git_remote_update_tips(
*/
GIT_EXTERN(int) git_remote_fetch(
git_remote *remote,
+ const git_strarray *refspecs,
const git_signature *signature,
const char *reflog_message);
diff --git a/src/hashsig.h b/include/git2/sys/hashsig.h
index 8c920cbf1..cd735e1b5 100644
--- a/src/hashsig.h
+++ b/include/git2/sys/hashsig.h
@@ -4,10 +4,12 @@
* 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_hashsig_h__
-#define INCLUDE_hashsig_h__
+#ifndef INCLUDE_sys_hashsig_h__
+#define INCLUDE_sys_hashsig_h__
-#include "common.h"
+#include "git2/common.h"
+
+GIT_BEGIN_DECL
/**
* Similarity signature of line hashes for a buffer
@@ -35,7 +37,7 @@ typedef enum {
* @param buflen The length of the data at `buf`
* @param generate_pairwise_hashes Should pairwise runs be hashed
*/
-extern int git_hashsig_create(
+GIT_EXTERN(int) git_hashsig_create(
git_hashsig **out,
const char *buf,
size_t buflen,
@@ -50,7 +52,7 @@ extern int git_hashsig_create(
* This will return an error if the file doesn't contain enough data to
* compute a valid signature.
*/
-extern int git_hashsig_create_fromfile(
+GIT_EXTERN(int) git_hashsig_create_fromfile(
git_hashsig **out,
const char *path,
git_hashsig_option_t opts);
@@ -58,15 +60,17 @@ extern int git_hashsig_create_fromfile(
/**
* Release memory for a content similarity signature
*/
-extern void git_hashsig_free(git_hashsig *sig);
+GIT_EXTERN(void) git_hashsig_free(git_hashsig *sig);
/**
* Measure similarity between two files
*
* @return <0 for error, [0 to 100] as similarity score
*/
-extern int git_hashsig_compare(
+GIT_EXTERN(int) git_hashsig_compare(
const git_hashsig *a,
const git_hashsig *b);
+GIT_END_DECL
+
#endif
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index 3b216a287..d943e550f 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -153,6 +153,19 @@ struct git_refdb_backend {
* Remove a reflog.
*/
int (*reflog_delete)(git_refdb_backend *backend, const char *name);
+
+ /**
+ * Lock a reference. The opaque parameter will be passed to the unlock function
+ */
+ int (*lock)(void **payload_out, git_refdb_backend *backend, const char *refname);
+
+ /**
+ * Unlock a reference. Only one of target or symbolic_target
+ * will be set. success indicates whether to update the
+ * reference or discard the lock (if it's false)
+ */
+ int (*unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog,
+ const git_reference *ref, const git_signature *sig, const char *message);
};
#define GIT_REFDB_BACKEND_VERSION 1
diff --git a/include/git2/transaction.h b/include/git2/transaction.h
new file mode 100644
index 000000000..64abb0c69
--- /dev/null
+++ b/include/git2/transaction.h
@@ -0,0 +1,111 @@
+/*
+ * 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_transaction_h__
+#define INCLUDE_git_transaction_h__
+
+#include "common.h"
+GIT_BEGIN_DECL
+
+/**
+ * Create a new transaction object
+ *
+ * This does not lock anything, but sets up the transaction object to
+ * know from which repository to lock.
+ *
+ * @param out the resulting transaction
+ * @param repo the repository in which to lock
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transaction_new(git_transaction **out, git_repository *repo);
+
+/**
+ * Lock a reference
+ *
+ * Lock the specified reference. This is the first step to updating a
+ * reference.
+ *
+ * @param tx the transaction
+ * @param refname the reference to lock
+ * @return 0 or an error message
+ */
+GIT_EXTERN(int) git_transaction_lock_ref(git_transaction *tx, const char *refname);
+
+/**
+ * Set the target of a reference
+ *
+ * Set the target of the specified reference. This reference must be
+ * locked.
+ *
+ * @param tx the transaction
+ * @param refname reference to update
+ * @param target target to set the reference to
+ * @param sig signature to use in the reflog; pass NULL to read the identity from the config
+ * @param msg message to use in the reflog
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg);
+
+/**
+ * Set the target of a reference
+ *
+ * Set the target of the specified reference. This reference must be
+ * locked.
+ *
+ * @param tx the transaction
+ * @param refname reference to update
+ * @param target target to set the reference to
+ * @param sig signature to use in the reflog; pass NULL to read the identity from the config
+ * @param msg message to use in the reflog
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg);
+
+/**
+ * Set the reflog of a reference
+ *
+ * Set the specified reference's reflog. If this is combined with
+ * setting the target, that update won't be written to the reflog.
+ *
+ * @param tx the transaction
+ * @param refname the reference whose reflog to set
+ * @param reflog the reflog as it should be written out
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog);
+
+/**
+ * Remove a reference
+ *
+ * @param tx the transaction
+ * @param refname the reference to remove
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_remove(git_transaction *tx, const char *refname);
+
+/**
+ * Commit the changes from the transaction
+ *
+ * Perform the changes that have been queued. The updates will be made
+ * one by one, and the first failure will stop the processing.
+ *
+ * @param tx the transaction
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transaction_commit(git_transaction *tx);
+
+/**
+ * Free the resources allocated by this transaction
+ *
+ * If any references remain locked, they will be unlocked without any
+ * changes made to them.
+ *
+ * @param tx the transaction
+ */
+GIT_EXTERN(void) git_transaction_free(git_transaction *tx);
+
+GIT_END_DECL
+#endif
diff --git a/include/git2/types.h b/include/git2/types.h
index 7ee7cc344..14b7071d2 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -171,6 +171,9 @@ typedef struct git_reference git_reference;
/** Iterator for references */
typedef struct git_reference_iterator git_reference_iterator;
+/** Transactional interface to references */
+typedef struct git_transaction git_transaction;
+
/** Merge heads, the input to merge */
typedef struct git_merge_head git_merge_head;
diff --git a/src/clone.c b/src/clone.c
index 43b839003..f18f07611 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -358,7 +358,7 @@ static int clone_into(git_repository *repo, git_remote *_remote, const git_check
git_remote_set_update_fetchhead(remote, 0);
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
- if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
+ if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0)
goto cleanup;
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
@@ -553,7 +553,7 @@ static int clone_local_into(git_repository *repo, git_remote *remote, const git_
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
- if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
+ if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0)
goto cleanup;
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
diff --git a/src/config_file.c b/src/config_file.c
index 7106f18db..8f55c42f3 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1163,7 +1163,7 @@ static int strip_comments(char *line, int in_quotes)
}
/* skip any space at the end */
- if (ptr > line && git__isspace(ptr[-1])) {
+ while (ptr > line && git__isspace(ptr[-1])) {
ptr--;
}
ptr[0] = '\0';
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 423a0ca33..9ebce06a0 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -8,9 +8,9 @@
#include "git2/config.h"
#include "git2/blob.h"
+#include "git2/sys/hashsig.h"
#include "diff.h"
-#include "hashsig.h"
#include "path.h"
#include "fileops.h"
#include "config.h"
diff --git a/src/filter.c b/src/filter.c
index b9e4f9ec8..b5a8bdd66 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -38,7 +38,7 @@ struct git_filter_list {
};
typedef struct {
- const char *filter_name;
+ char *filter_name;
git_filter *filter;
int priority;
int initialized;
@@ -75,6 +75,7 @@ static void filter_registry_shutdown(void)
fdef->initialized = false;
}
+ git__free(fdef->filter_name);
git__free(fdef->attrdata);
git__free(fdef);
}
@@ -230,6 +231,8 @@ int git_filter_register(
size_t nattr = 0, nmatch = 0;
git_buf attrs = GIT_BUF_INIT;
+ assert(name && filter);
+
if (filter_registry_initialize() < 0)
return -1;
@@ -246,7 +249,9 @@ int git_filter_register(
sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1);
GITERR_CHECK_ALLOC(fdef);
- fdef->filter_name = name;
+ fdef->filter_name = git__strdup(name);
+ GITERR_CHECK_ALLOC(fdef->filter_name);
+
fdef->filter = filter;
fdef->priority = priority;
fdef->nattrs = nattr;
@@ -256,6 +261,7 @@ int git_filter_register(
filter_def_set_attrs(fdef);
if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) {
+ git__free(fdef->filter_name);
git__free(fdef->attrdata);
git__free(fdef);
return -1;
@@ -270,6 +276,8 @@ int git_filter_unregister(const char *name)
size_t pos;
git_filter_def *fdef;
+ assert(name);
+
/* cannot unregister default filters */
if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name);
@@ -288,6 +296,7 @@ int git_filter_unregister(const char *name)
fdef->initialized = false;
}
+ git__free(fdef->filter_name);
git__free(fdef->attrdata);
git__free(fdef);
diff --git a/src/hashsig.c b/src/hashsig.c
index 109f966ba..a6d5f2041 100644
--- a/src/hashsig.c
+++ b/src/hashsig.c
@@ -4,7 +4,7 @@
* 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 "hashsig.h"
+#include "git2/sys/hashsig.h"
#include "fileops.h"
#include "util.h"
diff --git a/src/merge.c b/src/merge.c
index ddeea8752..8252f6767 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -22,7 +22,6 @@
#include "tree.h"
#include "merge_file.h"
#include "blob.h"
-#include "hashsig.h"
#include "oid.h"
#include "index.h"
#include "filebuf.h"
@@ -42,6 +41,7 @@
#include "git2/tree.h"
#include "git2/oidarray.h"
#include "git2/sys/index.h"
+#include "git2/sys/hashsig.h"
#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode)
@@ -62,16 +62,14 @@ struct merge_diff_df_data {
/* Merge base computation */
-int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
+int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[])
{
- git_revwalk *walk;
+ git_revwalk *walk = NULL;
git_vector list;
git_commit_list *result = NULL;
+ git_commit_list_node *commit;
int error = -1;
unsigned int i;
- git_commit_list_node *commit;
-
- assert(out && repo && input_array);
if (length < 2) {
giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
@@ -82,37 +80,92 @@ int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const
return -1;
if (git_revwalk_new(&walk, repo) < 0)
- goto cleanup;
+ goto on_error;
for (i = 1; i < length; i++) {
commit = git_revwalk__commit_lookup(walk, &input_array[i]);
if (commit == NULL)
- goto cleanup;
+ goto on_error;
git_vector_insert(&list, commit);
}
commit = git_revwalk__commit_lookup(walk, &input_array[0]);
if (commit == NULL)
- goto cleanup;
+ goto on_error;
if (git_merge__bases_many(&result, walk, commit, &list) < 0)
- goto cleanup;
+ goto on_error;
if (!result) {
giterr_set(GITERR_MERGE, "No merge base found");
error = GIT_ENOTFOUND;
- goto cleanup;
+ goto on_error;
}
+ *out = result;
+ *walk_out = walk;
+
+ git_vector_free(&list);
+ return 0;
+
+on_error:
+ git_vector_free(&list);
+ git_revwalk_free(walk);
+ return error;
+}
+
+int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_revwalk *walk;
+ git_commit_list *result = NULL;
+ int error = 0;
+
+ assert(out && repo && input_array);
+
+ if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
+ return error;
+
git_oid_cpy(out, &result->item->oid);
- error = 0;
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
+}
+
+int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_revwalk *walk;
+ git_commit_list *list, *result = NULL;
+ int error = 0;
+ git_array_oid_t array;
+
+ assert(out && repo && input_array);
+
+ if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
+ return error;
+
+ git_array_init(array);
+
+ list = result;
+ while (list) {
+ git_oid *id = git_array_alloc(array);
+ if (id == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+
+ git_oid_cpy(id, &list->item->oid);
+ list = list->next;
+ }
+
+ git_oidarray__from_array(out, &array);
cleanup:
git_commit_list_free(&result);
git_revwalk_free(walk);
- git_vector_free(&list);
+
return error;
}
diff --git a/src/path.c b/src/path.c
index 67133f97e..21b6e18d6 100644
--- a/src/path.c
+++ b/src/path.c
@@ -768,7 +768,7 @@ int git_path_cmp(
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;
+ size_t plen = path->size, newlen, depth = 1, i, offset;
for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
if (*p == '/' && *q == '/') {
@@ -808,8 +808,11 @@ int git_path_make_relative(git_buf *path, const char *parent)
newlen = (depth * 3) + plen;
+ /* save the offset as we might realllocate the pointer */
+ offset = p - path->ptr;
if (git_buf_try_grow(path, newlen + 1, 1, 0) < 0)
return -1;
+ p = path->ptr + offset;
memmove(path->ptr + (depth * 3), p, plen + 1);
diff --git a/src/refdb.c b/src/refdb.c
index 69bf74734..16fb519a6 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -242,3 +242,22 @@ int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
return 0;
}
+
+int git_refdb_lock(void **payload, git_refdb *db, const char *refname)
+{
+ assert(payload && db && refname);
+
+ if (!db->backend->lock) {
+ giterr_set(GITERR_REFERENCE, "backend does not support locking");
+ return -1;
+ }
+
+ return db->backend->lock(payload, db->backend, refname);
+}
+
+int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message)
+{
+ assert(db);
+
+ return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message);
+}
diff --git a/src/refdb.h b/src/refdb.h
index cbad86faf..4ee3b8065 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -51,6 +51,7 @@ int git_refdb_reflog_write(git_reflog *reflog);
int git_refdb_has_log(git_refdb *db, const char *refname);
int git_refdb_ensure_log(git_refdb *refdb, const char *refname);
-
+int git_refdb_lock(void **payload, git_refdb *db, const char *refname);
+int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message);
#endif
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 0e36ca8ac..f39ba4f9c 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -745,6 +745,57 @@ static int loose_commit(git_filebuf *file, const git_reference *ref)
return git_filebuf_commit(file);
}
+static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname)
+{
+ int error;
+ git_filebuf *lock;
+ refdb_fs_backend *backend = (refdb_fs_backend *) _backend;
+
+ lock = git__calloc(1, sizeof(git_filebuf));
+ GITERR_CHECK_ALLOC(lock);
+
+ if ((error = loose_lock(lock, backend, refname)) < 0) {
+ git__free(lock);
+ return error;
+ }
+
+ *out = lock;
+ return 0;
+}
+
+static int refdb_fs_backend__write_tail(
+ git_refdb_backend *_backend,
+ const git_reference *ref,
+ git_filebuf *file,
+ int update_reflog,
+ const git_signature *who,
+ const char *message,
+ const git_oid *old_id,
+ const char *old_target);
+
+static int refdb_fs_backend__delete_tail(
+ git_refdb_backend *_backend,
+ git_filebuf *file,
+ const char *ref_name,
+ const git_oid *old_id, const char *old_target);
+
+static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
+ const git_reference *ref, const git_signature *sig, const char *message)
+{
+ git_filebuf *lock = (git_filebuf *) payload;
+ int error = 0;
+
+ if (success == 2)
+ error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
+ else if (success)
+ error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, sig, message, NULL, NULL);
+ else
+ git_filebuf_cleanup(lock);
+
+ git__free(lock);
+ return error;
+}
+
/*
* Find out what object this reference resolves to.
*
@@ -1063,7 +1114,6 @@ cleanup:
return error;
}
-
static int refdb_fs_backend__write(
git_refdb_backend *_backend,
const git_reference *ref,
@@ -1075,9 +1125,7 @@ static int refdb_fs_backend__write(
{
refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_filebuf file = GIT_FILEBUF_INIT;
- int error = 0, cmp = 0, should_write;
- const char *new_target = NULL;
- const git_oid *new_id = NULL;
+ int error = 0;
assert(backend);
@@ -1089,6 +1137,24 @@ static int refdb_fs_backend__write(
if ((error = loose_lock(&file, backend, ref->name)) < 0)
return error;
+ return refdb_fs_backend__write_tail(_backend, ref, &file, true, who, message, old_id, old_target);
+}
+
+static int refdb_fs_backend__write_tail(
+ git_refdb_backend *_backend,
+ const git_reference *ref,
+ git_filebuf *file,
+ int update_reflog,
+ const git_signature *who,
+ const char *message,
+ const git_oid *old_id,
+ const char *old_target)
+{
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+ int error = 0, cmp = 0, should_write;
+ const char *new_target = NULL;
+ const git_oid *new_id = NULL;
+
if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
goto on_error;
@@ -1113,20 +1179,22 @@ static int refdb_fs_backend__write(
goto on_error; /* not really error */
}
- if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0)
- goto on_error;
-
- if (should_write) {
- if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
- goto on_error;
- if ((error = maybe_append_head(backend, ref, who, message)) < 0)
+ if (update_reflog) {
+ if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0)
goto on_error;
+
+ if (should_write) {
+ if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
+ goto on_error;
+ if ((error = maybe_append_head(backend, ref, who, message)) < 0)
+ goto on_error;
+ }
}
- return loose_commit(&file, ref);
+ return loose_commit(file, ref);
on_error:
- git_filebuf_cleanup(&file);
+ git_filebuf_cleanup(file);
return error;
}
@@ -1136,17 +1204,29 @@ static int refdb_fs_backend__delete(
const git_oid *old_id, const char *old_target)
{
refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- git_buf loose_path = GIT_BUF_INIT;
- size_t pack_pos;
git_filebuf file = GIT_FILEBUF_INIT;
- int error = 0, cmp = 0;
- bool loose_deleted = 0;
+ int error = 0;
assert(backend && ref_name);
if ((error = loose_lock(&file, backend, ref_name)) < 0)
return error;
+ return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
+}
+
+static int refdb_fs_backend__delete_tail(
+ git_refdb_backend *_backend,
+ git_filebuf *file,
+ const char *ref_name,
+ const git_oid *old_id, const char *old_target)
+{
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+ git_buf loose_path = GIT_BUF_INIT;
+ size_t pack_pos;
+ int error = 0, cmp = 0;
+ bool loose_deleted = 0;
+
error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
if (error < 0)
goto cleanup;
@@ -1192,7 +1272,7 @@ static int refdb_fs_backend__delete(
error = packed_write(backend);
cleanup:
- git_filebuf_cleanup(&file);
+ git_filebuf_cleanup(file);
return error;
}
@@ -1837,6 +1917,8 @@ int git_refdb_backend_fs(
backend->parent.del = &refdb_fs_backend__delete;
backend->parent.rename = &refdb_fs_backend__rename;
backend->parent.compress = &refdb_fs_backend__compress;
+ backend->parent.lock = &refdb_fs_backend__lock;
+ backend->parent.unlock = &refdb_fs_backend__unlock;
backend->parent.has_log = &refdb_reflog_fs__has_log;
backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
backend->parent.free = &refdb_fs_backend__free;
diff --git a/src/reflog.c b/src/reflog.c
index 8e41621ea..22aa7d8ed 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -148,7 +148,7 @@ size_t git_reflog_entrycount(git_reflog *reflog)
return reflog->entries.length;
}
-const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx)
+const git_reflog_entry * git_reflog_entry_byindex(const git_reflog *reflog, size_t idx)
{
assert(reflog);
diff --git a/src/refs.c b/src/refs.c
index 1603876da..08e407e48 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -411,7 +411,7 @@ static int reference__create(
return 0;
}
-static int log_signature(git_signature **out, git_repository *repo)
+int git_reference__log_signature(git_signature **out, git_repository *repo)
{
int error;
git_signature *who;
@@ -441,7 +441,7 @@ int git_reference_create_matching(
assert(id);
if (!signature) {
- if ((error = log_signature(&who, repo)) < 0)
+ if ((error = git_reference__log_signature(&who, repo)) < 0)
return error;
else
signature = who;
@@ -482,7 +482,7 @@ int git_reference_symbolic_create_matching(
assert(target);
if (!signature) {
- if ((error = log_signature(&who, repo)) < 0)
+ if ((error = git_reference__log_signature(&who, repo)) < 0)
return error;
else
signature = who;
diff --git a/src/refs.h b/src/refs.h
index 2e79bdfca..c6ec429a5 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -98,4 +98,6 @@ int git_reference_lookup_resolved(
const char *name,
int max_deref);
+int git_reference__log_signature(git_signature **out, git_repository *repo);
+
#endif
diff --git a/src/remote.c b/src/remote.c
index dfad946d5..dc9cae133 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -21,7 +21,7 @@
static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
-static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
+static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
{
git_refspec *spec;
@@ -34,7 +34,7 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
}
spec->push = !is_fetch;
- if (git_vector_insert(&remote->refspecs, spec) < 0) {
+ if (git_vector_insert(vector, spec) < 0) {
git_refspec__free(spec);
git__free(spec);
return -1;
@@ -43,6 +43,11 @@ static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
return 0;
}
+static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
+{
+ return add_refspec_to(&remote->refspecs, string, is_fetch);
+}
+
static int download_tags_value(git_remote *remote, git_config *cfg)
{
const git_config_entry *ce;
@@ -370,6 +375,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
+ git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 ||
git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
error = -1;
goto cleanup;
@@ -813,20 +819,43 @@ static int ls_to_vector(git_vector *out, git_remote *remote)
return 0;
}
-int git_remote_download(git_remote *remote)
+int git_remote_download(git_remote *remote, const git_strarray *refspecs)
{
- int error;
- git_vector refs;
+ int error = -1;
+ size_t i;
+ git_vector refs, specs, *to_active;
assert(remote);
if (ls_to_vector(&refs, remote) < 0)
return -1;
+ if ((git_vector_init(&specs, 0, NULL)) < 0)
+ goto on_error;
+
+ remote->passed_refspecs = 0;
+ if (!refspecs) {
+ to_active = &remote->refspecs;
+ } else {
+ for (i = 0; i < refspecs->count; i++) {
+ if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0)
+ goto on_error;
+ }
+
+ to_active = &specs;
+ remote->passed_refspecs = 1;
+ }
+
+ free_refspecs(&remote->passive_refspecs);
+ if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0)
+ goto on_error;
+
free_refspecs(&remote->active_refspecs);
+ error = dwim_refspecs(&remote->active_refspecs, to_active, &refs);
- error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs);
git_vector_free(&refs);
+ free_refspecs(&specs);
+ git_vector_free(&specs);
if (error < 0)
return error;
@@ -835,10 +864,17 @@ int git_remote_download(git_remote *remote)
return error;
return git_fetch_download_pack(remote);
+
+on_error:
+ git_vector_free(&refs);
+ free_refspecs(&specs);
+ git_vector_free(&specs);
+ return error;
}
int git_remote_fetch(
git_remote *remote,
+ const git_strarray *refspecs,
const git_signature *signature,
const char *reflog_message)
{
@@ -849,7 +885,7 @@ int git_remote_fetch(
if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0)
return error;
- error = git_remote_download(remote);
+ error = git_remote_download(remote, refspecs);
/* We don't need to be connected anymore */
git_remote_disconnect(remote);
@@ -1106,6 +1142,96 @@ on_error:
}
+/**
+ * Iteration over the three vectors, with a pause whenever we find a match
+ *
+ * On each stop, we store the iteration stat in the inout i,j,k
+ * parameters, and return the currently matching passive refspec as
+ * well as the head which we matched.
+ */
+static int next_head(const git_remote *remote, git_vector *refs,
+ git_refspec **out_spec, git_remote_head **out_head,
+ size_t *out_i, size_t *out_j, size_t *out_k)
+{
+ const git_vector *active, *passive;
+ git_remote_head *head;
+ git_refspec *spec, *passive_spec;
+ size_t i, j, k;
+
+ active = &remote->active_refspecs;
+ passive = &remote->passive_refspecs;
+
+ i = *out_i;
+ j = *out_j;
+ k = *out_k;
+
+ for (; i < refs->length; i++) {
+ head = git_vector_get(refs, i);
+
+ if (!git_reference_is_valid_name(head->name))
+ continue;
+
+ for (; j < active->length; j++) {
+ spec = git_vector_get(active, j);
+
+ if (!git_refspec_src_matches(spec, head->name))
+ continue;
+
+ for (; k < passive->length; k++) {
+ passive_spec = git_vector_get(passive, k);
+
+ if (!git_refspec_src_matches(passive_spec, head->name))
+ continue;
+
+ *out_spec = passive_spec;
+ *out_head = head;
+ *out_i = i;
+ *out_j = j;
+ *out_k = k + 1;
+ return 0;
+
+ }
+ k = 0;
+ }
+ j = 0;
+ }
+
+ return GIT_ITEROVER;
+}
+
+static int opportunistic_updates(const git_remote *remote, git_vector *refs, const git_signature *sig, const char *msg)
+{
+ size_t i, j, k;
+ git_refspec *spec;
+ git_remote_head *head;
+ git_reference *ref;
+ git_buf refname = GIT_BUF_INIT;
+ int error;
+
+ i = j = k = 0;
+
+ while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
+ /*
+ * If we got here, there is a refspec which was used
+ * for fetching which matches the source of one of the
+ * passive refspecs, so we should update that
+ * remote-tracking branch, but not add it to
+ * FETCH_HEAD
+ */
+
+ if ((error = git_refspec_transform(&refname, spec, head->name)) < 0)
+ return error;
+
+ error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, sig, msg);
+ git_buf_free(&refname);
+
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
int git_remote_update_tips(
git_remote *remote,
const git_signature *signature,
@@ -1136,6 +1262,10 @@ int git_remote_update_tips(
goto out;
}
+ /* only try to do opportunisitic updates if the refpec lists differ */
+ if (remote->passed_refspecs)
+ error = opportunistic_updates(remote, &refs, signature, reflog_message);
+
out:
git_vector_free(&refs);
git_refspec__free(&tagspec);
@@ -1189,6 +1319,9 @@ void git_remote_free(git_remote *remote)
free_refspecs(&remote->active_refspecs);
git_vector_free(&remote->active_refspecs);
+ free_refspecs(&remote->passive_refspecs);
+ git_vector_free(&remote->passive_refspecs);
+
git__free(remote->url);
git__free(remote->pushurl);
git__free(remote->name);
@@ -1640,27 +1773,14 @@ void git_remote_clear_refspecs(git_remote *remote)
git_vector_clear(&remote->refspecs);
}
-static int add_and_dwim(git_remote *remote, const char *str, int push)
-{
- git_refspec *spec;
- git_vector *vec;
-
- if (add_refspec(remote, str, !push) < 0)
- return -1;
-
- vec = &remote->refspecs;
- spec = git_vector_get(vec, vec->length - 1);
- return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs);
-}
-
int git_remote_add_fetch(git_remote *remote, const char *refspec)
{
- return add_and_dwim(remote, refspec, false);
+ return add_refspec(remote, refspec, true);
}
int git_remote_add_push(git_remote *remote, const char *refspec)
{
- return add_and_dwim(remote, refspec, true);
+ return add_refspec(remote, refspec, false);
}
static int set_refspecs(git_remote *remote, git_strarray *array, int push)
@@ -1688,10 +1808,7 @@ static int set_refspecs(git_remote *remote, git_strarray *array, int push)
return -1;
}
- free_refspecs(&remote->active_refspecs);
- git_vector_clear(&remote->active_refspecs);
-
- return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs);
+ return 0;
}
int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array)
diff --git a/src/remote.h b/src/remote.h
index f88601e9b..b79ace438 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -23,6 +23,7 @@ struct git_remote {
git_vector refs;
git_vector refspecs;
git_vector active_refspecs;
+ git_vector passive_refspecs;
git_transport_cb transport_cb;
void *transport_cb_payload;
git_transport *transport;
@@ -32,6 +33,7 @@ struct git_remote {
unsigned int need_pack;
git_remote_autotag_option_t download_tags;
int update_fetchhead;
+ int passed_refspecs;
};
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
diff --git a/src/repository.c b/src/repository.c
index 51d39eb6d..f032c899d 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1567,8 +1567,10 @@ int git_repository_head_unborn(git_repository *repo)
error = git_repository_head(&ref, repo);
git_reference_free(ref);
- if (error == GIT_EUNBORNBRANCH)
+ if (error == GIT_EUNBORNBRANCH) {
+ giterr_clear();
return 1;
+ }
if (error < 0)
return -1;
diff --git a/src/signature.c b/src/signature.c
index 2a16b484a..514b153ac 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -106,6 +106,30 @@ int git_signature_dup(git_signature **dest, const git_signature *source)
return 0;
}
+int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool)
+{
+ git_signature *signature;
+
+ if (source == NULL)
+ return 0;
+
+ signature = git_pool_mallocz(pool, sizeof(git_signature));
+ GITERR_CHECK_ALLOC(signature);
+
+ signature->name = git_pool_strdup(pool, source->name);
+ GITERR_CHECK_ALLOC(signature->name);
+
+ signature->email = git_pool_strdup(pool, source->email);
+ GITERR_CHECK_ALLOC(signature->email);
+
+ signature->when.time = source->when.time;
+ signature->when.offset = source->when.offset;
+
+ *dest = signature;
+
+ return 0;
+}
+
int git_signature_now(git_signature **sig_out, const char *name, const char *email)
{
time_t now;
diff --git a/src/signature.h b/src/signature.h
index 24655cbf5..eb71db7db 100644
--- a/src/signature.h
+++ b/src/signature.h
@@ -15,4 +15,6 @@
int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);
void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig);
+int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool);
+
#endif
diff --git a/src/stash.c b/src/stash.c
index caffd0cea..cad87d120 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -15,6 +15,7 @@
#include "git2/status.h"
#include "git2/checkout.h"
#include "git2/index.h"
+#include "git2/transaction.h"
#include "signature.h"
static int create_error(int error, const char *msg)
@@ -601,14 +602,21 @@ int git_stash_drop(
git_repository *repo,
size_t index)
{
- git_reference *stash;
+ git_transaction *tx;
+ git_reference *stash = NULL;
git_reflog *reflog = NULL;
size_t max;
int error;
- if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ if ((error = git_transaction_new(&tx, repo)) < 0)
return error;
+ if ((error = git_transaction_lock_ref(tx, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup;
@@ -623,29 +631,25 @@ int git_stash_drop(
if ((error = git_reflog_drop(reflog, index, true)) < 0)
goto cleanup;
- if ((error = git_reflog_write(reflog)) < 0)
+ if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0)
goto cleanup;
if (max == 1) {
- error = git_reference_delete(stash);
- git_reference_free(stash);
- stash = NULL;
+ if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
} else if (index == 0) {
const git_reflog_entry *entry;
entry = git_reflog_entry_byindex(reflog, 0);
-
- git_reference_free(stash);
- error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL);
- if (error < 0)
+ if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0)
goto cleanup;
-
- /* We need to undo the writing that we just did */
- error = git_reflog_write(reflog);
}
+ error = git_transaction_commit(tx);
+
cleanup:
git_reference_free(stash);
+ git_transaction_free(tx);
git_reflog_free(reflog);
return error;
}
diff --git a/src/transaction.c b/src/transaction.c
new file mode 100644
index 000000000..ebb943cea
--- /dev/null
+++ b/src/transaction.c
@@ -0,0 +1,352 @@
+/*
+ * 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 "common.h"
+#include "repository.h"
+#include "strmap.h"
+#include "refdb.h"
+#include "pool.h"
+#include "reflog.h"
+#include "signature.h"
+
+#include "git2/signature.h"
+#include "git2/sys/refs.h"
+#include "git2/sys/refdb_backend.h"
+
+GIT__USE_STRMAP;
+
+typedef struct {
+ const char *name;
+ void *payload;
+
+ git_ref_t ref_type;
+ union {
+ git_oid id;
+ char *symbolic;
+ } target;
+ git_reflog *reflog;
+
+ const char *message;
+ git_signature *sig;
+
+ unsigned int committed :1,
+ remove :1;
+} transaction_node;
+
+struct git_transaction {
+ git_repository *repo;
+ git_refdb *db;
+
+ git_strmap *locks;
+ git_pool pool;
+};
+
+int git_transaction_new(git_transaction **out, git_repository *repo)
+{
+ int error;
+ git_pool pool;
+ git_transaction *tx = NULL;
+
+ assert(out && repo);
+
+ if ((error = git_pool_init(&pool, 1, 0)) < 0)
+ return error;
+
+ tx = git_pool_mallocz(&pool, sizeof(git_transaction));
+ if (!tx) {
+ error = -1;
+ goto on_error;
+ }
+
+ if ((error = git_strmap_alloc(&tx->locks)) < 0) {
+ error = -1;
+ goto on_error;
+ }
+
+ if ((error = git_repository_refdb(&tx->db, repo)) < 0)
+ goto on_error;
+
+ memcpy(&tx->pool, &pool, sizeof(git_pool));
+ tx->repo = repo;
+ *out = tx;
+ return 0;
+
+on_error:
+ git_pool_clear(&pool);
+ return error;
+}
+
+int git_transaction_lock_ref(git_transaction *tx, const char *refname)
+{
+ int error;
+ transaction_node *node;
+
+ assert(tx && refname);
+
+ node = git_pool_mallocz(&tx->pool, sizeof(transaction_node));
+ GITERR_CHECK_ALLOC(node);
+
+ node->name = git_pool_strdup(&tx->pool, refname);
+ GITERR_CHECK_ALLOC(node->name);
+
+ if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0)
+ return error;
+
+ git_strmap_insert(tx->locks, node->name, node, error);
+ if (error < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
+
+ return error;
+}
+
+static int find_locked(transaction_node **out, git_transaction *tx, const char *refname)
+{
+ git_strmap_iter pos;
+ transaction_node *node;
+
+ pos = git_strmap_lookup_index(tx->locks, refname);
+ if (!git_strmap_valid_index(tx->locks, pos)) {
+ giterr_set(GITERR_REFERENCE, "the specified reference is not locked");
+ return GIT_ENOTFOUND;
+ }
+
+ node = git_strmap_value_at(tx->locks, pos);
+
+ *out = node;
+ return 0;
+}
+
+static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg)
+{
+ if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0)
+ return -1;
+
+ if (!node->sig) {
+ git_signature *tmp;
+ int error;
+
+ if (git_reference__log_signature(&tmp, tx->repo) < 0)
+ return -1;
+
+ /* make sure the sig we use is in our pool */
+ error = git_signature__pdup(&node->sig, tmp, &tx->pool);
+ git_signature_free(tmp);
+ if (error < 0)
+ return error;
+ }
+
+ if (msg) {
+ node->message = git_pool_strdup(&tx->pool, msg);
+ GITERR_CHECK_ALLOC(node->message);
+ }
+
+ return 0;
+}
+
+int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg)
+{
+ int error;
+ transaction_node *node;
+
+ assert(tx && refname && target);
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ if ((error = copy_common(node, tx, sig, msg)) < 0)
+ return error;
+
+ git_oid_cpy(&node->target.id, target);
+ node->ref_type = GIT_REF_OID;
+
+ return 0;
+}
+
+int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg)
+{
+ int error;
+ transaction_node *node;
+
+ assert(tx && refname && target);
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ if ((error = copy_common(node, tx, sig, msg)) < 0)
+ return error;
+
+ node->target.symbolic = git_pool_strdup(&tx->pool, target);
+ GITERR_CHECK_ALLOC(node->target.symbolic);
+ node->ref_type = GIT_REF_SYMBOLIC;
+
+ return 0;
+}
+
+int git_transaction_remove(git_transaction *tx, const char *refname)
+{
+ int error;
+ transaction_node *node;
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ node->remove = true;
+ node->ref_type = GIT_REF_OID; /* the id will be ignored */
+
+ return 0;
+}
+
+static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool)
+{
+ git_reflog *reflog;
+ git_reflog_entry *entries;
+ size_t len, i;
+
+ reflog = git_pool_mallocz(pool, sizeof(git_reflog));
+ GITERR_CHECK_ALLOC(reflog);
+
+ reflog->ref_name = git_pool_strdup(pool, in->ref_name);
+ GITERR_CHECK_ALLOC(reflog->ref_name);
+
+ len = in->entries.length;
+ reflog->entries.length = len;
+ reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *));
+ GITERR_CHECK_ALLOC(reflog->entries.contents);
+
+ entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry));
+ GITERR_CHECK_ALLOC(entries);
+
+ for (i = 0; i < len; i++) {
+ const git_reflog_entry *src;
+ git_reflog_entry *tgt;
+
+ tgt = &entries[i];
+ reflog->entries.contents[i] = tgt;
+
+ src = git_vector_get(&in->entries, i);
+ git_oid_cpy(&tgt->oid_old, &src->oid_old);
+ git_oid_cpy(&tgt->oid_cur, &src->oid_cur);
+
+ tgt->msg = git_pool_strdup(pool, src->msg);
+ GITERR_CHECK_ALLOC(tgt->msg);
+
+ if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0)
+ return -1;
+ }
+
+
+ *out = reflog;
+ return 0;
+}
+
+int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog)
+{
+ int error;
+ transaction_node *node;
+
+ assert(tx && refname && reflog);
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0)
+ return error;
+
+ return 0;
+}
+
+static int update_target(git_refdb *db, transaction_node *node)
+{
+ git_reference *ref;
+ int error, update_reflog;
+
+ if (node->ref_type == GIT_REF_OID) {
+ ref = git_reference__alloc(node->name, &node->target.id, NULL);
+ } else if (node->ref_type == GIT_REF_SYMBOLIC) {
+ ref = git_reference__alloc_symbolic(node->name, node->target.symbolic);
+ } else {
+ assert(0);
+ }
+
+ GITERR_CHECK_ALLOC(ref);
+ update_reflog = node->reflog == NULL;
+
+ if (node->remove) {
+ error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL);
+ } else if (node->ref_type == GIT_REF_OID) {
+ error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
+ } else if (node->ref_type == GIT_REF_SYMBOLIC) {
+ error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
+ } else {
+ assert(0);
+ }
+
+ git_reference_free(ref);
+ node->committed = true;
+
+ return error;
+}
+
+int git_transaction_commit(git_transaction *tx)
+{
+ transaction_node *node;
+ git_strmap_iter pos;
+ int error;
+
+ assert(tx);
+
+ for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
+ if (!git_strmap_has_data(tx->locks, pos))
+ continue;
+
+ node = git_strmap_value_at(tx->locks, pos);
+ if (node->reflog) {
+ if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0)
+ return error;
+ }
+
+ if (node->ref_type != GIT_REF_INVALID) {
+ if ((error = update_target(tx->db, node)) < 0)
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+void git_transaction_free(git_transaction *tx)
+{
+ transaction_node *node;
+ git_pool pool;
+ git_strmap_iter pos;
+
+ assert(tx);
+
+ /* start by unlocking the ones we've left hanging, if any */
+ for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) {
+ if (!git_strmap_has_data(tx->locks, pos))
+ continue;
+
+ node = git_strmap_value_at(tx->locks, pos);
+ if (node->committed)
+ continue;
+
+ git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
+ }
+
+ git_refdb_free(tx->db);
+ git_strmap_free(tx->locks);
+
+ /* tx is inside the pool, so we need to extract the data */
+ memcpy(&pool, &tx->pool, sizeof(git_pool));
+ git_pool_clear(&pool);
+}
diff --git a/tests/config/stress.c b/tests/config/stress.c
index 488915e79..e8e9d2b61 100644
--- a/tests/config/stress.c
+++ b/tests/config/stress.c
@@ -44,12 +44,24 @@ void test_config_stress__comments(void)
cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12")));
+ cl_git_pass(git_config_get_string(&str, config, "some.section.test2"));
+ cl_assert_equal_s("hello", str);
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.test3"));
+ cl_assert_equal_s("welcome", str);
+
cl_git_pass(git_config_get_string(&str, config, "some.section.other"));
cl_assert_equal_s("hello! \" ; ; ; ", str);
+ cl_git_pass(git_config_get_string(&str, config, "some.section.other2"));
+ cl_assert_equal_s("cool! \" # # # ", str);
+
cl_git_pass(git_config_get_string(&str, config, "some.section.multi"));
cl_assert_equal_s("hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#", str);
+ cl_git_pass(git_config_get_string(&str, config, "some.section.multi2"));
+ cl_assert_equal_s("good, this is a ; multiline comment # with ;\n special chars and other stuff !@#", str);
+
cl_git_pass(git_config_get_string(&str, config, "some.section.back"));
cl_assert_equal_s("this is \ba phrase", str);
diff --git a/tests/core/buffer.c b/tests/core/buffer.c
index 7482dadbe..641fed630 100644
--- a/tests/core/buffer.c
+++ b/tests/core/buffer.c
@@ -1,7 +1,7 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "buf_text.h"
-#include "hashsig.h"
+#include "git2/sys/hashsig.h"
#include "fileops.h"
#define TESTSTR "Have you seen that? Have you seeeen that??"
diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
index 7b64a6339..c8191c5f5 100644
--- a/tests/fetchhead/nonetwork.c
+++ b/tests/fetchhead/nonetwork.c
@@ -335,7 +335,7 @@ void test_fetchhead_nonetwork__unborn_with_upstream(void)
cl_git_pass(git_remote_set_url(remote, cl_fixture("testrepo.git")));
cl_git_pass(git_remote_save(remote));
- cl_git_pass(git_remote_fetch(remote, NULL, NULL));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
git_remote_free(remote);
cl_git_pass(git_repository_fetchhead_foreach(repo, assert_master_for_merge, NULL));
diff --git a/tests/merge/trees/treediff.c b/tests/merge/trees/treediff.c
index 2298a302b..8b47f7dee 100644
--- a/tests/merge/trees/treediff.c
+++ b/tests/merge/trees/treediff.c
@@ -3,7 +3,7 @@
#include "merge.h"
#include "../merge_helpers.h"
#include "diff.h"
-#include "hashsig.h"
+#include "git2/sys/hashsig.h"
static git_repository *repo;
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
index 8809f427d..bf0d0872b 100644
--- a/tests/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -36,7 +36,7 @@ void test_network_fetchlocal__complete(void)
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
git_remote_set_callbacks(origin, &callbacks);
cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(origin));
+ cl_git_pass(git_remote_download(origin, NULL));
cl_git_pass(git_remote_update_tips(origin, NULL, NULL));
cl_git_pass(git_reference_list(&refnames, repo));
@@ -74,7 +74,7 @@ void test_network_fetchlocal__partial(void)
cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
git_remote_set_callbacks(origin, &callbacks);
cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(origin));
+ cl_git_pass(git_remote_download(origin, NULL));
cl_git_pass(git_remote_update_tips(origin, NULL, NULL));
git_strarray_free(&refnames);
diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c
index f1084fc38..c6c9e4ca9 100644
--- a/tests/network/remote/local.c
+++ b/tests/network/remote/local.c
@@ -116,16 +116,20 @@ void test_network_remote_local__nested_tags_are_completely_peeled(void)
void test_network_remote_local__shorthand_fetch_refspec0(void)
{
- const char *refspec = "master:remotes/sloppy/master";
- const char *refspec2 = "master:boh/sloppy/master";
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ "master:boh/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 2,
+ };
git_reference *ref;
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_add_fetch(remote, refspec2));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, &array));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
@@ -137,17 +141,21 @@ void test_network_remote_local__shorthand_fetch_refspec0(void)
void test_network_remote_local__shorthand_fetch_refspec1(void)
{
- const char *refspec = "master";
- const char *refspec2 = "hard_tag";
+ char *refspec_strings[] = {
+ "master",
+ "hard_tag",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 2,
+ };
git_reference *ref;
connect_to_local_repository(cl_fixture("testrepo.git"));
git_remote_clear_refspecs(remote);
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_add_fetch(remote, refspec2));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, &array));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
@@ -162,7 +170,7 @@ void test_network_remote_local__tagopt(void)
connect_to_local_repository(cl_fixture("testrepo.git"));
git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, NULL));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
@@ -174,14 +182,20 @@ void test_network_remote_local__tagopt(void)
void test_network_remote_local__push_to_bare_remote(void)
{
+ char *refspec_strings[] = {
+ "master:master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
/* Should be able to push to a bare remote */
git_remote *localremote;
git_push *push;
/* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, "master:master"));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, &array));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
git_remote_disconnect(remote);
@@ -210,6 +224,13 @@ void test_network_remote_local__push_to_bare_remote(void)
void test_network_remote_local__push_to_bare_remote_with_file_url(void)
{
+ char *refspec_strings[] = {
+ "master:master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
/* Should be able to push to a bare remote */
git_remote *localremote;
git_push *push;
@@ -217,8 +238,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void)
/* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, "master:master"));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, &array));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
git_remote_disconnect(remote);
@@ -251,14 +271,20 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void)
void test_network_remote_local__push_to_non_bare_remote(void)
{
+ char *refspec_strings[] = {
+ "master:master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
/* Shouldn't be able to push to a non-bare remote */
git_remote *localremote;
git_push *push;
/* Get some commits */
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, "master:master"));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, &array));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
git_remote_disconnect(remote);
@@ -287,7 +313,13 @@ void test_network_remote_local__push_to_non_bare_remote(void)
void test_network_remote_local__fetch(void)
{
- const char *refspec = "master:remotes/sloppy/master";
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
git_reflog *log;
const git_reflog_entry *entry;
@@ -297,9 +329,8 @@ void test_network_remote_local__fetch(void)
cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com"));
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_fetch(remote, sig, "UPDAAAAAATE!!"));
+ cl_git_pass(git_remote_fetch(remote, &array, sig, "UPDAAAAAATE!!"));
cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
git_reference_free(ref);
@@ -316,7 +347,13 @@ void test_network_remote_local__fetch(void)
void test_network_remote_local__reflog(void)
{
- const char *refspec = "master:remotes/sloppy/master";
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
git_reflog *log;
const git_reflog_entry *entry;
@@ -325,9 +362,8 @@ void test_network_remote_local__reflog(void)
cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com"));
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, &array));
cl_git_pass(git_remote_update_tips(remote, sig, "UPDAAAAAATE!!"));
cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
@@ -342,7 +378,13 @@ void test_network_remote_local__reflog(void)
void test_network_remote_local__fetch_default_reflog_message(void)
{
- const char *refspec = "master:remotes/sloppy/master";
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
git_reflog *log;
const git_reflog_entry *entry;
@@ -352,9 +394,8 @@ void test_network_remote_local__fetch_default_reflog_message(void)
cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com"));
connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_fetch(remote, sig, NULL));
+ cl_git_pass(git_remote_fetch(remote, &array, sig, NULL));
cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
cl_assert_equal_i(1, git_reflog_entrycount(log));
@@ -367,3 +408,24 @@ void test_network_remote_local__fetch_default_reflog_message(void)
git_reflog_free(log);
git_signature_free(sig);
}
+
+void test_network_remote_local__opportunistic_update(void)
+{
+ git_reference *ref;
+ char *refspec_strings[] = {
+ "master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+
+ /* this remote has a passive refspec of "refs/heads/<star>:refs/remotes/origin/<star>" */
+ cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git")));
+ /* and we pass the active refspec "master" */
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ /* and we expect that to update our copy of origin's master */
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/origin/master"));
+ git_reference_free(ref);
+}
diff --git a/tests/online/fetch.c b/tests/online/fetch.c
index f03a6faa6..ce92ee82b 100644
--- a/tests/online/fetch.c
+++ b/tests/online/fetch.c
@@ -47,7 +47,7 @@ static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
git_remote_set_callbacks(remote, &callbacks);
git_remote_set_autotag(remote, flag);
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, NULL));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
git_remote_disconnect(remote);
cl_assert_equal_i(counter, n);
@@ -86,11 +86,11 @@ void test_online_fetch__fetch_twice(void)
git_remote *remote;
cl_git_pass(git_remote_create(&remote, _repo, "test", "git://github.com/libgit2/TestGitRepository.git"));
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, NULL));
git_remote_disconnect(remote);
git_remote_connect(remote, GIT_DIRECTION_FETCH);
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, NULL));
git_remote_disconnect(remote);
git_remote_free(remote);
@@ -128,7 +128,7 @@ void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date
callbacks.transfer_progress = &transferProgressCallback;
callbacks.payload = &invoked;
git_remote_set_callbacks(remote, &callbacks);
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, NULL));
cl_assert_equal_i(false, invoked);
@@ -162,7 +162,7 @@ void test_online_fetch__can_cancel(void)
git_remote_set_callbacks(remote, &callbacks);
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_fail_with(git_remote_download(remote), -4321);
+ cl_git_fail_with(git_remote_download(remote, NULL), -4321);
git_remote_disconnect(remote);
git_remote_free(remote);
}
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
index 3f27e1331..bd423bbb0 100644
--- a/tests/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -40,17 +40,19 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet
git_remote *remote;
git_buf fetchhead_buf = GIT_BUF_INIT;
int equals = 0;
+ git_strarray array, *active_refs = NULL;
cl_git_pass(git_remote_load(&remote, g_repo, "origin"));
git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
if(fetchspec != NULL) {
- git_remote_clear_refspecs(remote);
- git_remote_add_fetch(remote, fetchspec);
+ array.count = 1;
+ array.strings = (char **) &fetchspec;
+ active_refs = &array;
}
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_download(remote, active_refs));
cl_git_pass(git_remote_update_tips(remote, NULL, NULL));
git_remote_disconnect(remote);
git_remote_free(remote);
diff --git a/tests/online/push.c b/tests/online/push.c
index 70ec705fe..b09c7ad1f 100644
--- a/tests/online/push.c
+++ b/tests/online/push.c
@@ -408,7 +408,7 @@ void test_online_push__initialize(void)
/* Now that we've deleted everything, fetch from the remote */
cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(_remote));
+ cl_git_pass(git_remote_download(_remote, NULL));
cl_git_pass(git_remote_update_tips(_remote, NULL, NULL));
git_remote_disconnect(_remote);
}
diff --git a/tests/refs/transactions.c b/tests/refs/transactions.c
new file mode 100644
index 000000000..39ea1cae5
--- /dev/null
+++ b/tests/refs/transactions.c
@@ -0,0 +1,110 @@
+#include "clar_libgit2.h"
+#include "git2/transaction.h"
+
+static git_repository *g_repo;
+static git_transaction *g_tx;
+
+void test_refs_transactions__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_transaction_new(&g_tx, g_repo));
+}
+
+void test_refs_transactions__cleanup(void)
+{
+ git_transaction_free(g_tx);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_transactions__single_ref_oid(void)
+{
+ git_reference *ref;
+ git_oid id;
+
+ git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__single_ref_symbolic(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD"));
+ cl_git_pass(git_transaction_set_symbolic_target(g_tx, "HEAD", "refs/heads/foo", NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+
+ cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__single_ref_mix_types(void)
+{
+ git_reference *ref;
+ git_oid id;
+
+ git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD"));
+ cl_git_pass(git_transaction_set_symbolic_target(g_tx, "refs/heads/master", "refs/heads/foo", NULL, NULL));
+ cl_git_pass(git_transaction_set_target(g_tx, "HEAD", &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+ cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__single_ref_delete(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_remove(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+}
+
+void test_refs_transactions__single_create(void)
+{
+ git_reference *ref;
+ const char *name = "refs/heads/new-branch";
+ git_oid id;
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, name));
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, name));
+
+ git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_transaction_set_target(g_tx, name, &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, name));
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__unlocked_set(void)
+{
+ git_oid id;
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/foo", &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+}
diff --git a/tests/resources/config/config12 b/tests/resources/config/config12
index b57a81b08..6917880b5 100644
--- a/tests/resources/config/config12
+++ b/tests/resources/config/config12
@@ -1,7 +1,13 @@
[some "section"]
test = hi ; comment
+ test2 = hello ; comment
+ test3 = welcome #comment
other = "hello! \" ; ; ; " ; more test
+ other2 = "cool! \" # # # " # more test
multi = "hi, this is a ; \
multiline comment # with ;\n special chars \
and other stuff !@#"
+ multi2 = "good, this is a ; \
+multiline comment # with ;\n special chars \
+and other stuff !@#" #^^^
back = "this is \ba phrase"
diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c
index 677e1a1b6..fa974eb9a 100644
--- a/tests/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -127,7 +127,7 @@ void test_revwalk_mergebase__prefer_youngest_merge_base(void)
{
git_oid result, one, two, expected;
- cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f "));
+ cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f"));
cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
@@ -140,7 +140,7 @@ 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(&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"));
@@ -153,6 +153,26 @@ void test_revwalk_mergebase__multiple_merge_bases(void)
git_oidarray_free(&result);
}
+void test_revwalk_mergebase__multiple_merge_bases_many_commits(void)
+{
+ git_oid expected1, expected2;
+ git_oidarray result = {NULL, 0};
+
+ git_oid *input = git__malloc(sizeof(git_oid) * 2);
+
+ cl_git_pass(git_oid_fromstr(&input[0], "a4a7dce85cf63874e984719f4fdd239f5145052f"));
+ cl_git_pass(git_oid_fromstr(&input[1], "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+ cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+
+ cl_git_pass(git_merge_bases_many(&result, _repo, 2, input));
+ 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/stash/save.c b/tests/stash/save.c
index 7873d20ba..a5bdd0cbe 100644
--- a/tests/stash/save.c
+++ b/tests/stash/save.c
@@ -370,8 +370,8 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_
void test_stash_save__ignored_directory(void)
{
- cl_git_pass(mkdir("stash/ignored_directory", 0777));
- cl_git_pass(mkdir("stash/ignored_directory/sub", 0777));
+ cl_git_pass(p_mkdir("stash/ignored_directory", 0777));
+ cl_git_pass(p_mkdir("stash/ignored_directory/sub", 0777));
cl_git_mkfile("stash/ignored_directory/sub/some_file", "stuff");
assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_WT_NEW);