summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt19
-rw-r--r--examples/general.c2
-rw-r--r--examples/network/clone.c96
-rw-r--r--examples/network/fetch.c22
-rw-r--r--examples/network/index-pack.c10
-rw-r--r--examples/showindex.c3
-rw-r--r--include/git2.h1
-rw-r--r--include/git2/checkout.h21
-rw-r--r--include/git2/clone.h34
-rw-r--r--include/git2/errors.h2
-rw-r--r--include/git2/index.h291
-rw-r--r--include/git2/indexer.h30
-rw-r--r--include/git2/reflog.h6
-rw-r--r--include/git2/remote.h33
-rw-r--r--include/git2/repository.h22
-rw-r--r--include/git2/stash.h122
-rw-r--r--src/attr.c2
-rw-r--r--src/branch.c44
-rw-r--r--src/buffer.c28
-rw-r--r--src/buffer.h25
-rw-r--r--src/checkout.c63
-rw-r--r--src/clone.c57
-rw-r--r--src/config.c80
-rw-r--r--src/config.h5
-rw-r--r--src/fetch.c60
-rw-r--r--src/fetch.h14
-rw-r--r--src/index.c634
-rw-r--r--src/index.h5
-rw-r--r--src/indexer.c51
-rw-r--r--src/iterator.c2
-rw-r--r--src/merge.c48
-rw-r--r--src/merge.h19
-rw-r--r--src/netops.c3
-rw-r--r--src/netops.h2
-rw-r--r--src/reflog.c22
-rw-r--r--src/refs.h12
-rw-r--r--src/refspec.c11
-rw-r--r--src/refspec.h2
-rw-r--r--src/remote.c401
-rw-r--r--src/remote.h1
-rw-r--r--src/repository.c47
-rw-r--r--src/reset.c17
-rw-r--r--src/stash.c659
-rw-r--r--src/submodule.c6
-rw-r--r--src/transport.h2
-rw-r--r--src/transports/http.c3
-rw-r--r--src/tree.c4
-rw-r--r--src/vector.c14
-rw-r--r--src/vector.h1
-rw-r--r--tests-clar/attr/repo.c4
-rw-r--r--tests-clar/buf/splice.c93
-rw-r--r--tests-clar/checkout/head.c2
-rw-r--r--tests-clar/checkout/index.c53
-rw-r--r--tests-clar/checkout/tree.c24
-rw-r--r--tests-clar/checkout/typechange.c2
-rw-r--r--tests-clar/clone/network.c29
-rw-r--r--tests-clar/clone/nonetwork.c4
-rw-r--r--tests-clar/config/config_helpers.c37
-rw-r--r--tests-clar/config/config_helpers.h9
-rw-r--r--tests-clar/core/vector.c84
-rw-r--r--tests-clar/index/conflicts.c217
-rw-r--r--tests-clar/index/filemodes.c66
-rw-r--r--tests-clar/index/read_tree.c8
-rw-r--r--tests-clar/index/rename.c10
-rw-r--r--tests-clar/index/reuc.c236
-rw-r--r--tests-clar/index/stage.c60
-rw-r--r--tests-clar/index/tests.c8
-rw-r--r--tests-clar/network/fetch.c12
-rw-r--r--tests-clar/network/remoterename.c201
-rw-r--r--tests-clar/network/remotes.c31
-rw-r--r--tests-clar/object/commit/commitstagedfile.c69
-rw-r--r--tests-clar/object/message.c65
-rw-r--r--tests-clar/pack/packbuilder.c2
-rw-r--r--tests-clar/refs/branches/delete.c15
-rw-r--r--tests-clar/refs/branches/move.c22
-rw-r--r--tests-clar/refs/reflog/drop.c39
-rw-r--r--tests-clar/repo/state.c98
-rw-r--r--tests-clar/reset/hard.c35
-rw-r--r--tests-clar/reset/soft.c18
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/HEAD1
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD1
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/MERGE_MODE0
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/MERGE_MSG5
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD1
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/config6
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/description1
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/indexbin0 -> 842 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/logs/HEAD5
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch2
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master2
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712bin0 -> 141 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91bin0 -> 141 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81bin0 -> 34 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211ebin0 -> 42 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6bin0 -> 159 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534bin0 -> 74 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c8762
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8cbin0 -> 39 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3bin0 -> 36 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52dabin0 -> 36 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46adbin0 -> 45 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399bin0 -> 49 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8bin0 -> 85 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4bin0 -> 41 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71bin0 -> 41 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038cbin0 -> 40 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cdabin0 -> 66 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673bin0 -> 38 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735abin0 -> 78 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2bin0 -> 57 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8bin0 -> 38 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9bin0 -> 39 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50bin0 -> 140 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c51
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092cbin0 -> 36 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0dbin0 -> 66 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff3
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478bin0 -> 87 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406bin0 -> 141 bytes
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/refs/heads/branch1
-rw-r--r--tests-clar/resources/mergedrepo/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/mergedrepo/conflicts-one.txt5
-rw-r--r--tests-clar/resources/mergedrepo/conflicts-two.txt5
-rw-r--r--tests-clar/resources/mergedrepo/one.txt10
-rw-r--r--tests-clar/resources/mergedrepo/two.txt12
-rw-r--r--tests-clar/stash/drop.c126
-rw-r--r--tests-clar/stash/foreach.c120
-rw-r--r--tests-clar/stash/save.c370
-rw-r--r--tests-clar/stash/stash_helpers.c67
-rw-r--r--tests-clar/stash/stash_helpers.h8
-rw-r--r--tests-clar/status/worktree.c10
-rw-r--r--tests-clar/submodule/status.c2
135 files changed, 4756 insertions, 629 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a57394640..59c6b4388 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,6 +85,8 @@ IF (MSVC)
# Default to stdcall, as that's what the CLR expects and how the Windows API is built
OPTION (STDCALL "Buildl libgit2 with the __stdcall convention" ON)
+ STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+
SET(CMAKE_C_FLAGS "/W4 /MP /nologo /Zi ${CMAKE_C_FLAGS}")
IF (STDCALL)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gz")
@@ -94,9 +96,9 @@ IF (MSVC)
SET(WIN_RC "src/win32/git2.rc")
# Precompiled headers
+
ELSE ()
- SET(CMAKE_C_FLAGS_DEBUG "-O0 -g ${CMAKE_C_FLAGS}")
- SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
+ SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
IF (MINGW) # MinGW always does PIC and complains if we tell it to
STRING(REGEX REPLACE "-fPIC" "" CMAKE_SHARED_LIBRARY_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
ELSE ()
@@ -108,10 +110,15 @@ ELSE ()
ENDIF ()
ENDIF()
-# Build Debug by default
-IF (NOT CMAKE_BUILD_TYPE)
- SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
-ENDIF ()
+IF( NOT CMAKE_CONFIGURATION_TYPES )
+ # Build Debug by default
+ IF (NOT CMAKE_BUILD_TYPE)
+ SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+ ENDIF ()
+ELSE()
+ # Using a multi-configuration generator eg MSVC or Xcode
+ # that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE
+ENDIF()
IF (OPENSSL_FOUND)
ADD_DEFINITIONS(-DGIT_SSL)
diff --git a/examples/general.c b/examples/general.c
index e001a6889..d9467f5b5 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -371,7 +371,7 @@ int main (int argc, char** argv)
// All these properties are exported publicly in the `git_index_entry` struct
ecount = git_index_entrycount(index);
for (i = 0; i < ecount; ++i) {
- git_index_entry *e = git_index_get(index, i);
+ git_index_entry *e = git_index_get_byindex(index, i);
printf("path: %s\n", e->path);
printf("mtime: %d\n", (int)e->mtime.seconds);
diff --git a/examples/network/clone.c b/examples/network/clone.c
index fb571bd3a..791600171 100644
--- a/examples/network/clone.c
+++ b/examples/network/clone.c
@@ -7,62 +7,72 @@
#include <pthread.h>
#include <unistd.h>
-struct dl_data {
- git_indexer_stats fetch_stats;
- git_indexer_stats checkout_stats;
- git_checkout_opts opts;
- int ret;
- int finished;
- const char *url;
+typedef struct progress_data {
+ git_transfer_progress fetch_progress;
+ size_t completed_steps;
+ size_t total_steps;
const char *path;
-};
+} progress_data;
-static void *clone_thread(void *ptr)
+static void print_progress(const progress_data *pd)
{
- struct dl_data *data = (struct dl_data *)ptr;
- git_repository *repo = NULL;
-
- // Kick off the clone
- data->ret = git_clone(&repo, data->url, data->path,
- &data->fetch_stats, &data->checkout_stats,
- &data->opts);
- if (repo) git_repository_free(repo);
- data->finished = 1;
+ int network_percent = (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects;
+ int index_percent = (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects;
+ int checkout_percent = pd->total_steps > 0
+ ? (100 * pd->completed_steps) / pd->total_steps
+ : 0.f;
+ int kbytes = pd->fetch_progress.received_bytes / 1024;
+ printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4lu/%4lu) %s\n",
+ network_percent, kbytes,
+ pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
+ index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
+ checkout_percent, pd->completed_steps, pd->total_steps,
+ pd->path);
+}
- pthread_exit(&data->ret);
+static void fetch_progress(const git_transfer_progress *stats, void *payload)
+{
+ progress_data *pd = (progress_data*)payload;
+ pd->fetch_progress = *stats;
+ print_progress(pd);
+}
+static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ progress_data *pd = (progress_data*)payload;
+ pd->completed_steps = cur;
+ pd->total_steps = tot;
+ pd->path = path;
+ print_progress(pd);
}
int do_clone(git_repository *repo, int argc, char **argv)
{
- struct dl_data data = {0};
- pthread_t worker;
+ progress_data pd = {0};
+ git_repository *cloned_repo = NULL;
+ git_checkout_opts checkout_opts = {0};
+ const char *url = argv[1];
+ const char *path = argv[2];
+ int error;
// Validate args
if (argc < 3) {
- printf("USAGE: %s <url> <path>\n", argv[0]);
+ printf ("USAGE: %s <url> <path>\n", argv[0]);
return -1;
}
- // Data for background thread
- data.url = argv[1];
- data.path = argv[2];
- data.opts.disable_filters = 1;
- printf("Cloning '%s' to '%s'\n", data.url, data.path);
+ // Set up options
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
+ checkout_opts.progress_cb = checkout_progress;
+ checkout_opts.progress_payload = &pd;
- // Create the worker thread
- pthread_create(&worker, NULL, clone_thread, &data);
-
- // Watch for progress information
- do {
- usleep(10000);
- printf("Fetch %d/%d – Checkout %d/%d\n",
- data.fetch_stats.processed, data.fetch_stats.total,
- data.checkout_stats.processed, data.checkout_stats.total);
- } while (!data.finished);
- printf("Fetch %d/%d – Checkout %d/%d\n",
- data.fetch_stats.processed, data.fetch_stats.total,
- data.checkout_stats.processed, data.checkout_stats.total);
-
- return data.ret;
+ // Do the clone
+ error = git_clone(&cloned_repo, url, path, &fetch_progress, &pd, &checkout_opts);
+ printf("\n");
+ if (error != 0) {
+ const git_error *err = giterr_last();
+ if (err) printf("ERROR %d: %s\n", err->klass, err->message);
+ else printf("ERROR %d: no detailed info\n", error);
+ }
+ else if (cloned_repo) git_repository_free(cloned_repo);
+ return error;
}
-
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index fa941b97a..496498e8c 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -8,8 +8,6 @@
struct dl_data {
git_remote *remote;
- git_off_t *bytes;
- git_indexer_stats *stats;
int ret;
int finished;
};
@@ -35,7 +33,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, data->bytes, data->stats) < 0) {
+ if (git_remote_download(data->remote, NULL, NULL) < 0) {
data->ret = -1;
goto exit;
}
@@ -69,15 +67,14 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
- git_off_t bytes = 0;
- git_indexer_stats stats;
+ const git_transfer_progress *stats;
pthread_t worker;
struct dl_data data;
git_remote_callbacks callbacks;
argc = argc;
// Figure out whether it's a named remote or a URL
- printf("Fetching %s\n", argv[1]);
+ printf("Fetching %s for repo %p\n", argv[1], repo);
if (git_remote_load(&remote, repo, argv[1]) < 0) {
if (git_remote_new(&remote, repo, NULL, argv[1], NULL) < 0)
return -1;
@@ -91,11 +88,10 @@ int fetch(git_repository *repo, int argc, char **argv)
// Set up the information for the background worker thread
data.remote = remote;
- data.bytes = &bytes;
- data.stats = &stats;
data.ret = 0;
data.finished = 0;
- memset(&stats, 0, sizeof(stats));
+
+ stats = git_remote_stats(remote);
pthread_create(&worker, NULL, download, &data);
@@ -106,16 +102,18 @@ int fetch(git_repository *repo, int argc, char **argv)
do {
usleep(10000);
- if (stats.total > 0)
+ if (stats->total_objects > 0)
printf("Received %d/%d objects (%d) in %d bytes\r",
- stats.received, stats.total, stats.processed, bytes);
+ stats->received_objects, stats->total_objects,
+ stats->indexed_objects, stats->received_bytes);
} while (!data.finished);
if (data.ret < 0)
goto on_error;
pthread_join(worker, NULL);
- printf("\rReceived %d/%d objects in %zu bytes\n", stats.processed, stats.total, bytes);
+ printf("\rReceived %d/%d objects in %zu bytes\n",
+ stats->indexed_objects, stats->total_objects, stats->received_bytes);
// Disconnect the underlying connection to prevent from idling.
git_remote_disconnect(remote);
diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c
index 85aac4aff..4d3dc84d6 100644
--- a/examples/network/index-pack.c
+++ b/examples/network/index-pack.c
@@ -10,10 +10,10 @@
// This could be run in the main loop whilst the application waits for
// the indexing to finish in a worker thread
-static int index_cb(const git_indexer_stats *stats, void *data)
+static int index_cb(const git_transfer_progress *stats, void *data)
{
data = data;
- printf("\rProcessing %d of %d", stats->processed, stats->total);
+ printf("\rProcessing %d of %d", stats->indexed_objects, stats->total_objects);
return 0;
}
@@ -21,7 +21,7 @@ static int index_cb(const git_indexer_stats *stats, void *data)
int index_pack(git_repository *repo, int argc, char **argv)
{
git_indexer_stream *idx;
- git_indexer_stats stats = {0, 0};
+ git_transfer_progress stats = {0, 0};
int error, fd;
char hash[GIT_OID_HEXSZ + 1] = {0};
ssize_t read_bytes;
@@ -33,7 +33,7 @@ int index_pack(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
- if (git_indexer_stream_new(&idx, ".") < 0) {
+ if (git_indexer_stream_new(&idx, ".", NULL, NULL) < 0) {
puts("bad idx");
return -1;
}
@@ -63,7 +63,7 @@ int index_pack(git_repository *repo, int argc, char **argv)
if ((error = git_indexer_stream_finalize(idx, &stats)) < 0)
goto cleanup;
- printf("\rIndexing %d of %d\n", stats.processed, stats.total);
+ printf("\rIndexing %d of %d\n", stats.indexed_objects, stats.total_objects);
git_oid_fmt(hash, git_indexer_stream_hash(idx));
puts(hash);
diff --git a/examples/showindex.c b/examples/showindex.c
index 7f2130b90..d26fbaebd 100644
--- a/examples/showindex.c
+++ b/examples/showindex.c
@@ -19,12 +19,13 @@ int main (int argc, char** argv)
ecount = git_index_entrycount(index);
for (i = 0; i < ecount; ++i) {
- git_index_entry *e = git_index_get(index, i);
+ git_index_entry *e = git_index_get_byindex(index, i);
oid = e->oid;
git_oid_fmt(out, &oid);
printf("File Path: %s\n", e->path);
+ printf(" Stage: %d\n", git_index_entry_stage(e));
printf(" Blob SHA: %s\n", out);
printf("File Size: %d\n", (int)e->file_size);
printf(" Device: %d\n", (int)e->dev);
diff --git a/include/git2.h b/include/git2.h
index d55543986..3bb2fce11 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -52,5 +52,6 @@
#include "git2/reset.h"
#include "git2/message.h"
#include "git2/pack.h"
+#include "git2/stash.h"
#endif
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index b4f9ad081..390d2f215 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -65,9 +65,16 @@ typedef struct git_checkout_opts {
const git_oid *blob_oid,
int file_mode,
void *payload);
-
void *notify_payload;
+ /* Optional callback to notify the consumer of checkout progress. */
+ void (* progress_cb)(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload);
+ void *progress_payload;
+
/** When not NULL, array of fnmatch patterns specifying
* which paths should be taken into account
*/
@@ -80,29 +87,25 @@ typedef struct git_checkout_opts {
*
* @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL)
- * @param stats structure through which progress information is reported
* @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
* branch, GIT_ERROR otherwise (use giterr_last for information
* about the error)
*/
GIT_EXTERN(int) git_checkout_head(
git_repository *repo,
- git_checkout_opts *opts,
- git_indexer_stats *stats);
+ git_checkout_opts *opts);
/**
* Updates files in the working tree to match the content of the index.
*
* @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL)
- * @param stats structure through which progress information is reported
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
*/
GIT_EXTERN(int) git_checkout_index(
git_repository *repo,
- git_checkout_opts *opts,
- git_indexer_stats *stats);
+ git_checkout_opts *opts);
/**
* Updates files in the index and working tree to match the content of the
@@ -112,15 +115,13 @@ GIT_EXTERN(int) git_checkout_index(
* @param treeish a commit, tag or tree which content will be used to update
* the working directory
* @param opts specifies checkout options (may be NULL)
- * @param stats structure through which progress information is reported
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
*/
GIT_EXTERN(int) git_checkout_tree(
git_repository *repo,
git_object *treeish,
- git_checkout_opts *opts,
- git_indexer_stats *stats);
+ git_checkout_opts *opts);
/** @} */
GIT_END_DECL
diff --git a/include/git2/clone.h b/include/git2/clone.h
index c4dfc652b..7d8d32118 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -29,19 +29,22 @@ GIT_BEGIN_DECL
* @param out pointer that will receive the resulting repository object
* @param origin_url repository to clone from
* @param workdir_path local directory to clone to
- * @param fetch_stats pointer to structure that receives fetch progress
- * information (may be NULL)
+ * @param fetch_progress_cb optional callback for fetch progress. Be aware that
+ * this is called inline with network and indexing operations, so performance
+ * may be affected.
+ * @param fetch_progress_payload payload for fetch_progress_cb
* @param checkout_opts options for the checkout step. If NULL, no checkout
* is performed
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
*/
-GIT_EXTERN(int) git_clone(git_repository **out,
- const char *origin_url,
- const char *workdir_path,
- git_indexer_stats *fetch_stats,
- git_indexer_stats *checkout_stats,
- git_checkout_opts *checkout_opts);
+GIT_EXTERN(int) git_clone(
+ git_repository **out,
+ const char *origin_url,
+ const char *workdir_path,
+ git_transfer_progress_callback fetch_progress_cb,
+ void *fetch_progress_payload,
+ git_checkout_opts *checkout_opts);
/**
* Create a bare clone of a remote repository.
@@ -49,13 +52,18 @@ GIT_EXTERN(int) git_clone(git_repository **out,
* @param out pointer that will receive the resulting repository object
* @param origin_url repository to clone from
* @param dest_path local directory to clone to
- * @param fetch_stats pointer to structure that receives fetch progress information (may be NULL)
+ * @param fetch_progress_cb optional callback for fetch progress. Be aware that
+ * this is called inline with network and indexing operations, so performance
+ * may be affected.
+ * @param fetch_progress_payload payload for fetch_progress_cb
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information about the error)
*/
-GIT_EXTERN(int) git_clone_bare(git_repository **out,
- const char *origin_url,
- const char *dest_path,
- git_indexer_stats *fetch_stats);
+GIT_EXTERN(int) git_clone_bare(
+ git_repository **out,
+ const char *origin_url,
+ const char *dest_path,
+ git_transfer_progress_callback fetch_progress_cb,
+ void *fetch_progress_payload);
/** @} */
GIT_END_DECL
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 38b7fe0ae..8bb47f354 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -28,6 +28,7 @@ enum {
GIT_EUSER = -7,
GIT_EBAREREPO = -8,
GIT_EORPHANEDHEAD = -9,
+ GIT_EUNMERGED = -10,
GIT_PASSTHROUGH = -30,
GIT_ITEROVER = -31,
@@ -58,6 +59,7 @@ typedef enum {
GITERR_SSL,
GITERR_SUBMODULE,
GITERR_THREAD,
+ GITERR_STASH,
} git_error_t;
/**
diff --git a/include/git2/index.h b/include/git2/index.h
index 062932e1a..1d91663d8 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -84,12 +84,12 @@ typedef struct git_index_entry {
char *path;
} git_index_entry;
-/** Representation of an unmerged file entry in the index. */
-typedef struct git_index_entry_unmerged {
+/** Representation of a resolve undo entry in the index. */
+typedef struct git_index_reuc_entry {
unsigned int mode[3];
git_oid oid[3];
char *path;
-} git_index_entry_unmerged;
+} git_index_reuc_entry;
/** Capabilities of system that affect index actions. */
enum {
@@ -99,6 +99,12 @@ enum {
GIT_INDEXCAP_FROM_OWNER = ~0u
};
+/** @name Index File Functions
+ *
+ * These functions work on the index file itself.
+ */
+/**@{*/
+
/**
* Create a new bare Git index object as a memory representation
* of the Git index file in 'index_path', without a repository
@@ -120,20 +126,19 @@ enum {
GIT_EXTERN(int) git_index_open(git_index **index, const char *index_path);
/**
- * Clear the contents (all the entries) of an index object.
- * This clears the index object in memory; changes must be manually
- * written to disk for them to take effect.
+ * Free an existing index object.
*
* @param index an existing index object
*/
-GIT_EXTERN(void) git_index_clear(git_index *index);
+GIT_EXTERN(void) git_index_free(git_index *index);
/**
- * Free an existing index object.
+ * Get the repository this index relates to
*
- * @param index an existing index object
+ * @param index The index
+ * @return A pointer to the repository
*/
-GIT_EXTERN(void) git_index_free(git_index *index);
+GIT_EXTERN(git_repository *) git_index_owner(const git_index *index);
/**
* Read index capabilities flags.
@@ -175,44 +180,92 @@ GIT_EXTERN(int) git_index_read(git_index *index);
GIT_EXTERN(int) git_index_write(git_index *index);
/**
- * Find the first index of any entries which point to given
- * path in the Git index.
+ * Read a tree into the index file with stats
+ *
+ * The current index contents will be replaced by the specified tree.
*
* @param index an existing index object
- * @param path path to search
- * @return an index >= 0 if found, -1 otherwise
+ * @param tree tree to read
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_find(git_index *index, const char *path);
+GIT_EXTERN(int) git_index_read_tree(git_index *index, git_tree *tree);
+
+/**@}*/
+
+/** @name Raw Index Entry Functions
+ *
+ * These functions work on index entries, and allow for raw manipulation
+ * of the entries.
+ */
+/**@{*/
+
+/* Index entry manipulation */
/**
- * Remove all entries with equal path except last added
+ * Get the count of entries currently in the index
*
* @param index an existing index object
+ * @return integer of count of current entries
*/
-GIT_EXTERN(void) git_index_uniq(git_index *index);
+GIT_EXTERN(unsigned int) git_index_entrycount(git_index *index);
/**
- * Add or update an index entry from a file in disk
+ * Clear the contents (all the entries) of an index object.
+ * This clears the index object in memory; changes must be manually
+ * written to disk for them to take effect.
*
- * The file `path` must be relative to the repository's
- * working folder and must be readable.
+ * @param index an existing index object
+ */
+GIT_EXTERN(void) git_index_clear(git_index *index);
+
+/**
+ * Get a pointer to one of the entries in the index
*
- * This method will fail in bare index instances.
+ * The values of this entry can be modified (except the path)
+ * and the changes will be written back to disk on the next
+ * write() call.
*
- * This forces the file to be added to the index, not looking
- * at gitignore rules. Those rules can be evaluated through
- * the git_status APIs (in status.h) before calling this.
+ * The entry should not be freed by the caller.
*
* @param index an existing index object
- * @param path filename to add
- * @param stage stage for the entry
+ * @param n the position of the entry
+ * @return a pointer to the entry; NULL if out of bounds
+ */
+GIT_EXTERN(git_index_entry *) git_index_get_byindex(git_index *index, size_t n);
+
+/**
+ * Get a pointer to one of the entries in the index
+ *
+ * The values of this entry can be modified (except the path)
+ * and the changes will be written back to disk on the next
+ * write() call.
+ *
+ * The entry should not be freed by the caller.
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @param stage stage to search
+ * @return a pointer to the entry; NULL if it was not found
+ */
+GIT_EXTERN(git_index_entry *) git_index_get_bypath(git_index *index, const char *path, int stage);
+
+/**
+ * Remove an entry from the index
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @param stage stage to search
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
+GIT_EXTERN(int) git_index_remove(git_index *index, const char *path, int stage);
/**
* Add or update an index entry from an in-memory struct
*
+ * If a previous index entry exists that has the same path and stage
+ * as the given 'source_entry', it will be replaced. Otherwise, the
+ * 'source_entry' will be added.
+ *
* A full copy (including the 'path' string) of the given
* 'source_entry' will be inserted on the index.
*
@@ -220,133 +273,207 @@ GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
* @param source_entry new entry object
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry);
+GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_entry);
/**
- * Add (append) an index entry from a file in disk
+ * Return the stage number from a git index entry
+ *
+ * This entry is calculated from the entry's flag
+ * attribute like this:
+ *
+ * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
*
- * A new entry will always be inserted into the index;
- * if the index already contains an entry for such
- * path, the old entry will **not** be replaced.
+ * @param entry The entry
+ * @returns the stage number
+ */
+GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
+
+/**@}*/
+
+/** @name Workdir Index Entry Functions
+ *
+ * These functions work on index entries specifically in the working
+ * directory (ie, stage 0).
+ */
+/**@{*/
+
+/**
+ * Add or update an index entry from a file in disk
*
* The file `path` must be relative to the repository's
* working folder and must be readable.
*
* This method will fail in bare index instances.
*
+ * This forces the file to be added to the index, not looking
+ * at gitignore rules. Those rules can be evaluated through
+ * the git_status APIs (in status.h) before calling this.
+ *
+ * If this file currently is the result of a merge conflict, this
+ * file will no longer be marked as conflicting. The data about
+ * the conflict will be moved to the "resolve undo" (REUC) section.
+ *
* @param index an existing index object
* @param path filename to add
- * @param stage stage for the entry
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
+GIT_EXTERN(int) git_index_add_from_workdir(git_index *index, const char *path);
/**
- * Add (append) an index entry from an in-memory struct
+ * Find the first index of any entries which point to given
+ * path in the Git index.
*
- * A new entry will always be inserted into the index;
- * if the index already contains an entry for the path
- * in the `entry` struct, the old entry will **not** be
- * replaced.
+ * @param index an existing index object
+ * @param path path to search
+ * @return an index >= 0 if found, -1 otherwise
+ */
+GIT_EXTERN(int) git_index_find(git_index *index, const char *path);
+
+/**@}*/
+
+/** @name Conflict Index Entry Functions
*
- * A full copy (including the 'path' string) of the given
- * 'source_entry' will be inserted on the index.
+ * These functions work on conflict index entries specifically (ie, stages 1-3)
+ */
+/**@{*/
+
+/**
+ * Add or update index entries to represent a conflict
+ *
+ * The entries are the entries from the tree included in the merge. Any
+ * entry may be null to indicate that that file was not present in the
+ * trees during the merge. For example, ancestor_entry may be NULL to
+ * indicate that a file was added in both branches and must be resolved.
*
* @param index an existing index object
- * @param source_entry new entry object
+ * @param ancestor_entry the entry data for the ancestor of the conflict
+ * @param our_entry the entry data for our side of the merge conflict
+ * @param their_entry the entry data for their side of the merge conflict
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *source_entry);
+GIT_EXTERN(int) git_index_conflict_add(git_index *index,
+ const git_index_entry *ancestor_entry,
+ const git_index_entry *our_entry,
+ const git_index_entry *their_entry);
/**
- * Remove an entry from the index
+ * Get the index entries that represent a conflict of a single file.
*
+ * The values of this entry can be modified (except the paths)
+ * and the changes will be written back to disk on the next
+ * write() call.
+ *
+ * @param ancestor_out Pointer to store the ancestor entry
+ * @param our_out Pointer to store the our entry
+ * @param their_out Pointer to store the their entry
* @param index an existing index object
- * @param position position of the entry to remove
- * @return 0 or an error code
+ * @param path path to search
*/
-GIT_EXTERN(int) git_index_remove(git_index *index, int position);
-
+GIT_EXTERN(int) git_index_conflict_get(git_index_entry **ancestor_out, git_index_entry **our_out, git_index_entry **their_out, git_index *index, const char *path);
/**
- * Get a pointer to one of the entries in the index
+ * Removes the index entries that represent a conflict of a single file.
*
- * This entry can be modified, and the changes will be written
- * back to disk on the next write() call.
- *
- * The entry should not be freed by the caller.
+ * @param index an existing index object
+ * @param path to search
+ */
+GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path);
+
+/**
+ * Remove all conflicts in the index (entries with a stage greater than 0.)
*
* @param index an existing index object
- * @param n the position of the entry
- * @return a pointer to the entry; NULL if out of bounds
*/
-GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, size_t n);
+GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index);
+
+/**@}*/
+
+/** @name Resolve Undo (REUC) index entry manipulation.
+ *
+ * These functions work on the Resolve Undo index extension and contains
+ * data about the original files that led to a merge conflict.
+ */
+/**@{*/
/**
- * Get the count of entries currently in the index
+ * Get the count of resolve undo entries currently in the index.
*
* @param index an existing index object
- * @return integer of count of current entries
+ * @return integer of count of current resolve undo entries
*/
-GIT_EXTERN(unsigned int) git_index_entrycount(git_index *index);
+GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index);
/**
- * Get the count of unmerged entries currently in the index
+ * Finds the resolve undo entry that points to the given path in the Git
+ * index.
*
* @param index an existing index object
- * @return integer of count of current unmerged entries
+ * @param path path to search
+ * @return an index >= 0 if found, -1 otherwise
*/
-GIT_EXTERN(unsigned int) git_index_entrycount_unmerged(git_index *index);
+GIT_EXTERN(int) git_index_reuc_find(git_index *index, const char *path);
/**
- * Get an unmerged entry from the index.
+ * Get a resolve undo entry from the index.
*
* The returned entry is read-only and should not be modified
* of freed by the caller.
*
* @param index an existing index object
* @param path path to search
- * @return the unmerged entry; NULL if not found
+ * @return the resolve undo entry; NULL if not found
*/
-GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_bypath(git_index *index, const char *path);
+GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
/**
- * Get an unmerged entry from the index.
+ * Get a resolve undo entry from the index.
*
* The returned entry is read-only and should not be modified
* of freed by the caller.
*
* @param index an existing index object
* @param n the position of the entry
- * @return a pointer to the unmerged entry; NULL if out of bounds
+ * @return a pointer to the resolve undo entry; NULL if out of bounds
*/
-GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_index *index, size_t n);
+GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
/**
- * Return the stage number from a git index entry
+ * Adds an resolve undo entry for a file based on the given parameters.
*
- * This entry is calculated from the entry's flag
- * attribute like this:
+ * The resolve undo entry contains the OIDs of files that were involved
+ * in a merge conflict after the conflict has been resolved. This allows
+ * conflicts to be re-resolved later.
*
- * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
+ * If there exists a resolve undo entry for the given path in the index,
+ * it will be removed.
*
- * @param entry The entry
- * @returns the stage number
+ * This method will fail in bare index instances.
+ *
+ * @param index an existing index object
+ * @param path filename to add
+ * @param ancestor_mode mode of the ancestor file
+ * @param ancestor_oid oid of the ancestor file
+ * @param our_mode mode of our file
+ * @param our_oid oid of our file
+ * @param their_mode mode of their file
+ * @param their_oid oid of their file
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
+GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
+ int ancestor_mode, git_oid *ancestor_oid,
+ int our_mode, git_oid *our_oid,
+ int their_mode, git_oid *their_oid);
/**
- * Read a tree into the index file with stats
- *
- * The current index contents will be replaced by the specified tree. The total
- * node count is collected in stats.
+ * Remove an resolve undo entry from the index
*
* @param index an existing index object
- * @param tree tree to read
- * @param stats structure that receives the total node count (may be NULL)
+ * @param position position of the resolve undo entry to remove
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_read_tree(git_index *index, git_tree *tree, git_indexer_stats *stats);
+GIT_EXTERN(int) git_index_reuc_remove(git_index *index, int position);
+
+/**@}*/
/** @} */
GIT_END_DECL
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index 87f48fe27..a2a155473 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -16,13 +16,19 @@ GIT_BEGIN_DECL
* This is passed as the first argument to the callback to allow the
* user to see the progress.
*/
-typedef struct git_indexer_stats {
- unsigned int total;
- unsigned int processed;
- unsigned int received;
-} git_indexer_stats;
+typedef struct git_transfer_progress {
+ unsigned int total_objects;
+ unsigned int indexed_objects;
+ unsigned int received_objects;
+ size_t received_bytes;
+} git_transfer_progress;
+/**
+ * Type for progress callbacks during indexing
+ */
+typedef void (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
+
typedef struct git_indexer git_indexer;
typedef struct git_indexer_stream git_indexer_stream;
@@ -31,8 +37,14 @@ typedef struct git_indexer_stream git_indexer_stream;
*
* @param out where to store the indexer instance
* @param path to the directory where the packfile should be stored
+ * @param progress_cb function to call with progress information
+ * @param progress_payload payload for the progress callback
*/
-GIT_EXTERN(int) git_indexer_stream_new(git_indexer_stream **out, const char *path);
+GIT_EXTERN(int) git_indexer_stream_new(
+ git_indexer_stream **out,
+ const char *path,
+ git_transfer_progress_callback progress_cb,
+ void *progress_callback_payload);
/**
* Add data to the indexer
@@ -42,7 +54,7 @@ GIT_EXTERN(int) git_indexer_stream_new(git_indexer_stream **out, const char *pat
* @param size the size of the data
* @param stats stat storage
*/
-GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats);
+GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats);
/**
* Finalize the pack and index
@@ -51,7 +63,7 @@ GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data
*
* @param idx the indexer
*/
-GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats);
+GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats);
/**
* Get the packfile's hash
@@ -88,7 +100,7 @@ GIT_EXTERN(int) git_indexer_new(git_indexer **out, const char *packname);
* @param idx the indexer instance
* @param stats storage for the running state
*/
-GIT_EXTERN(int) git_indexer_run(git_indexer *idx, git_indexer_stats *stats);
+GIT_EXTERN(int) git_indexer_run(git_indexer *idx, git_transfer_progress *stats);
/**
* Write the index file to disk.
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index 447915ef8..d48a89725 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -97,9 +97,9 @@ GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog
/**
* Remove an entry from the reflog by its index
*
- * To ensure there's no gap in the log history, set the `rewrite_previosu_entry` to 1.
- * When deleting entry `n`, member old_oid of entry `n-1` (if any) will be updated with
- * the value of memeber new_oid of entry `n+1`.
+ * To ensure there's no gap in the log history, set `rewrite_previous_entry`
+ * param value to 1. When deleting entry `n`, member old_oid of entry `n-1`
+ * (if any) will be updated with the value of member new_oid of entry `n+1`.
*
* @param reflog a previously loaded reflog.
*
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 6471acc6a..ad5c38902 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -183,10 +183,16 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
* filename will be NULL and the function will return success.
*
* @param remote the remote to download from
- * @param filename where to store the temporary filename
+ * @param progress_cb function to call with progress information. Be aware that
+ * this is called inline with network and indexing operations, so performance
+ * may be affected.
+ * @param progress_payload payload for the progress callback
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
+GIT_EXTERN(int) git_remote_download(
+ git_remote *remote,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload);
/**
* Check whether the remote is connected
@@ -313,6 +319,11 @@ struct git_remote_callbacks {
*/
GIT_EXTERN(void) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
+/**
+ * Get the statistics structure that is filled in by the fetch operation.
+ */
+GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
+
enum {
GIT_REMOTE_DOWNLOAD_TAGS_UNSET,
GIT_REMOTE_DOWNLOAD_TAGS_NONE,
@@ -336,6 +347,24 @@ GIT_EXTERN(int) git_remote_autotag(git_remote *remote);
*/
GIT_EXTERN(void) git_remote_set_autotag(git_remote *remote, int value);
+/**
+ * Give the remote a new name
+ *
+ * All remote-tracking branches and configuration settings
+ * for the remote are updated.
+ *
+ * @param remote the remote to rename
+ * @param new_name the new name the remote should bear
+ * @param callback Optional callback to notify the consumer of fetch refspecs
+ * that haven't been automatically updated and need potential manual tweaking.
+ * @param payload Additional data to pass to the callback
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_rename(
+ git_remote *remote,
+ const char *new_name,
+ int (*callback)(const char *problematic_refspec, void *payload),
+ void *payload);
/** @} */
GIT_END_DECL
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 193ac9523..4d122265c 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -569,6 +569,28 @@ GIT_EXTERN(int) git_repository_set_head_detached(
GIT_EXTERN(int) git_repository_detach_head(
git_repository* repo);
+typedef enum {
+ GIT_REPOSITORY_STATE_NONE,
+ GIT_REPOSITORY_STATE_MERGE,
+ GIT_REPOSITORY_STATE_REVERT,
+ GIT_REPOSITORY_STATE_CHERRY_PICK,
+ GIT_REPOSITORY_STATE_BISECT,
+ GIT_REPOSITORY_STATE_REBASE,
+ GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,
+ GIT_REPOSITORY_STATE_REBASE_MERGE,
+ GIT_REPOSITORY_STATE_APPLY_MAILBOX,
+ GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE,
+} git_repository_state_t;
+
+/**
+ * Determines the status of a git repository - ie, whether an operation
+ * (merge, cherry-pick, etc) is in progress.
+ *
+ * @param repo Repository pointer
+ * @return The state of the repository
+ */
+GIT_EXTERN(int) git_repository_state(git_repository *repo);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/stash.h b/include/git2/stash.h
new file mode 100644
index 000000000..3ecd9e88d
--- /dev/null
+++ b/include/git2/stash.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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_stash_h__
+#define INCLUDE_git_stash_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/stash.h
+ * @brief Git stash management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+enum {
+ GIT_STASH_DEFAULT = 0,
+
+ /* All changes already added to the index
+ * are left intact in the working directory
+ */
+ GIT_STASH_KEEP_INDEX = (1 << 0),
+
+ /* All untracked files are also stashed and then
+ * cleaned up from the working directory
+ */
+ GIT_STASH_INCLUDE_UNTRACKED = (1 << 1),
+
+ /* All ignored files are also stashed and then
+ * cleaned up from the working directory
+ */
+ GIT_STASH_INCLUDE_IGNORED = (1 << 2),
+};
+
+/**
+ * Save the local modifications to a new stash.
+ *
+ * @param out Object id of the commit containing the stashed state.
+ * This commit is also the target of the direct reference refs/stash.
+ *
+ * @param repo The owning repository.
+ *
+ * @param stasher The identity of the person performing the stashing.
+ *
+ * @param message Optional description along with the stashed state.
+ *
+ * @param flags Flags to control the stashing process.
+ *
+ * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
+ * or error code.
+ */
+
+GIT_EXTERN(int) git_stash_save(
+ git_oid *out,
+ git_repository *repo,
+ git_signature *stasher,
+ const char *message,
+ uint32_t flags);
+
+/**
+ * When iterating over all the stashed states, callback that will be
+ * issued per entry.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ *
+ * @param message The stash message.
+ *
+ * @param stash_oid The commit oid of the stashed state.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+typedef int (*stash_cb)(
+ size_t index,
+ const char* message,
+ const git_oid *stash_oid,
+ void *payload);
+
+/**
+ * Loop over all the stashed states and issue a callback for each one.
+ *
+ * If the callback returns a non-zero value, this will stop looping.
+ *
+ * @param repo Repository where to find the stash.
+ *
+ * @param callabck Callback to invoke per found stashed state. The most recent
+ * stash state will be enumerated first.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_stash_foreach(
+ git_repository *repo,
+ stash_cb callback,
+ void *payload);
+
+/**
+ * Remove a single stashed state from the stash list.
+ *
+ * @param repo The owning repository.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ *
+ * @return 0 on success, or error code
+ */
+
+GIT_EXTERN(int) git_stash_drop(
+ git_repository *repo,
+ size_t index);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/src/attr.c b/src/attr.c
index 025ad3c87..f5e09cc08 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -307,7 +307,7 @@ static int load_attr_blob_from_index(
(error = git_index_find(index, relfile)) < 0)
return error;
- entry = git_index_get(index, error);
+ entry = git_index_get_byindex(index, error);
if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
return GIT_ENOTFOUND;
diff --git a/src/branch.c b/src/branch.c
index 991314508..43bebd9ef 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -92,6 +92,8 @@ cleanup:
int git_branch_delete(git_reference *branch)
{
int is_head;
+ git_buf config_section = GIT_BUF_INIT;
+ int error = -1;
assert(branch);
@@ -110,7 +112,23 @@ int git_branch_delete(git_reference *branch)
return -1;
}
- return git_reference_delete(branch);
+ if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ goto on_error;
+
+ if (git_config_rename_section(
+ git_reference_owner(branch),
+ git_buf_cstr(&config_section),
+ NULL) < 0)
+ goto on_error;
+
+ if (git_reference_delete(branch) < 0)
+ goto on_error;
+
+ error = 0;
+
+on_error:
+ git_buf_free(&config_section);
+ return error;
}
typedef struct {
@@ -161,7 +179,9 @@ int git_branch_move(
const char *new_branch_name,
int force)
{
- git_buf new_reference_name = GIT_BUF_INIT;
+ git_buf new_reference_name = GIT_BUF_INIT,
+ old_config_section = GIT_BUF_INIT,
+ new_config_section = GIT_BUF_INIT;
int error;
assert(branch && new_branch_name);
@@ -172,10 +192,28 @@ int git_branch_move(
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
goto cleanup;
- error = git_reference_rename(branch, git_buf_cstr(&new_reference_name), force);
+ if (git_buf_printf(
+ &old_config_section,
+ "branch.%s",
+ git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_rename(branch, git_buf_cstr(&new_reference_name), force)) < 0)
+ goto cleanup;
+
+ if (git_buf_printf(&new_config_section, "branch.%s", new_branch_name) < 0)
+ goto cleanup;
+
+ if ((error = git_config_rename_section(
+ git_reference_owner(branch),
+ git_buf_cstr(&old_config_section),
+ git_buf_cstr(&new_config_section))) < 0)
+ goto cleanup;
cleanup:
git_buf_free(&new_reference_name);
+ git_buf_free(&old_config_section);
+ git_buf_free(&new_config_section);
return error;
}
diff --git a/src/buffer.c b/src/buffer.c
index b40b16b66..e55b0a230 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -549,3 +549,31 @@ void git_buf_unescape(git_buf *buf)
{
buf->size = git__unescape(buf->ptr);
}
+
+int git_buf_splice(
+ git_buf *buf,
+ size_t where,
+ size_t nb_to_remove,
+ const char *data,
+ size_t nb_to_insert)
+{
+ assert(buf &&
+ where <= git_buf_len(buf) &&
+ where + nb_to_remove <= git_buf_len(buf));
+
+ /* Ported from git.git
+ * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
+ */
+ if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0)
+ return -1;
+
+ memmove(buf->ptr + where + nb_to_insert,
+ buf->ptr + where + nb_to_remove,
+ buf->size - where - nb_to_remove);
+
+ memcpy(buf->ptr + where, data, nb_to_insert);
+
+ buf->size = buf->size + nb_to_insert - nb_to_remove;
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
diff --git a/src/buffer.h b/src/buffer.h
index 2aae06c7c..a2896d486 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -158,4 +158,29 @@ void git_buf_unescape(git_buf *buf);
/* Write data as base64 encoded in buffer */
int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
+/*
+ * Insert, remove or replace a portion of the buffer.
+ *
+ * @param buf The buffer to work with
+ *
+ * @param where The location in the buffer where the transformation
+ * should be applied.
+ *
+ * @param nb_to_remove The number of chars to be removed. 0 to not
+ * remove any character in the buffer.
+ *
+ * @param data A pointer to the data which should be inserted.
+ *
+ * @param nb_to_insert The number of chars to be inserted. 0 to not
+ * insert any character from the buffer.
+ *
+ * @return 0 or an error code.
+ */
+int git_buf_splice(
+ git_buf *buf,
+ size_t where,
+ size_t nb_to_remove,
+ const char *data,
+ size_t nb_to_insert);
+
#endif
diff --git a/src/checkout.c b/src/checkout.c
index b56b459d2..b7bfa409a 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -27,12 +27,13 @@ struct checkout_diff_data
git_buf *path;
size_t workdir_len;
git_checkout_opts *checkout_opts;
- git_indexer_stats *stats;
git_repository *owner;
bool can_symlink;
bool found_submodules;
bool create_submodules;
int error;
+ size_t total_steps;
+ size_t completed_steps;
};
static int buffer_to_file(
@@ -158,6 +159,18 @@ static int checkout_submodule(
return 0;
}
+static void report_progress(
+ struct checkout_diff_data *data,
+ const char *path)
+{
+ if (data->checkout_opts->progress_cb)
+ data->checkout_opts->progress_cb(
+ path,
+ data->completed_steps,
+ data->total_steps,
+ data->checkout_opts->progress_payload);
+}
+
static int checkout_blob(
struct checkout_diff_data *data,
const git_diff_file *file)
@@ -191,7 +204,6 @@ static int checkout_remove_the_old(
git_checkout_opts *opts = data->checkout_opts;
GIT_UNUSED(progress);
- data->stats->processed++;
if ((delta->status == GIT_DELTA_UNTRACKED &&
(opts->checkout_strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0) ||
@@ -202,6 +214,9 @@ static int checkout_remove_the_old(
delta->new_file.path,
git_repository_workdir(data->owner),
GIT_DIRREMOVAL_FILES_AND_DIRS);
+
+ data->completed_steps++;
+ report_progress(data, delta->new_file.path);
}
return data->error;
@@ -216,7 +231,6 @@ static int checkout_create_the_new(
bool do_checkout = false, do_notify = false;
GIT_UNUSED(progress);
- data->stats->processed++;
if (delta->status == GIT_DELTA_MODIFIED ||
delta->status == GIT_DELTA_TYPECHANGE)
@@ -243,14 +257,22 @@ static int checkout_create_the_new(
if (do_checkout) {
bool is_submodule = S_ISGITLINK(delta->old_file.mode);
- if (is_submodule)
+ if (is_submodule) {
data->found_submodules = true;
+ }
- if (!is_submodule && !data->create_submodules)
+ if (!is_submodule && !data->create_submodules) {
error = checkout_blob(data, &delta->old_file);
+ data->completed_steps++;
+ report_progress(data, delta->old_file.path);
+ }
- else if (is_submodule && data->create_submodules)
+ else if (is_submodule && data->create_submodules) {
error = checkout_submodule(data, &delta->old_file);
+ data->completed_steps++;
+ report_progress(data, delta->old_file.path);
+ }
+
}
if (error)
@@ -304,11 +326,9 @@ static void normalize_options(git_checkout_opts *normalized, git_checkout_opts *
int git_checkout_index(
git_repository *repo,
- git_checkout_opts *opts,
- git_indexer_stats *stats)
+ git_checkout_opts *opts)
{
git_diff_list *diff = NULL;
- git_indexer_stats dummy_stats;
git_diff_options diff_opts = {0};
git_checkout_opts checkout_opts;
@@ -339,20 +359,13 @@ int git_checkout_index(
normalize_options(&checkout_opts, opts);
- if (!stats)
- stats = &dummy_stats;
-
- stats->processed = 0;
- /* total based on 3 passes, but it might be 2 if no submodules */
- stats->total = (unsigned int)git_diff_num_deltas(diff) * 3;
-
memset(&data, 0, sizeof(data));
data.path = &workdir;
data.workdir_len = git_buf_len(&workdir);
data.checkout_opts = &checkout_opts;
- data.stats = stats;
data.owner = repo;
+ data.total_steps = (size_t)git_diff_num_deltas(diff);
if ((error = retrieve_symlink_capabilities(repo, &data.can_symlink)) < 0)
goto cleanup;
@@ -367,6 +380,8 @@ int git_checkout_index(
* checked out during pass #2.
*/
+ report_progress(&data, NULL);
+
if (!(error = git_diff_foreach(
diff, &data, checkout_remove_the_old, NULL, NULL)) &&
!(error = git_diff_foreach(
@@ -378,7 +393,7 @@ int git_checkout_index(
diff, &data, checkout_create_the_new, NULL, NULL);
}
- stats->processed = stats->total;
+ report_progress(&data, NULL);
cleanup:
if (error == GIT_EUSER)
@@ -393,8 +408,7 @@ cleanup:
int git_checkout_tree(
git_repository *repo,
git_object *treeish,
- git_checkout_opts *opts,
- git_indexer_stats *stats)
+ git_checkout_opts *opts)
{
git_index *index = NULL;
git_tree *tree = NULL;
@@ -411,13 +425,13 @@ int git_checkout_tree(
if ((error = git_repository_index(&index, repo)) < 0)
goto cleanup;
- if ((error = git_index_read_tree(index, tree, NULL)) < 0)
+ if ((error = git_index_read_tree(index, tree)) < 0)
goto cleanup;
if ((error = git_index_write(index)) < 0)
goto cleanup;
- error = git_checkout_index(repo, opts, stats);
+ error = git_checkout_index(repo, opts);
cleanup:
git_index_free(index);
@@ -427,8 +441,7 @@ cleanup:
int git_checkout_head(
git_repository *repo,
- git_checkout_opts *opts,
- git_indexer_stats *stats)
+ git_checkout_opts *opts)
{
git_reference *head;
int error;
@@ -442,7 +455,7 @@ int git_checkout_head(
if ((error = git_reference_peel(&tree, head, GIT_OBJ_TREE)) < 0)
goto cleanup;
- error = git_checkout_tree(repo, tree, opts, stats);
+ error = git_checkout_tree(repo, tree, opts);
cleanup:
git_reference_free(head);
diff --git a/src/clone.c b/src/clone.c
index 85e69ad97..ab8b9bcbb 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -248,22 +248,20 @@ cleanup:
-static int setup_remotes_and_fetch(git_repository *repo,
- const char *origin_url,
- git_indexer_stats *fetch_stats)
+static int setup_remotes_and_fetch(
+ git_repository *repo,
+ const char *origin_url,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload)
{
int retcode = GIT_ERROR;
git_remote *origin = NULL;
- git_off_t bytes = 0;
- git_indexer_stats dummy_stats;
-
- if (!fetch_stats) fetch_stats = &dummy_stats;
/* Create the "origin" remote */
if (!git_remote_add(&origin, repo, GIT_REMOTE_ORIGIN, origin_url)) {
/* Connect and download everything */
if (!git_remote_connect(origin, GIT_DIR_FETCH)) {
- if (!git_remote_download(origin, &bytes, fetch_stats)) {
+ if (!git_remote_download(origin, progress_cb, progress_payload)) {
/* Create "origin/foo" branches for all remote branches */
if (!git_remote_update_tips(origin)) {
/* Point HEAD to the same ref as the remote's head */
@@ -311,23 +309,21 @@ static int clone_internal(
git_repository **out,
const char *origin_url,
const char *path,
- git_indexer_stats *fetch_stats,
- git_indexer_stats *checkout_stats,
+ git_transfer_progress_callback fetch_progress_cb,
+ void *fetch_progress_payload,
git_checkout_opts *checkout_opts,
bool is_bare)
{
int retcode = GIT_ERROR;
git_repository *repo = NULL;
- git_indexer_stats dummy_stats;
-
- if (!fetch_stats) fetch_stats = &dummy_stats;
if (!path_is_okay(path)) {
return GIT_ERROR;
}
if (!(retcode = git_repository_init(&repo, path, is_bare))) {
- if ((retcode = setup_remotes_and_fetch(repo, origin_url, fetch_stats)) < 0) {
+ if ((retcode = setup_remotes_and_fetch(repo, origin_url,
+ fetch_progress_cb, fetch_progress_payload)) < 0) {
/* Failed to fetch; clean up */
git_repository_free(repo);
git_futils_rmdir_r(path, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS);
@@ -338,15 +334,17 @@ static int clone_internal(
}
if (!retcode && should_checkout(repo, is_bare, checkout_opts))
- retcode = git_checkout_head(*out, checkout_opts, checkout_stats);
+ retcode = git_checkout_head(*out, checkout_opts);
return retcode;
}
-int git_clone_bare(git_repository **out,
- const char *origin_url,
- const char *dest_path,
- git_indexer_stats *fetch_stats)
+int git_clone_bare(
+ git_repository **out,
+ const char *origin_url,
+ const char *dest_path,
+ git_transfer_progress_callback fetch_progress_cb,
+ void *fetch_progress_payload)
{
assert(out && origin_url && dest_path);
@@ -354,19 +352,20 @@ int git_clone_bare(git_repository **out,
out,
origin_url,
dest_path,
- fetch_stats,
- NULL,
+ fetch_progress_cb,
+ fetch_progress_payload,
NULL,
1);
}
-int git_clone(git_repository **out,
- const char *origin_url,
- const char *workdir_path,
- git_indexer_stats *fetch_stats,
- git_indexer_stats *checkout_stats,
- git_checkout_opts *checkout_opts)
+int git_clone(
+ git_repository **out,
+ const char *origin_url,
+ const char *workdir_path,
+ git_transfer_progress_callback fetch_progress_cb,
+ void *fetch_progress_payload,
+ git_checkout_opts *checkout_opts)
{
assert(out && origin_url && workdir_path);
@@ -374,8 +373,8 @@ int git_clone(git_repository **out,
out,
origin_url,
workdir_path,
- fetch_stats,
- checkout_stats,
+ fetch_progress_cb,
+ fetch_progress_payload,
checkout_opts,
0);
}
diff --git a/src/config.c b/src/config.c
index f9bd205af..937510d7e 100644
--- a/src/config.c
+++ b/src/config.c
@@ -400,7 +400,7 @@ static int get_string_at_file(const char **out, git_config_file *file, const cha
*out = NULL;
res = file->get(file, name, &entry);
- if (res != GIT_ENOTFOUND)
+ if (!res)
*out = entry->value;
return res;
@@ -720,3 +720,81 @@ fail_parse:
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
return -1;
}
+
+struct rename_data
+{
+ git_config *config;
+ const char *old_name;
+ const char *new_name;
+};
+
+static int rename_config_entries_cb(
+ const git_config_entry *entry,
+ void *payload)
+{
+ struct rename_data *data = (struct rename_data *)payload;
+
+ if (data->new_name != NULL) {
+ git_buf name = GIT_BUF_INIT;
+ int error;
+
+ if (git_buf_printf(
+ &name,
+ "%s.%s",
+ data->new_name,
+ entry->name + strlen(data->old_name) + 1) < 0)
+ return -1;
+
+ error = git_config_set_string(
+ data->config,
+ git_buf_cstr(&name),
+ entry->value);
+
+ git_buf_free(&name);
+
+ if (error)
+ return error;
+ }
+
+ return git_config_delete(data->config, entry->name);
+}
+
+int git_config_rename_section(
+ git_repository *repo,
+ const char *old_section_name,
+ const char *new_section_name)
+{
+ git_config *config;
+ git_buf pattern = GIT_BUF_INIT;
+ int error = -1;
+ struct rename_data data;
+
+ git_buf_puts_escape_regex(&pattern, old_section_name);
+ git_buf_puts(&pattern, "\\..+");
+ if (git_buf_oom(&pattern))
+ goto cleanup;
+
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ goto cleanup;
+
+ data.config = config;
+ data.old_name = old_section_name;
+ data.new_name = new_section_name;
+
+ if ((error = git_config_foreach_match(
+ config,
+ git_buf_cstr(&pattern),
+ rename_config_entries_cb, &data)) < 0) {
+ giterr_set(GITERR_CONFIG,
+ "Cannot rename config section '%s' to '%s'",
+ old_section_name,
+ new_section_name);
+ goto cleanup;
+ }
+
+ error = 0;
+
+cleanup:
+ git_buf_free(&pattern);
+ return error;
+}
diff --git a/src/config.h b/src/config.h
index 16b8413b7..a0569ec93 100644
--- a/src/config.h
+++ b/src/config.h
@@ -27,4 +27,9 @@ extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_xdg_r(git_buf *system_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
+extern int git_config_rename_section(
+ git_repository *repo,
+ const char *old_section_name, /* eg "branch.dummy" */
+ const char *new_section_name); /* NULL to drop the old section */
+
#endif
diff --git a/src/fetch.c b/src/fetch.c
index dc01f6791..0aabe744f 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -19,6 +19,8 @@
#include "netops.h"
#include "pkt.h"
+#define NETWORK_XFER_THRESHOLD (100*1024)
+
struct filter_payload {
git_remote *remote;
const git_refspec *spec, *tagspec;
@@ -302,7 +304,10 @@ on_error:
return error;
}
-int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
+int git_fetch_download_pack(
+ git_remote *remote,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload)
{
git_transport *t = remote->transport;
@@ -310,13 +315,14 @@ int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_st
return 0;
if (t->own_logic)
- return t->download_pack(t, remote->repo, bytes, stats);
+ return t->download_pack(t, remote->repo, &remote->stats);
- return git_fetch__download_pack(t, remote->repo, bytes, stats);
+ return git_fetch__download_pack(t, remote->repo, &remote->stats,
+ progress_cb, progress_payload);
}
-static int no_sideband(git_transport *t, git_indexer_stream *idx, gitno_buffer *buf, git_off_t *bytes, git_indexer_stats *stats)
+static int no_sideband(git_transport *t, git_indexer_stream *idx, gitno_buffer *buf, git_transfer_progress *stats)
{
int recvd;
@@ -333,8 +339,6 @@ static int no_sideband(git_transport *t, git_indexer_stream *idx, gitno_buffer *
if ((recvd = gitno_recv(buf)) < 0)
return -1;
-
- *bytes += recvd;
} while(recvd > 0);
if (git_indexer_stream_finalize(idx, stats))
@@ -343,27 +347,58 @@ static int no_sideband(git_transport *t, git_indexer_stream *idx, gitno_buffer *
return 0;
}
+struct network_packetsize_payload
+{
+ git_transfer_progress_callback callback;
+ void *payload;
+ git_transfer_progress *stats;
+ git_off_t last_fired_bytes;
+};
+
+static void network_packetsize(int received, void *payload)
+{
+ struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
+
+ /* Accumulate bytes */
+ npp->stats->received_bytes += received;
+
+ /* Fire notification if the threshold is reached */
+ if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
+ npp->last_fired_bytes = npp->stats->received_bytes;
+ npp->callback(npp->stats, npp->payload);
+ }
+}
+
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(
git_transport *t,
git_repository *repo,
- git_off_t *bytes,
- git_indexer_stats *stats)
+ git_transfer_progress *stats,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload)
{
git_buf path = GIT_BUF_INIT;
gitno_buffer *buf = &t->buffer;
git_indexer_stream *idx = NULL;
int error = -1;
+ struct network_packetsize_payload npp = {0};
+
+ if (progress_cb) {
+ npp.callback = progress_cb;
+ npp.payload = progress_payload;
+ npp.stats = stats;
+ buf->packetsize_cb = &network_packetsize;
+ buf->packetsize_payload = &npp;
+ }
if (git_buf_joinpath(&path, git_repository_path(repo), "objects/pack") < 0)
return -1;
- if (git_indexer_stream_new(&idx, git_buf_cstr(&path)) < 0)
+ if (git_indexer_stream_new(&idx, git_buf_cstr(&path), progress_cb, progress_payload) < 0)
goto on_error;
git_buf_free(&path);
- memset(stats, 0, sizeof(git_indexer_stats));
- *bytes = 0;
+ memset(stats, 0, sizeof(git_transfer_progress));
/*
* If the remote doesn't support the side-band, we can feed
@@ -371,7 +406,7 @@ int git_fetch__download_pack(
* check which one belongs there.
*/
if (!t->caps.side_band && !t->caps.side_band_64k) {
- if (no_sideband(t, idx, buf, bytes, stats) < 0)
+ if (no_sideband(t, idx, buf, stats) < 0)
goto on_error;
git_indexer_stream_free(idx);
@@ -398,7 +433,6 @@ int git_fetch__download_pack(
git__free(pkt);
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
- *bytes += p->len;
if (git_indexer_stream_add(idx, p->data, p->len, stats) < 0)
goto on_error;
diff --git a/src/fetch.h b/src/fetch.h
index 87bb43b07..5b8c20665 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -10,9 +10,19 @@
#include "netops.h"
int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
-int git_fetch__download_pack(git_transport *t, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
+int git_fetch_download_pack(
+ git_remote *remote,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload);
+
+int git_fetch__download_pack(
+ git_transport *t,
+ git_repository *repo,
+ git_transfer_progress *stats,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload);
+
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
#endif
diff --git a/src/index.c b/src/index.c
index f9f3b14cc..44dd93417 100644
--- a/src/index.c
+++ b/src/index.c
@@ -81,6 +81,11 @@ struct entry_long {
char path[1]; /* arbitrary length */
};
+struct entry_srch_key {
+ const char *path;
+ int stage;
+};
+
/* local declarations */
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
@@ -90,53 +95,126 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
static int is_index_extended(git_index *index);
static int write_index(git_index *index, git_filebuf *file);
+static int index_find(git_index *index, const char *path, int stage);
+
static void index_entry_free(git_index_entry *entry);
+static void index_entry_reuc_free(git_index_reuc_entry *reuc);
+
+GIT_INLINE(int) index_entry_stage(const git_index_entry *entry)
+{
+ return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
+}
static int index_srch(const void *key, const void *array_member)
{
+ const struct entry_srch_key *srch_key = key;
const git_index_entry *entry = array_member;
+ int ret;
+
+ ret = strcmp(srch_key->path, entry->path);
- return strcmp(key, entry->path);
+ if (ret == 0)
+ ret = srch_key->stage - index_entry_stage(entry);
+
+ return ret;
}
static int index_isrch(const void *key, const void *array_member)
{
+ const struct entry_srch_key *srch_key = key;
const git_index_entry *entry = array_member;
+ int ret;
+
+ ret = strcasecmp(srch_key->path, entry->path);
+
+ if (ret == 0)
+ ret = srch_key->stage - index_entry_stage(entry);
- return strcasecmp(key, entry->path);
+ return ret;
+}
+
+static int index_cmp_path(const void *a, const void *b)
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+static int index_icmp_path(const void *a, const void *b)
+{
+ return strcasecmp((const char *)a, (const char *)b);
+}
+
+static int index_srch_path(const void *path, const void *array_member)
+{
+ const git_index_entry *entry = array_member;
+
+ return strcmp((const char *)path, entry->path);
+}
+
+static int index_isrch_path(const void *path, const void *array_member)
+{
+ const git_index_entry *entry = array_member;
+
+ return strcasecmp((const char *)path, entry->path);
}
static int index_cmp(const void *a, const void *b)
{
+ int diff;
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
- return strcmp(entry_a->path, entry_b->path);
+ diff = strcmp(entry_a->path, entry_b->path);
+
+ if (diff == 0)
+ diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b));
+
+ return diff;
}
static int index_icmp(const void *a, const void *b)
{
+ int diff;
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
- return strcasecmp(entry_a->path, entry_b->path);
+ diff = strcasecmp(entry_a->path, entry_b->path);
+
+ if (diff == 0)
+ diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b));
+
+ return diff;
}
-static int unmerged_srch(const void *key, const void *array_member)
+static int reuc_srch(const void *key, const void *array_member)
{
- const git_index_entry_unmerged *entry = array_member;
+ const git_index_reuc_entry *reuc = array_member;
- return strcmp(key, entry->path);
+ return strcmp(key, reuc->path);
}
-static int unmerged_cmp(const void *a, const void *b)
+static int reuc_isrch(const void *key, const void *array_member)
{
- const git_index_entry_unmerged *info_a = a;
- const git_index_entry_unmerged *info_b = b;
+ const git_index_reuc_entry *reuc = array_member;
+
+ return strcasecmp(key, reuc->path);
+}
+
+static int reuc_cmp(const void *a, const void *b)
+{
+ const git_index_reuc_entry *info_a = a;
+ const git_index_reuc_entry *info_b = b;
return strcmp(info_a->path, info_b->path);
}
+static int reuc_icmp(const void *a, const void *b)
+{
+ const git_index_reuc_entry *info_a = a;
+ const git_index_reuc_entry *info_b = b;
+
+ return strcasecmp(info_a->path, info_b->path);
+}
+
static unsigned int index_create_mode(unsigned int mode)
{
if (S_ISLNK(mode))
@@ -165,9 +243,16 @@ static unsigned int index_merge_mode(
static void index_set_ignore_case(git_index *index, bool ignore_case)
{
index->entries._cmp = ignore_case ? index_icmp : index_cmp;
+ index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path;
index->entries_search = ignore_case ? index_isrch : index_srch;
+ index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path;
index->entries.sorted = 0;
git_vector_sort(&index->entries);
+
+ index->reuc._cmp = ignore_case ? reuc_icmp : reuc_cmp;
+ index->reuc_search = ignore_case ? reuc_isrch : reuc_srch;
+ index->reuc.sorted = 0;
+ git_vector_sort(&index->reuc);
}
int git_index_open(git_index **index_out, const char *index_path)
@@ -185,7 +270,10 @@ int git_index_open(git_index **index_out, const char *index_path)
if (git_vector_init(&index->entries, 32, index_cmp) < 0)
return -1;
+ index->entries_cmp_path = index_cmp_path;
index->entries_search = index_srch;
+ index->entries_search_path = index_srch_path;
+ index->reuc_search = reuc_srch;
/* Check if index file is stored on disk already */
if (git_path_exists(index->index_file_path) == true)
@@ -199,6 +287,7 @@ int git_index_open(git_index **index_out, const char *index_path)
static void index_free(git_index *index)
{
git_index_entry *e;
+ git_index_reuc_entry *reuc;
unsigned int i;
git_index_clear(index);
@@ -206,10 +295,10 @@ static void index_free(git_index *index)
index_entry_free(e);
}
git_vector_free(&index->entries);
- git_vector_foreach(&index->unmerged, i, e) {
- index_entry_free(e);
+ git_vector_foreach(&index->reuc, i, reuc) {
+ index_entry_reuc_free(reuc);
}
- git_vector_free(&index->unmerged);
+ git_vector_free(&index->reuc);
git__free(index->index_file_path);
git__free(index);
@@ -236,15 +325,15 @@ void git_index_clear(git_index *index)
git__free(e);
}
- for (i = 0; i < index->unmerged.length; ++i) {
- git_index_entry_unmerged *e;
- e = git_vector_get(&index->unmerged, i);
+ for (i = 0; i < index->reuc.length; ++i) {
+ git_index_reuc_entry *e;
+ e = git_vector_get(&index->reuc, i);
git__free(e->path);
git__free(e);
}
git_vector_clear(&index->entries);
- git_vector_clear(&index->unmerged);
+ git_vector_clear(&index->reuc);
index->last_modified = 0;
git_tree_cache_free(index->tree);
@@ -340,6 +429,7 @@ int git_index_write(git_index *index)
int error;
git_vector_sort(&index->entries);
+ git_vector_sort(&index->reuc);
if ((error = git_filebuf_open(
&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
@@ -367,16 +457,25 @@ unsigned int git_index_entrycount(git_index *index)
return (unsigned int)index->entries.length;
}
-unsigned int git_index_entrycount_unmerged(git_index *index)
+git_index_entry *git_index_get_byindex(git_index *index, size_t n)
{
assert(index);
- return (unsigned int)index->unmerged.length;
+ git_vector_sort(&index->entries);
+ return git_vector_get(&index->entries, n);
}
-git_index_entry *git_index_get(git_index *index, size_t n)
+git_index_entry *git_index_get_bypath(git_index *index, const char *path, int stage)
{
+ int pos;
+
+ assert(index);
+
git_vector_sort(&index->entries);
- return git_vector_get(&index->entries, n);
+
+ if((pos = index_find(index, path, stage)) < 0)
+ return NULL;
+
+ return git_index_get_byindex(index, pos);
}
void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
@@ -393,7 +492,7 @@ void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
entry->file_size = st->st_size;
}
-static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)
+static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
{
git_index_entry *entry = NULL;
struct stat st;
@@ -402,8 +501,6 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
git_buf full_path = GIT_BUF_INIT;
int error;
- assert(stage >= 0 && stage <= 3);
-
if (INDEX_OWNER(index) == NULL ||
(workdir = git_repository_workdir(INDEX_OWNER(index))) == NULL)
{
@@ -436,7 +533,6 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
git_index__init_entry_from_stat(&st, entry);
entry->oid = oid;
- entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
entry->path = git__strdup(rel_path);
GITERR_CHECK_ALLOC(entry->path);
@@ -444,6 +540,46 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
return 0;
}
+static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
+ const char *path,
+ int ancestor_mode, git_oid *ancestor_oid,
+ int our_mode, git_oid *our_oid, int their_mode, git_oid *their_oid)
+{
+ git_index_reuc_entry *reuc = NULL;
+
+ assert(reuc_out && path);
+
+ *reuc_out = NULL;
+
+ reuc = git__calloc(1, sizeof(git_index_reuc_entry));
+ GITERR_CHECK_ALLOC(reuc);
+
+ reuc->path = git__strdup(path);
+ if (reuc->path == NULL)
+ return -1;
+
+ reuc->mode[0] = ancestor_mode;
+ git_oid_cpy(&reuc->oid[0], ancestor_oid);
+
+ reuc->mode[1] = our_mode;
+ git_oid_cpy(&reuc->oid[1], our_oid);
+
+ reuc->mode[2] = their_mode;
+ git_oid_cpy(&reuc->oid[2], their_oid);
+
+ *reuc_out = reuc;
+ return 0;
+}
+
+static void index_entry_reuc_free(git_index_reuc_entry *reuc)
+{
+ if (!reuc)
+ return;
+
+ git__free(reuc->path);
+ git__free(reuc);
+}
+
static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
{
git_index_entry *entry;
@@ -486,10 +622,10 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
if (path_length < GIT_IDXENTRY_NAMEMASK)
entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
else
- entry->flags |= GIT_IDXENTRY_NAMEMASK;;
+ entry->flags |= GIT_IDXENTRY_NAMEMASK;
/* look if an entry with this path already exists */
- if ((position = git_index_find(index, entry->path)) >= 0) {
+ if ((position = index_find(index, entry->path, index_entry_stage(entry))) >= 0) {
existing = (git_index_entry **)&index->entries.contents[position];
/* update filemode to existing values if stat is not trusted */
@@ -510,34 +646,56 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
return 0;
}
-static int index_add(git_index *index, const char *path, int stage, int replace)
+static int index_conflict_to_reuc(git_index *index, const char *path)
{
- git_index_entry *entry = NULL;
+ git_index_entry *conflict_entries[3];
+ int ancestor_mode, our_mode, their_mode;
+ git_oid *ancestor_oid, *our_oid, *their_oid;
int ret;
- if ((ret = index_entry_init(&entry, index, path, stage)) < 0 ||
- (ret = index_insert(index, entry, replace)) < 0)
- {
- index_entry_free(entry);
+ if ((ret = git_index_conflict_get(&conflict_entries[0],
+ &conflict_entries[1], &conflict_entries[2], index, path)) < 0)
return ret;
- }
- git_tree_cache_invalidate_path(index->tree, entry->path);
- return 0;
-}
+ ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode;
+ our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode;
+ their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode;
-int git_index_add(git_index *index, const char *path, int stage)
-{
- return index_add(index, path, stage, 1);
+ ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->oid;
+ our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->oid;
+ their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->oid;
+
+ if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid,
+ our_mode, our_oid, their_mode, their_oid)) >= 0)
+ ret = git_index_conflict_remove(index, path);
+
+ return ret;
}
-int git_index_append(git_index *index, const char *path, int stage)
+int git_index_add_from_workdir(git_index *index, const char *path)
{
- return index_add(index, path, stage, 0);
+ git_index_entry *entry = NULL;
+ int ret;
+
+ assert(index && path);
+
+ if ((ret = index_entry_init(&entry, index, path)) < 0 ||
+ (ret = index_insert(index, entry, 1)) < 0)
+ goto on_error;
+
+ /* Adding implies conflict was resolved, move conflict entries to REUC */
+ if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
+ goto on_error;
+
+ git_tree_cache_invalidate_path(index->tree, entry->path);
+ return 0;
+
+on_error:
+ index_entry_free(entry);
+ return ret;
}
-static int index_add2(
- git_index *index, const git_index_entry *source_entry, int replace)
+int git_index_add(git_index *index, const git_index_entry *source_entry)
{
git_index_entry *entry = NULL;
int ret;
@@ -546,7 +704,7 @@ static int index_add2(
if (entry == NULL)
return -1;
- if ((ret = index_insert(index, entry, replace)) < 0) {
+ if ((ret = index_insert(index, entry, 1)) < 0) {
index_entry_free(entry);
return ret;
}
@@ -555,23 +713,17 @@ static int index_add2(
return 0;
}
-int git_index_add2(git_index *index, const git_index_entry *source_entry)
-{
- return index_add2(index, source_entry, 1);
-}
-
-int git_index_append2(git_index *index, const git_index_entry *source_entry)
-{
- return index_add2(index, source_entry, 0);
-}
-
-int git_index_remove(git_index *index, int position)
+int git_index_remove(git_index *index, const char *path, int stage)
{
+ int position;
int error;
git_index_entry *entry;
git_vector_sort(&index->entries);
+ if ((position = index_find(index, path, stage)) < 0)
+ return position;
+
entry = git_vector_get(&index->entries, position);
if (entry != NULL)
git_tree_cache_invalidate_path(index->tree, entry->path);
@@ -584,45 +736,277 @@ int git_index_remove(git_index *index, int position)
return error;
}
+static int index_find(git_index *index, const char *path, int stage)
+{
+ struct entry_srch_key srch_key;
+
+ assert(path);
+
+ srch_key.path = path;
+ srch_key.stage = stage;
+
+ return git_vector_bsearch2(&index->entries, index->entries_search, &srch_key);
+}
+
int git_index_find(git_index *index, const char *path)
{
- return git_vector_bsearch2(&index->entries, index->entries_search, path);
+ int pos;
+
+ assert(index && path);
+
+ if ((pos = git_vector_bsearch2(&index->entries, index->entries_search_path, path)) < 0)
+ return pos;
+
+ /* Since our binary search only looked at path, we may be in the
+ * middle of a list of stages. */
+ while (pos > 0) {
+ git_index_entry *prev = git_vector_get(&index->entries, pos-1);
+
+ if (index->entries_cmp_path(prev->path, path) != 0)
+ break;
+
+ --pos;
+ }
+
+ return pos;
}
unsigned int git_index__prefix_position(git_index *index, const char *path)
{
+ struct entry_srch_key srch_key;
unsigned int pos;
- git_vector_bsearch3(&pos, &index->entries, index->entries_search, path);
+ srch_key.path = path;
+ srch_key.stage = 0;
+
+ git_vector_bsearch3(&pos, &index->entries, index->entries_search, &srch_key);
return pos;
}
-void git_index_uniq(git_index *index)
+int git_index_conflict_add(git_index *index,
+ const git_index_entry *ancestor_entry,
+ const git_index_entry *our_entry,
+ const git_index_entry *their_entry)
+{
+ git_index_entry *entries[3] = { 0 };
+ size_t i;
+ int ret = 0;
+
+ assert (index);
+
+ if ((ancestor_entry != NULL && (entries[0] = index_entry_dup(ancestor_entry)) == NULL) ||
+ (our_entry != NULL && (entries[1] = index_entry_dup(our_entry)) == NULL) ||
+ (their_entry != NULL && (entries[2] = index_entry_dup(their_entry)) == NULL))
+ return -1;
+
+ for (i = 0; i < 3; i++) {
+ if (entries[i] == NULL)
+ continue;
+
+ /* Make sure stage is correct */
+ entries[i]->flags = (entries[i]->flags & ~GIT_IDXENTRY_STAGEMASK) |
+ ((i+1) << GIT_IDXENTRY_STAGESHIFT);
+
+ if ((ret = index_insert(index, entries[i], 1)) < 0)
+ goto on_error;
+ }
+
+ return 0;
+
+on_error:
+ for (i = 0; i < 3; i++) {
+ if (entries[i] != NULL)
+ index_entry_free(entries[i]);
+ }
+
+ return ret;
+}
+
+int git_index_conflict_get(git_index_entry **ancestor_out,
+ git_index_entry **our_out,
+ git_index_entry **their_out,
+ git_index *index, const char *path)
+{
+ int pos, stage;
+ git_index_entry *conflict_entry;
+ int error = GIT_ENOTFOUND;
+
+ assert(ancestor_out && our_out && their_out && index && path);
+
+ *ancestor_out = NULL;
+ *our_out = NULL;
+ *their_out = NULL;
+
+ if ((pos = git_index_find(index, path)) < 0)
+ return pos;
+
+ while ((unsigned int)pos < git_index_entrycount(index)) {
+ conflict_entry = git_vector_get(&index->entries, pos);
+
+ if (index->entries_cmp_path(conflict_entry->path, path) != 0)
+ break;
+
+ stage = index_entry_stage(conflict_entry);
+
+ switch (stage) {
+ case 3:
+ *their_out = conflict_entry;
+ error = 0;
+ break;
+ case 2:
+ *our_out = conflict_entry;
+ error = 0;
+ break;
+ case 1:
+ *ancestor_out = conflict_entry;
+ error = 0;
+ break;
+ default:
+ break;
+ };
+
+ ++pos;
+ }
+
+ return error;
+}
+
+int git_index_conflict_remove(git_index *index, const char *path)
+{
+ int pos;
+ git_index_entry *conflict_entry;
+
+ assert(index && path);
+
+ if ((pos = git_index_find(index, path)) < 0)
+ return pos;
+
+ while ((unsigned int)pos < git_index_entrycount(index)) {
+ conflict_entry = git_vector_get(&index->entries, pos);
+
+ if (index->entries_cmp_path(conflict_entry->path, path) != 0)
+ break;
+
+ if (index_entry_stage(conflict_entry) == 0) {
+ pos++;
+ continue;
+ }
+
+ git_vector_remove(&index->entries, (unsigned int)pos);
+ }
+
+ return 0;
+}
+
+static int index_conflicts_match(git_vector *v, size_t idx)
{
- git_vector_uniq(&index->entries);
+ git_index_entry *entry = git_vector_get(v, idx);
+
+ if (index_entry_stage(entry) > 0)
+ return 1;
+
+ return 0;
}
-const git_index_entry_unmerged *git_index_get_unmerged_bypath(
+void git_index_conflict_cleanup(git_index *index)
+{
+ assert(index);
+ git_vector_remove_matching(&index->entries, index_conflicts_match);
+}
+
+unsigned int git_index_reuc_entrycount(git_index *index)
+{
+ assert(index);
+ return (unsigned int)index->reuc.length;
+}
+
+static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int replace)
+{
+ git_index_reuc_entry **existing = NULL;
+ int position;
+
+ assert(index && reuc && reuc->path != NULL);
+
+ if ((position = git_index_reuc_find(index, reuc->path)) >= 0)
+ existing = (git_index_reuc_entry **)&index->reuc.contents[position];
+
+ if (!replace || !existing)
+ return git_vector_insert(&index->reuc, reuc);
+
+ /* exists, replace it */
+ git__free((*existing)->path);
+ git__free(*existing);
+ *existing = reuc;
+
+ return 0;
+}
+
+int git_index_reuc_add(git_index *index, const char *path,
+ int ancestor_mode, git_oid *ancestor_oid,
+ int our_mode, git_oid *our_oid,
+ int their_mode, git_oid *their_oid)
+{
+ git_index_reuc_entry *reuc = NULL;
+ int error = 0;
+
+ assert(index && path);
+
+ if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
+ (error = index_reuc_insert(index, reuc, 1)) < 0)
+ {
+ index_entry_reuc_free(reuc);
+ return error;
+ }
+
+ return error;
+}
+
+int git_index_reuc_find(git_index *index, const char *path)
+{
+ return git_vector_bsearch2(&index->reuc, index->reuc_search, path);
+}
+
+const git_index_reuc_entry *git_index_reuc_get_bypath(
git_index *index, const char *path)
{
int pos;
assert(index && path);
- if (!index->unmerged.length)
+ if (!index->reuc.length)
return NULL;
- if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < 0)
+ git_vector_sort(&index->reuc);
+
+ if ((pos = git_index_reuc_find(index, path)) < 0)
return NULL;
- return git_vector_get(&index->unmerged, pos);
+ return git_vector_get(&index->reuc, pos);
}
-const git_index_entry_unmerged *git_index_get_unmerged_byindex(
+const git_index_reuc_entry *git_index_reuc_get_byindex(
git_index *index, size_t n)
{
assert(index);
- return git_vector_get(&index->unmerged, n);
+
+ git_vector_sort(&index->reuc);
+ return git_vector_get(&index->reuc, n);
+}
+
+int git_index_reuc_remove(git_index *index, int position)
+{
+ int error;
+ git_index_reuc_entry *reuc;
+
+ git_vector_sort(&index->reuc);
+
+ reuc = git_vector_get(&index->reuc, position);
+ error = git_vector_remove(&index->reuc, (unsigned int)position);
+
+ if (!error)
+ index_entry_reuc_free(reuc);
+
+ return error;
}
static int index_error_invalid(const char *message)
@@ -631,26 +1015,26 @@ static int index_error_invalid(const char *message)
return -1;
}
-static int read_unmerged(git_index *index, const char *buffer, size_t size)
+static int read_reuc(git_index *index, const char *buffer, size_t size)
{
const char *endptr;
size_t len;
int i;
- if (git_vector_init(&index->unmerged, 16, unmerged_cmp) < 0)
+ if (git_vector_init(&index->reuc, 16, reuc_cmp) < 0)
return -1;
while (size) {
- git_index_entry_unmerged *lost;
+ git_index_reuc_entry *lost;
len = strlen(buffer) + 1;
if (size <= len)
- return index_error_invalid("reading unmerged entries");
+ return index_error_invalid("reading reuc entries");
- lost = git__malloc(sizeof(git_index_entry_unmerged));
+ lost = git__malloc(sizeof(git_index_reuc_entry));
GITERR_CHECK_ALLOC(lost);
- if (git_vector_insert(&index->unmerged, lost) < 0)
+ if (git_vector_insert(&index->reuc, lost) < 0)
return -1;
/* read NUL-terminated pathname for entry */
@@ -667,13 +1051,13 @@ static int read_unmerged(git_index *index, const char *buffer, size_t size)
if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
!endptr || endptr == buffer || *endptr ||
(unsigned)tmp > UINT_MAX)
- return index_error_invalid("reading unmerged entry stage");
+ return index_error_invalid("reading reuc entry stage");
lost->mode[i] = tmp;
len = (endptr + 1) - buffer;
if (size <= len)
- return index_error_invalid("reading unmerged entry stage");
+ return index_error_invalid("reading reuc entry stage");
size -= len;
buffer += len;
@@ -684,7 +1068,7 @@ static int read_unmerged(git_index *index, const char *buffer, size_t size)
if (!lost->mode[i])
continue;
if (size < 20)
- return index_error_invalid("reading unmerged entry oid");
+ return index_error_invalid("reading reuc entry oid");
git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
size -= 20;
@@ -692,6 +1076,9 @@ static int read_unmerged(git_index *index, const char *buffer, size_t size)
}
}
+ /* entries are guaranteed to be sorted on-disk */
+ index->reuc.sorted = 1;
+
return 0;
}
@@ -797,7 +1184,7 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)
return 0;
} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
- if (read_unmerged(index, buffer + 8, dest.extension_size) < 0)
+ if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
return 0;
}
/* else, unsupported extension. We cannot parse this, but we can skip
@@ -996,6 +1383,69 @@ static int write_entries(git_index *index, git_filebuf *file)
return error;
}
+static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data)
+{
+ struct index_extension ondisk;
+ int error = 0;
+
+ memset(&ondisk, 0x0, sizeof(struct index_extension));
+ memcpy(&ondisk, header, 4);
+ ondisk.extension_size = htonl(header->extension_size);
+
+ if ((error = git_filebuf_write(file, &ondisk, sizeof(struct index_extension))) == 0)
+ error = git_filebuf_write(file, data->ptr, data->size);
+
+ return error;
+}
+
+static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc)
+{
+ int i;
+ int error = 0;
+
+ if ((error = git_buf_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0)
+ return error;
+
+ for (i = 0; i < 3; i++) {
+ if ((error = git_buf_printf(reuc_buf, "%o", reuc->mode[i])) < 0 ||
+ (error = git_buf_put(reuc_buf, "\0", 1)) < 0)
+ return error;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (reuc->mode[i] && (error = git_buf_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_RAWSZ)) < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int write_reuc_extension(git_index *index, git_filebuf *file)
+{
+ git_buf reuc_buf = GIT_BUF_INIT;
+ git_vector *out = &index->reuc;
+ git_index_reuc_entry *reuc;
+ struct index_extension extension;
+ unsigned int i;
+ int error = 0;
+
+ git_vector_foreach(out, i, reuc) {
+ if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0)
+ goto done;
+ }
+
+ memset(&extension, 0x0, sizeof(struct index_extension));
+ memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4);
+ extension.extension_size = reuc_buf.size;
+
+ error = write_extension(file, &extension, &reuc_buf);
+
+ git_buf_free(&reuc_buf);
+
+done:
+ return error;
+}
+
static int write_index(git_index *index, git_filebuf *file)
{
git_oid hash_final;
@@ -1018,7 +1468,11 @@ static int write_index(git_index *index, git_filebuf *file)
if (write_entries(index, file) < 0)
return -1;
- /* TODO: write extensions (tree cache) */
+ /* TODO: write tree cache extension */
+
+ /* write the reuc extension */
+ if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
+ return -1;
/* get out the hash for all the contents we've appended to the file */
git_filebuf_hash(&hash_final, file);
@@ -1029,22 +1483,20 @@ static int write_index(git_index *index, git_filebuf *file)
int git_index_entry_stage(const git_index_entry *entry)
{
- return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
+ return index_entry_stage(entry);
}
typedef struct read_tree_data {
git_index *index;
- git_indexer_stats *stats;
+ git_transfer_progress *stats;
} read_tree_data;
static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *data)
{
- read_tree_data *rtd = data;
+ git_index *index = (git_index *)data;
git_index_entry *entry = NULL;
git_buf path = GIT_BUF_INIT;
- rtd->stats->total++;
-
if (git_tree_entry__is_tree(tentry))
return 0;
@@ -1059,7 +1511,7 @@ static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *da
entry->path = git_buf_detach(&path);
git_buf_free(&path);
- if (index_insert(rtd->index, entry, 0) < 0) {
+ if (index_insert(index, entry, 0) < 0) {
index_entry_free(entry);
return -1;
}
@@ -1067,16 +1519,14 @@ static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *da
return 0;
}
-int git_index_read_tree(git_index *index, git_tree *tree, git_indexer_stats *stats)
+int git_index_read_tree(git_index *index, git_tree *tree)
{
- git_indexer_stats dummy_stats;
- read_tree_data rtd = {index, NULL};
-
- if (!stats) stats = &dummy_stats;
- stats->total = 0;
- rtd.stats = stats;
-
git_index_clear(index);
- return git_tree_walk(tree, read_tree_cb, GIT_TREEWALK_POST, &rtd);
+ return git_tree_walk(tree, read_tree_cb, GIT_TREEWALK_POST, index);
+}
+
+git_repository *git_index_owner(const git_index *index)
+{
+ return INDEX_OWNER(index);
}
diff --git a/src/index.h b/src/index.h
index 7dd23ee60..0fd59dd45 100644
--- a/src/index.h
+++ b/src/index.h
@@ -33,9 +33,12 @@ struct git_index {
git_tree_cache *tree;
- git_vector unmerged;
+ git_vector reuc;
+ git_vector_cmp entries_cmp_path;
git_vector_cmp entries_search;
+ git_vector_cmp entries_search_path;
+ git_vector_cmp reuc_search;
};
extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
diff --git a/src/indexer.c b/src/indexer.c
index 7d4e18d7a..ec4ef7147 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -49,6 +49,8 @@ struct git_indexer_stream {
git_vector deltas;
unsigned int fanout[256];
git_oid hash;
+ git_transfer_progress_callback progress_cb;
+ void *progress_payload;
};
struct delta_info {
@@ -138,7 +140,11 @@ static int cache_cmp(const void *a, const void *b)
return git_oid_cmp(&ea->sha1, &eb->sha1);
}
-int git_indexer_stream_new(git_indexer_stream **out, const char *prefix)
+int git_indexer_stream_new(
+ git_indexer_stream **out,
+ const char *prefix,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload)
{
git_indexer_stream *idx;
git_buf path = GIT_BUF_INIT;
@@ -147,6 +153,8 @@ int git_indexer_stream_new(git_indexer_stream **out, const char *prefix)
idx = git__calloc(1, sizeof(git_indexer_stream));
GITERR_CHECK_ALLOC(idx);
+ idx->progress_cb = progress_cb;
+ idx->progress_payload = progress_payload;
error = git_buf_joinpath(&path, prefix, suff);
if (error < 0)
@@ -273,7 +281,13 @@ on_error:
return -1;
}
-int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats)
+static void do_progress_callback(git_indexer_stream *idx, git_transfer_progress *stats)
+{
+ if (!idx->progress_cb) return;
+ idx->progress_cb(stats, idx->progress_payload);
+}
+
+int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
{
int error;
struct git_pack_header hdr;
@@ -282,7 +296,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
assert(idx && data && stats);
- processed = stats->processed;
+ processed = stats->indexed_objects;
if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1;
@@ -324,8 +338,10 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)
return -1;
- memset(stats, 0, sizeof(git_indexer_stats));
- stats->total = (unsigned int)idx->nr_objects;
+ stats->received_objects = 0;
+ stats->indexed_objects = 0;
+ stats->total_objects = (unsigned int)idx->nr_objects;
+ do_progress_callback(idx, stats);
}
/* Now that we have data in the pack, let's try to parse it */
@@ -361,7 +377,8 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (error < 0)
return error;
- stats->received++;
+ stats->received_objects++;
+ do_progress_callback(idx, stats);
continue;
}
@@ -379,8 +396,9 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
git__free(obj.data);
- stats->processed = (unsigned int)++processed;
- stats->received++;
+ stats->indexed_objects = (unsigned int)++processed;
+ stats->received_objects++;
+ do_progress_callback(idx, stats);
}
return 0;
@@ -412,7 +430,7 @@ static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char
return git_buf_oom(path) ? -1 : 0;
}
-static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)
+static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
{
unsigned int i;
struct delta_info *delta;
@@ -428,13 +446,14 @@ static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)
return -1;
git__free(obj.data);
- stats->processed++;
+ stats->indexed_objects++;
+ do_progress_callback(idx, stats);
}
return 0;
}
-int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats)
+int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats)
{
git_mwindow *w = NULL;
unsigned int i, long_offsets = 0, left;
@@ -455,7 +474,7 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stat
if (resolve_deltas(idx, stats) < 0)
return -1;
- if (stats->processed != stats->total) {
+ if (stats->indexed_objects != stats->total_objects) {
giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
return -1;
}
@@ -782,7 +801,7 @@ cleanup:
return error;
}
-int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
+int git_indexer_run(git_indexer *idx, git_transfer_progress *stats)
{
git_mwindow_file *mwf;
git_off_t off = sizeof(struct git_pack_header);
@@ -797,8 +816,8 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
if (error < 0)
return error;
- stats->total = (unsigned int)idx->nr_objects;
- stats->processed = processed = 0;
+ stats->total_objects = (unsigned int)idx->nr_objects;
+ stats->indexed_objects = processed = 0;
while (processed < idx->nr_objects) {
git_rawobj obj;
@@ -868,7 +887,7 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
git__free(obj.data);
- stats->processed = ++processed;
+ stats->indexed_objects = ++processed;
}
cleanup:
diff --git a/src/iterator.c b/src/iterator.c
index df6da9a87..5fac41046 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -336,7 +336,7 @@ static int index_iterator__current(
git_iterator *self, const git_index_entry **entry)
{
index_iterator *ii = (index_iterator *)self;
- git_index_entry *ie = git_index_get(ii->index, ii->current);
+ git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
if (ie != NULL &&
ii->base.end != NULL &&
diff --git a/src/merge.c b/src/merge.c
new file mode 100644
index 000000000..135af6a8c
--- /dev/null
+++ b/src/merge.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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 "repository.h"
+#include "buffer.h"
+#include "merge.h"
+#include "refs.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/reset.h"
+
+int git_merge__cleanup(git_repository *repo)
+{
+ int error = 0;
+ git_buf merge_head_path = GIT_BUF_INIT,
+ merge_mode_path = GIT_BUF_INIT,
+ merge_msg_path = GIT_BUF_INIT;
+
+ assert(repo);
+
+ if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 ||
+ git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 ||
+ git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0)
+ return -1;
+
+ if (git_path_isfile(merge_head_path.ptr)) {
+ if ((error = p_unlink(merge_head_path.ptr)) < 0)
+ goto cleanup;
+ }
+
+ if (git_path_isfile(merge_mode_path.ptr))
+ (void)p_unlink(merge_mode_path.ptr);
+
+ if (git_path_isfile(merge_msg_path.ptr))
+ (void)p_unlink(merge_msg_path.ptr);
+
+cleanup:
+ git_buf_free(&merge_msg_path);
+ git_buf_free(&merge_mode_path);
+ git_buf_free(&merge_head_path);
+
+ return error;
+}
+
diff --git a/src/merge.h b/src/merge.h
new file mode 100644
index 000000000..2117d9214
--- /dev/null
+++ b/src/merge.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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_merge_h__
+#define INCLUDE_merge_h__
+
+#include "git2/types.h"
+
+#define GIT_MERGE_MSG_FILE "MERGE_MSG"
+#define GIT_MERGE_MODE_FILE "MERGE_MODE"
+
+#define MERGE_CONFIG_FILE_MODE 0666
+
+int git_merge__cleanup(git_repository *repo);
+
+#endif
diff --git a/src/netops.c b/src/netops.c
index df502e619..d9663e63c 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -117,6 +117,7 @@ static int gitno__recv_ssl(gitno_buffer *buf)
}
buf->offset += ret;
+ if (buf->packetsize_cb) buf->packetsize_cb(ret, buf->packetsize_payload);
return ret;
}
#endif
@@ -132,6 +133,7 @@ int gitno__recv(gitno_buffer *buf)
}
buf->offset += ret;
+ if (buf->packetsize_cb) buf->packetsize_cb(ret, buf->packetsize_payload);
return ret;
}
@@ -142,7 +144,6 @@ void gitno_buffer_setup_callback(
size_t len,
int (*recv)(gitno_buffer *buf), void *cb_data)
{
- memset(buf, 0x0, sizeof(gitno_buffer));
memset(data, 0x0, len);
buf->data = data;
buf->len = len;
diff --git a/src/netops.h b/src/netops.h
index 7c53fd0dc..64da7fba9 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -20,6 +20,8 @@ struct gitno_buffer {
#endif
int (*recv)(gitno_buffer *buffer);
void *cb_data;
+ void (*packetsize_cb)(int received, void *payload);
+ void *packetsize_payload;
};
void gitno_buffer_setup(git_transport *t, gitno_buffer *buf, char *data, size_t len);
diff --git a/src/reflog.c b/src/reflog.c
index a1ea7a27d..5d1465eca 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -166,6 +166,9 @@ void git_reflog_free(git_reflog *reflog)
unsigned int i;
git_reflog_entry *entry;
+ if (reflog == NULL)
+ return;
+
for (i=0; i < reflog->entries.length; i++) {
entry = git_vector_get(&reflog->entries, i);
@@ -185,7 +188,10 @@ static int retrieve_reflog_path(git_buf *path, git_reference *ref)
static int create_new_reflog_file(const char *filepath)
{
- int fd;
+ int fd, error;
+
+ if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+ return error;
if ((fd = p_open(filepath,
O_WRONLY | O_CREAT | O_TRUNC,
@@ -463,26 +469,26 @@ int git_reflog_drop(
if (!rewrite_previous_entry)
return 0;
- /* No need to rewrite anything when removing the first entry */
- if (idx == 0)
+ /* No need to rewrite anything when removing the most recent entry */
+ if (idx == entrycount - 1)
return 0;
/* There are no more entries in the log */
if (entrycount == 1)
return 0;
- entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1);
+ entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
- /* If the last entry has just been removed... */
- if (idx == entrycount - 1) {
- /* ...clear the oid_old member of the "new" last entry */
+ /* If the oldest entry has just been removed... */
+ if (idx == 0) {
+ /* ...clear the oid_old member of the "new" oldest entry */
if (git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO) < 0)
return -1;
return 0;
}
- previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
+ previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1);
git_oid_cpy(&entry->oid_old, &previous->oid_cur);
return 0;
diff --git a/src/refs.h b/src/refs.h
index 54359f07b..a58bebd0d 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -28,10 +28,22 @@
#define GIT_PACKEDREFS_FILE_MODE 0666
#define GIT_HEAD_FILE "HEAD"
+#define GIT_ORIG_HEAD_FILE "ORIG_HEAD"
#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
+#define GIT_REVERT_HEAD_FILE "REVERT_HEAD"
+#define GIT_CHERRY_PICK_HEAD_FILE "CHERRY_PICK_HEAD"
+#define GIT_BISECT_LOG_FILE "BISECT_LOG"
+#define GIT_REBASE_MERGE_DIR "rebase-merge/"
+#define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive"
+#define GIT_REBASE_APPLY_DIR "rebase-apply/"
+#define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing"
+#define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying"
#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
+#define GIT_STASH_FILE "stash"
+#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
+
#define GIT_REFNAME_MAX 1024
struct git_reference {
diff --git a/src/refspec.c b/src/refspec.c
index b1790b32c..8b69e9d8e 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -225,3 +225,14 @@ int git_refspec_transform_l(git_buf *out, const git_refspec *spec, const char *n
return refspec_transform(out, spec->dst, spec->src, name);
}
+int git_refspec__serialize(git_buf *out, const git_refspec *refspec)
+{
+ if (refspec->force)
+ git_buf_putc(out, '+');
+
+ git_buf_printf(out, "%s:%s",
+ refspec->src != NULL ? refspec->src : "",
+ refspec->dst != NULL ? refspec->dst : "");
+
+ return git_buf_oom(out) == false;
+}
diff --git a/src/refspec.h b/src/refspec.h
index 6e0596a55..40da16afc 100644
--- a/src/refspec.h
+++ b/src/refspec.h
@@ -51,4 +51,6 @@ int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *n
*/
int git_refspec_transform_l(git_buf *out, const git_refspec *spec, const char *name);
+int git_refspec__serialize(git_buf *out, const git_refspec *refspec);
+
#endif
diff --git a/src/remote.c b/src/remote.c
index e05ea059f..d3a2c4564 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -201,12 +201,75 @@ cleanup:
return error;
}
+static int ensure_remote_name_is_valid(const char *name)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_refspec refspec;
+ int error = -1;
+
+ if (!name || *name == '\0')
+ goto cleanup;
+
+ git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", name);
+ error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);
+
+ git_buf_free(&buf);
+ git_refspec__free(&refspec);
+
+cleanup:
+ if (error)
+ giterr_set(
+ GITERR_CONFIG,
+ "'%s' is not a valid remote name.", name);
+
+ return error;
+}
+
+static int update_config_refspec(
+ git_config *config,
+ const char *remote_name,
+ const git_refspec *refspec,
+ int git_direction)
+{
+ git_buf name = GIT_BUF_INIT, value = GIT_BUF_INIT;
+ int error = -1;
+
+ if (refspec->src == NULL || refspec->dst == NULL)
+ return 0;
+
+ if (git_buf_printf(
+ &name,
+ "remote.%s.%s",
+ remote_name,
+ git_direction == GIT_DIR_FETCH ? "fetch" : "push") < 0)
+ goto cleanup;
+
+ if (git_refspec__serialize(&value, refspec) < 0)
+ goto cleanup;
+
+ error = git_config_set_string(
+ config,
+ git_buf_cstr(&name),
+ git_buf_cstr(&value));
+
+cleanup:
+ git_buf_free(&name);
+ git_buf_free(&value);
+
+ return error;
+}
+
int git_remote_save(const git_remote *remote)
{
int error;
git_config *config;
const char *tagopt = NULL;
- git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ assert(remote);
+
+ if (ensure_remote_name_is_valid(remote->name) < 0)
+ return -1;
if (git_repository_config__weakptr(&config, remote->repo) < 0)
return -1;
@@ -239,33 +302,19 @@ int git_remote_save(const git_remote *remote)
}
}
- if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {
- git_buf_clear(&buf);
- git_buf_clear(&value);
- git_buf_printf(&buf, "remote.%s.fetch", remote->name);
- if (remote->fetch.force)
- git_buf_putc(&value, '+');
- git_buf_printf(&value, "%s:%s", remote->fetch.src, remote->fetch.dst);
- if (git_buf_oom(&buf) || git_buf_oom(&value))
- return -1;
-
- if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
+ if (update_config_refspec(
+ config,
+ remote->name,
+ &remote->fetch,
+ GIT_DIR_FETCH) < 0)
goto on_error;
- }
-
- if (remote->push.src != NULL && remote->push.dst != NULL) {
- git_buf_clear(&buf);
- git_buf_clear(&value);
- git_buf_printf(&buf, "remote.%s.push", remote->name);
- if (remote->push.force)
- git_buf_putc(&value, '+');
- git_buf_printf(&value, "%s:%s", remote->push.src, remote->push.dst);
- if (git_buf_oom(&buf) || git_buf_oom(&value))
- return -1;
- if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
+ if (update_config_refspec(
+ config,
+ remote->name,
+ &remote->push,
+ GIT_DIR_PUSH) < 0)
goto on_error;
- }
/*
* What action to take depends on the old and new values. This
@@ -300,13 +349,11 @@ int git_remote_save(const git_remote *remote)
}
git_buf_free(&buf);
- git_buf_free(&value);
return 0;
on_error:
git_buf_free(&buf);
- git_buf_free(&value);
return -1;
}
@@ -472,16 +519,19 @@ int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
return 0;
}
-int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
+int git_remote_download(
+ git_remote *remote,
+ git_transfer_progress_callback progress_cb,
+ void *progress_payload)
{
int error;
- assert(remote && bytes && stats);
+ assert(remote);
if ((error = git_fetch_negotiate(remote)) < 0)
return error;
- return git_fetch_download_pack(remote, bytes, stats);
+ return git_fetch_download_pack(remote, progress_cb, progress_payload);
}
int git_remote_update_tips(git_remote *remote)
@@ -742,6 +792,12 @@ void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callback
}
}
+const git_transfer_progress* git_remote_stats(git_remote *remote)
+{
+ assert(remote);
+ return &remote->stats;
+}
+
int git_remote_autotag(git_remote *remote)
{
return remote->download_tags;
@@ -751,3 +807,288 @@ void git_remote_set_autotag(git_remote *remote, int value)
{
remote->download_tags = value;
}
+
+static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
+{
+ int error;
+ git_remote *remote;
+
+ error = git_remote_load(&remote, repo, name);
+
+ if (error == GIT_ENOTFOUND)
+ return 0;
+
+ if (error < 0)
+ return error;
+
+ git_remote_free(remote);
+
+ giterr_set(
+ GITERR_CONFIG,
+ "Remote '%s' already exists.", name);
+
+ return GIT_EEXISTS;
+}
+
+static int rename_remote_config_section(
+ git_repository *repo,
+ const char *old_name,
+ const char *new_name)
+{
+ git_buf old_section_name = GIT_BUF_INIT,
+ new_section_name = GIT_BUF_INIT;
+ int error = -1;
+
+ if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
+ goto cleanup;
+
+ if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
+ goto cleanup;
+
+ error = git_config_rename_section(
+ repo,
+ git_buf_cstr(&old_section_name),
+ git_buf_cstr(&new_section_name));
+
+cleanup:
+ git_buf_free(&old_section_name);
+ git_buf_free(&new_section_name);
+
+ return error;
+}
+
+struct update_data
+{
+ git_config *config;
+ const char *old_remote_name;
+ const char *new_remote_name;
+};
+
+static int update_config_entries_cb(
+ const git_config_entry *entry,
+ void *payload)
+{
+ struct update_data *data = (struct update_data *)payload;
+
+ if (strcmp(entry->value, data->old_remote_name))
+ return 0;
+
+ return git_config_set_string(
+ data->config,
+ entry->name,
+ data->new_remote_name);
+}
+
+static int update_branch_remote_config_entry(
+ git_repository *repo,
+ const char *old_name,
+ const char *new_name)
+{
+ git_config *config;
+ struct update_data data;
+
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
+
+ data.config = config;
+ data.old_remote_name = old_name;
+ data.new_remote_name = new_name;
+
+ return git_config_foreach_match(
+ config,
+ "branch\\..+\\.remote",
+ update_config_entries_cb, &data);
+}
+
+static int rename_cb(const char *ref, void *data)
+{
+ if (git__prefixcmp(ref, GIT_REFS_REMOTES_DIR))
+ return 0;
+
+ return git_vector_insert((git_vector *)data, git__strdup(ref));
+}
+
+static int rename_one_remote_reference(
+ git_repository *repo,
+ const char *reference_name,
+ const char *old_remote_name,
+ const char *new_remote_name)
+{
+ int error = -1;
+ git_buf new_name = GIT_BUF_INIT;
+ git_reference *reference = NULL;
+
+ if (git_buf_printf(
+ &new_name,
+ GIT_REFS_REMOTES_DIR "%s%s",
+ new_remote_name,
+ reference_name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0)
+ return -1;
+
+ if (git_reference_lookup(&reference, repo, reference_name) < 0)
+ goto cleanup;
+
+ error = git_reference_rename(reference, git_buf_cstr(&new_name), 0);
+
+cleanup:
+ git_reference_free(reference);
+ git_buf_free(&new_name);
+ return error;
+}
+
+static int rename_remote_references(
+ git_repository *repo,
+ const char *old_name,
+ const char *new_name)
+{
+ git_vector refnames;
+ int error = -1;
+ unsigned int i;
+ char *name;
+
+ if (git_vector_init(&refnames, 8, NULL) < 0)
+ goto cleanup;
+
+ if (git_reference_foreach(
+ repo,
+ GIT_REF_LISTALL,
+ rename_cb,
+ &refnames) < 0)
+ goto cleanup;
+
+ git_vector_foreach(&refnames, i, name) {
+ if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0)
+ goto cleanup;
+ }
+
+ error = 0;
+cleanup:
+ git_vector_foreach(&refnames, i, name) {
+ git__free(name);
+ }
+
+ git_vector_free(&refnames);
+ return error;
+}
+
+static int rename_fetch_refspecs(
+ git_remote *remote,
+ const char *new_name,
+ int (*callback)(const char *problematic_refspec, void *payload),
+ void *payload)
+{
+ git_config *config;
+ const git_refspec *fetch_refspec;
+ git_buf dst_prefix = GIT_BUF_INIT, serialized = GIT_BUF_INIT;
+ const char* pos;
+ int error = -1;
+
+ fetch_refspec = git_remote_fetchspec(remote);
+
+ /* Is there a refspec to deal with? */
+ if (fetch_refspec->src == NULL &&
+ fetch_refspec->dst == NULL)
+ return 0;
+
+ if (git_refspec__serialize(&serialized, fetch_refspec) < 0)
+ goto cleanup;
+
+ /* Is it an in-memory remote? */
+ if (remote->name == '\0') {
+ error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0;
+ goto cleanup;
+ }
+
+ if (git_buf_printf(&dst_prefix, ":refs/remotes/%s/", remote->name) < 0)
+ goto cleanup;
+
+ pos = strstr(git_buf_cstr(&serialized), git_buf_cstr(&dst_prefix));
+
+ /* Does the dst part of the refspec follow the extected standard format? */
+ if (!pos) {
+ error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0;
+ goto cleanup;
+ }
+
+ if (git_buf_splice(
+ &serialized,
+ pos - git_buf_cstr(&serialized) + strlen(":refs/remotes/"),
+ strlen(remote->name), new_name,
+ strlen(new_name)) < 0)
+ goto cleanup;
+
+ git_refspec__free(&remote->fetch);
+
+ if (git_refspec__parse(&remote->fetch, git_buf_cstr(&serialized), true) < 0)
+ goto cleanup;
+
+ if (git_repository_config__weakptr(&config, remote->repo) < 0)
+ goto cleanup;
+
+ error = update_config_refspec(config, new_name, &remote->fetch, GIT_DIR_FETCH);
+
+cleanup:
+ git_buf_free(&serialized);
+ git_buf_free(&dst_prefix);
+ return error;
+}
+
+int git_remote_rename(
+ git_remote *remote,
+ const char *new_name,
+ int (*callback)(const char *problematic_refspec, void *payload),
+ void *payload)
+{
+ int error;
+
+ assert(remote && new_name);
+
+ if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
+ return error;
+
+ if ((error = ensure_remote_name_is_valid(new_name)) < 0)
+ return error;
+
+ if (!remote->name) {
+ if ((error = rename_fetch_refspecs(
+ remote,
+ new_name,
+ callback,
+ payload)) < 0)
+ return error;
+
+ remote->name = git__strdup(new_name);
+
+ return git_remote_save(remote);
+ }
+
+ if ((error = rename_remote_config_section(
+ remote->repo,
+ remote->name,
+ new_name)) < 0)
+ return error;
+
+ if ((error = update_branch_remote_config_entry(
+ remote->repo,
+ remote->name,
+ new_name)) < 0)
+ return error;
+
+ if ((error = rename_remote_references(
+ remote->repo,
+ remote->name,
+ new_name)) < 0)
+ return error;
+
+ if ((error = rename_fetch_refspecs(
+ remote,
+ new_name,
+ callback,
+ payload)) < 0)
+ return error;
+
+ git__free(remote->name);
+ remote->name = git__strdup(new_name);
+
+ return 0;
+}
diff --git a/src/remote.h b/src/remote.h
index 05073db8c..1b382e1bb 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -25,6 +25,7 @@ struct git_remote {
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
+ git_transfer_progress stats;
unsigned int need_pack:1,
download_tags:2, /* There are four possible values */
check_cert:1;
diff --git a/src/repository.c b/src/repository.c
index 43e0eda8f..0e416e0b8 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -20,6 +20,7 @@
#include "filter.h"
#include "odb.h"
#include "remote.h"
+#include "merge.h"
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
@@ -1348,15 +1349,13 @@ int git_repository_head_tree(git_tree **tree, git_repository *repo)
return 0;
}
-#define MERGE_MSG_FILE "MERGE_MSG"
-
int git_repository_message(char *buffer, size_t len, git_repository *repo)
{
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
struct stat st;
int error;
- if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0)
+ if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
return -1;
if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
@@ -1382,7 +1381,7 @@ int git_repository_message_remove(git_repository *repo)
git_buf path = GIT_BUF_INIT;
int error;
- if (git_buf_joinpath(&path, repo->path_repository, MERGE_MSG_FILE) < 0)
+ if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
return -1;
error = p_unlink(git_buf_cstr(&path));
@@ -1541,3 +1540,43 @@ cleanup:
git_reference_free(new_head);
return error;
}
+
+/**
+ * Loosely ported from git.git
+ * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
+ */
+int git_repository_state(git_repository *repo)
+{
+ git_buf repo_path = GIT_BUF_INIT;
+ int state = GIT_REPOSITORY_STATE_NONE;
+
+ assert(repo);
+
+ if (!git_repository_head_detached(repo))
+ return state;
+
+ if (git_buf_puts(&repo_path, repo->path_repository) < 0)
+ return -1;
+
+ if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE))
+ state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE;
+ else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR))
+ state = GIT_REPOSITORY_STATE_REBASE_MERGE;
+ else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE))
+ state = GIT_REPOSITORY_STATE_REBASE;
+ else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE))
+ state = GIT_REPOSITORY_STATE_APPLY_MAILBOX;
+ else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR))
+ state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE;
+ else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE))
+ state = GIT_REPOSITORY_STATE_MERGE;
+ else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE))
+ state = GIT_REPOSITORY_STATE_REVERT;
+ else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE))
+ state = GIT_REPOSITORY_STATE_CHERRY_PICK;
+ else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
+ state = GIT_REPOSITORY_STATE_BISECT;
+
+ git_buf_free(&repo_path);
+ return state;
+}
diff --git a/src/reset.c b/src/reset.c
index 560ae17b1..7df1c1a57 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -8,8 +8,10 @@
#include "common.h"
#include "commit.h"
#include "tag.h"
+#include "merge.h"
#include "git2/reset.h"
#include "git2/checkout.h"
+#include "git2/merge.h"
#define ERROR_MSG "Cannot perform reset"
@@ -88,6 +90,12 @@ int git_reset(
goto cleanup;
}
+ if (reset_type == GIT_RESET_SOFT && (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE)) {
+ giterr_set(GITERR_OBJECT, "%s (soft) while in the middle of a merge.", ERROR_MSG);
+ error = GIT_EUNMERGED;
+ goto cleanup;
+ }
+
//TODO: Check for unmerged entries
if (update_head(repo, commit) < 0)
@@ -108,7 +116,7 @@ int git_reset(
goto cleanup;
}
- if (git_index_read_tree(index, tree, NULL) < 0) {
+ if (git_index_read_tree(index, tree) < 0) {
giterr_set(GITERR_INDEX, "%s - Failed to update the index.", ERROR_MSG);
goto cleanup;
}
@@ -118,6 +126,11 @@ int git_reset(
goto cleanup;
}
+ if ((error = git_merge__cleanup(repo)) < 0) {
+ giterr_set(GITERR_INDEX, "%s - Failed to clean up merge data.", ERROR_MSG);
+ goto cleanup;
+ }
+
if (reset_type == GIT_RESET_MIXED) {
error = 0;
goto cleanup;
@@ -129,7 +142,7 @@ int git_reset(
| GIT_CHECKOUT_OVERWRITE_MODIFIED
| GIT_CHECKOUT_REMOVE_UNTRACKED;
- if (git_checkout_index(repo, &opts, NULL) < 0) {
+ if (git_checkout_index(repo, &opts) < 0) {
giterr_set(GITERR_INDEX, "%s - Failed to checkout the index.", ERROR_MSG);
goto cleanup;
}
diff --git a/src/stash.c b/src/stash.c
new file mode 100644
index 000000000..9c9c5dce7
--- /dev/null
+++ b/src/stash.c
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * 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 "commit.h"
+#include "tree.h"
+#include "reflog.h"
+#include "git2/diff.h"
+#include "git2/stash.h"
+#include "git2/status.h"
+#include "git2/checkout.h"
+
+static int create_error(int error, const char *msg)
+{
+ giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg);
+ return error;
+}
+
+static int ensure_non_bare_repository(git_repository *repo)
+{
+ if (!git_repository_is_bare(repo))
+ return 0;
+
+ return create_error(GIT_EBAREREPO,
+ "Stash related operations require a working directory.");
+}
+
+static int retrieve_head(git_reference **out, git_repository *repo)
+{
+ int error = git_repository_head(out, repo);
+
+ if (error == GIT_EORPHANEDHEAD)
+ return create_error(error, "You do not have the initial commit yet.");
+
+ return error;
+}
+
+static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit)
+{
+ char *formatted_oid;
+
+ formatted_oid = git_oid_allocfmt(b_commit);
+ GITERR_CHECK_ALLOC(formatted_oid);
+
+ git_buf_put(out, formatted_oid, 7);
+ git__free(formatted_oid);
+
+ return git_buf_oom(out) ? -1 : 0;
+}
+
+static int append_commit_description(git_buf *out, git_commit* commit)
+{
+ const char *message;
+ int pos = 0, len;
+
+ if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
+ return -1;
+
+ message = git_commit_message(commit);
+ len = strlen(message);
+
+ /* TODO: Replace with proper commit short message
+ * when git_commit_message_short() is implemented.
+ */
+ while (pos < len && message[pos] != '\n')
+ pos++;
+
+ git_buf_putc(out, ' ');
+ git_buf_put(out, message, pos);
+ git_buf_putc(out, '\n');
+
+ return git_buf_oom(out) ? -1 : 0;
+}
+
+static int retrieve_base_commit_and_message(
+ git_commit **b_commit,
+ git_buf *stash_message,
+ git_repository *repo)
+{
+ git_reference *head = NULL;
+ int error;
+
+ if ((error = retrieve_head(&head, repo)) < 0)
+ return error;
+
+ error = -1;
+
+ if (strcmp("HEAD", git_reference_name(head)) == 0)
+ git_buf_puts(stash_message, "(no branch): ");
+ else
+ git_buf_printf(
+ stash_message,
+ "%s: ",
+ git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR));
+
+ if (git_commit_lookup(b_commit, repo, git_reference_oid(head)) < 0)
+ goto cleanup;
+
+ if (append_commit_description(stash_message, *b_commit) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_reference_free(head);
+ return error;
+}
+
+static int build_tree_from_index(git_tree **out, git_index *index)
+{
+ git_oid i_tree_oid;
+
+ if (git_tree_create_fromindex(&i_tree_oid, index) < 0)
+ return -1;
+
+ return git_tree_lookup(out, git_index_owner(index), &i_tree_oid);
+}
+
+static int commit_index(
+ git_commit **i_commit,
+ git_index *index,
+ git_signature *stasher,
+ const char *message,
+ const git_commit *parent)
+{
+ git_tree *i_tree = NULL;
+ git_oid i_commit_oid;
+ git_buf msg = GIT_BUF_INIT;
+ int error = -1;
+
+ if (build_tree_from_index(&i_tree, index) < 0)
+ goto cleanup;
+
+ if (git_buf_printf(&msg, "index on %s\n", message) < 0)
+ goto cleanup;
+
+ if (git_commit_create(
+ &i_commit_oid,
+ git_index_owner(index),
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ git_buf_cstr(&msg),
+ i_tree,
+ 1,
+ &parent) < 0)
+ goto cleanup;
+
+ error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid);
+
+cleanup:
+ git_tree_free(i_tree);
+ git_buf_free(&msg);
+ return error;
+}
+
+struct cb_data {
+ git_index *index;
+
+ bool include_changed;
+ bool include_untracked;
+ bool include_ignored;
+};
+
+static int update_index_cb(
+ void *cb_data,
+ const git_diff_delta *delta,
+ float progress)
+{
+ int pos;
+ struct cb_data *data = (struct cb_data *)cb_data;
+
+ GIT_UNUSED(progress);
+
+ switch (delta->status) {
+ case GIT_DELTA_IGNORED:
+ if (!data->include_ignored)
+ break;
+
+ return git_index_add_from_workdir(data->index, delta->new_file.path);
+
+ case GIT_DELTA_UNTRACKED:
+ if (!data->include_untracked)
+ break;
+
+ return git_index_add_from_workdir(data->index, delta->new_file.path);
+
+ case GIT_DELTA_ADDED:
+ /* Fall through */
+ case GIT_DELTA_MODIFIED:
+ if (!data->include_changed)
+ break;
+
+ return git_index_add_from_workdir(data->index, delta->new_file.path);
+
+ case GIT_DELTA_DELETED:
+ if (!data->include_changed)
+ break;
+
+ if ((pos = git_index_find(data->index, delta->new_file.path)) < 0)
+ return -1;
+
+ if (git_index_remove(data->index, delta->new_file.path, 0) < 0)
+ return -1;
+
+ default:
+ /* Unimplemented */
+ giterr_set(
+ GITERR_INVALID,
+ "Cannot update index. Unimplemented status kind (%d)",
+ delta->status);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_untracked_tree(
+ git_tree **tree_out,
+ git_index *index,
+ git_commit *i_commit,
+ uint32_t flags)
+{
+ git_tree *i_tree = NULL;
+ git_diff_list *diff = NULL;
+ git_diff_options opts = {0};
+ struct cb_data data = {0};
+ int error = -1;
+
+ git_index_clear(index);
+
+ data.index = index;
+
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ data.include_untracked = true;
+ }
+
+ if (flags & GIT_STASH_INCLUDE_IGNORED) {
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ data.include_ignored = true;
+ }
+
+ if (git_commit_tree(&i_tree, i_commit) < 0)
+ goto cleanup;
+
+ if (git_diff_workdir_to_tree(git_index_owner(index), &opts, i_tree, &diff) < 0)
+ goto cleanup;
+
+ if (git_diff_foreach(diff, &data, update_index_cb, NULL, NULL) < 0)
+ goto cleanup;
+
+ if (build_tree_from_index(tree_out, index) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_diff_list_free(diff);
+ git_tree_free(i_tree);
+ return error;
+}
+
+static int commit_untracked(
+ git_commit **u_commit,
+ git_index *index,
+ git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ uint32_t flags)
+{
+ git_tree *u_tree = NULL;
+ git_oid u_commit_oid;
+ git_buf msg = GIT_BUF_INIT;
+ int error = -1;
+
+ if (build_untracked_tree(&u_tree, index, i_commit, flags) < 0)
+ goto cleanup;
+
+ if (git_buf_printf(&msg, "untracked files on %s\n", message) < 0)
+ goto cleanup;
+
+ if (git_commit_create(
+ &u_commit_oid,
+ git_index_owner(index),
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ git_buf_cstr(&msg),
+ u_tree,
+ 0,
+ NULL) < 0)
+ goto cleanup;
+
+ error = git_commit_lookup(u_commit, git_index_owner(index), &u_commit_oid);
+
+cleanup:
+ git_tree_free(u_tree);
+ git_buf_free(&msg);
+ return error;
+}
+
+static int build_workdir_tree(
+ git_tree **tree_out,
+ git_index *index,
+ git_commit *b_commit)
+{
+ git_tree *b_tree = NULL;
+ git_diff_list *diff = NULL, *diff2 = NULL;
+ git_diff_options opts = {0};
+ struct cb_data data = {0};
+ int error = -1;
+
+ if (git_commit_tree(&b_tree, b_commit) < 0)
+ goto cleanup;
+
+ if (git_diff_index_to_tree(git_index_owner(index), &opts, b_tree, &diff) < 0)
+ goto cleanup;
+
+ if (git_diff_workdir_to_index(git_index_owner(index), &opts, &diff2) < 0)
+ goto cleanup;
+
+ if (git_diff_merge(diff, diff2) < 0)
+ goto cleanup;
+
+ data.index = index;
+ data.include_changed = true;
+
+ if (git_diff_foreach(diff, &data, update_index_cb, NULL, NULL) < 0)
+ goto cleanup;
+
+ if (build_tree_from_index(tree_out, index) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_diff_list_free(diff);
+ git_diff_list_free(diff2);
+ git_tree_free(b_tree);
+ return error;
+}
+
+static int commit_worktree(
+ git_oid *w_commit_oid,
+ git_index *index,
+ git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit)
+{
+ git_tree *w_tree = NULL, *i_tree = NULL;
+ int error = -1;
+
+ const git_commit *parents[] = { NULL, NULL, NULL };
+
+ parents[0] = b_commit;
+ parents[1] = i_commit;
+ parents[2] = u_commit;
+
+ if (git_commit_tree(&i_tree, i_commit) < 0)
+ return -1;
+
+ if (git_index_read_tree(index, i_tree) < 0)
+ goto cleanup;
+
+ if (build_workdir_tree(&w_tree, index, b_commit) < 0)
+ goto cleanup;
+
+ if (git_commit_create(
+ w_commit_oid,
+ git_index_owner(index),
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ message,
+ w_tree,
+ u_commit ? 3 : 2, parents) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_tree_free(i_tree);
+ git_tree_free(w_tree);
+ return error;
+}
+
+static int prepare_worktree_commit_message(
+ git_buf* msg,
+ const char *user_message)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int error = -1;
+
+ git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg));
+ git_buf_clear(msg);
+
+ if (!user_message)
+ git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
+ else {
+ const char *colon;
+
+ if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
+ goto cleanup;
+
+ git_buf_puts(msg, "On ");
+ git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
+ git_buf_printf(msg, ": %s\n", user_message);
+ }
+
+ error = git_buf_oom(msg) || git_buf_oom(&buf) ? -1 : 0;
+
+cleanup:
+ git_buf_free(&buf);
+ return error;
+}
+
+static int update_reflog(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ git_signature *stasher,
+ const char *message)
+{
+ git_reference *stash = NULL;
+ git_reflog *reflog = NULL;
+ int error;
+
+ if ((error = git_reference_create_oid(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_read(&reflog, stash)) < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_write(reflog)) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_reference_free(stash);
+ git_reflog_free(reflog);
+ return error;
+}
+
+static int is_dirty_cb(const char *path, unsigned int status, void *payload)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(status);
+ GIT_UNUSED(payload);
+
+ return 1;
+}
+
+static int ensure_there_are_changes_to_stash(
+ git_repository *repo,
+ bool include_untracked_files,
+ bool include_ignored_files)
+{
+ int error;
+ git_status_options opts;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ if (include_untracked_files)
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ if (include_ignored_files)
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+ error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
+
+ if (error == GIT_EUSER)
+ return 0;
+
+ if (!error)
+ return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
+
+ return error;
+}
+
+static int reset_index_and_workdir(
+ git_repository *repo,
+ git_commit *commit,
+ bool remove_untracked)
+{
+ git_checkout_opts opts;
+
+ memset(&opts, 0, sizeof(git_checkout_opts));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_CREATE_MISSING | GIT_CHECKOUT_OVERWRITE_MODIFIED;
+
+ if (remove_untracked)
+ opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ return git_checkout_tree(repo, (git_object *)commit, &opts);
+}
+
+int git_stash_save(
+ git_oid *out,
+ git_repository *repo,
+ git_signature *stasher,
+ const char *message,
+ uint32_t flags)
+{
+ git_index *index = NULL;
+ git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
+ git_buf msg = GIT_BUF_INIT;
+ int error;
+
+ assert(out && repo && stasher);
+
+ if ((error = ensure_non_bare_repository(repo)) < 0)
+ return error;
+
+ if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
+ goto cleanup;
+
+ if ((error = ensure_there_are_changes_to_stash(
+ repo,
+ (flags & GIT_STASH_INCLUDE_UNTRACKED) == GIT_STASH_INCLUDE_UNTRACKED,
+ (flags & GIT_STASH_INCLUDE_IGNORED) == GIT_STASH_INCLUDE_IGNORED)) < 0)
+ goto cleanup;
+
+ error = -1;
+
+ if (git_repository_index(&index, repo) < 0)
+ goto cleanup;
+
+ if (commit_index(&i_commit, index, stasher, git_buf_cstr(&msg), b_commit) < 0)
+ goto cleanup;
+
+ if ((flags & GIT_STASH_INCLUDE_UNTRACKED || flags & GIT_STASH_INCLUDE_IGNORED)
+ && commit_untracked(&u_commit, index, stasher, git_buf_cstr(&msg), i_commit, flags) < 0)
+ goto cleanup;
+
+ if (prepare_worktree_commit_message(&msg, message) < 0)
+ goto cleanup;
+
+ if (commit_worktree(out, index, stasher, git_buf_cstr(&msg), i_commit, b_commit, u_commit) < 0)
+ goto cleanup;
+
+ git_buf_rtrim(&msg);
+ if (update_reflog(out, repo, stasher, git_buf_cstr(&msg)) < 0)
+ goto cleanup;
+
+ if (reset_index_and_workdir(
+ repo,
+ ((flags & GIT_STASH_KEEP_INDEX) == GIT_STASH_KEEP_INDEX) ?
+ i_commit : b_commit,
+ (flags & GIT_STASH_INCLUDE_UNTRACKED) == GIT_STASH_INCLUDE_UNTRACKED) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_buf_free(&msg);
+ git_commit_free(i_commit);
+ git_commit_free(b_commit);
+ git_commit_free(u_commit);
+ git_index_free(index);
+ return error;
+}
+
+int git_stash_foreach(
+ git_repository *repo,
+ stash_cb callback,
+ void *payload)
+{
+ git_reference *stash;
+ git_reflog *reflog = NULL;
+ int error;
+ size_t i, max;
+ const git_reflog_entry *entry;
+
+ error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
+ if (error == GIT_ENOTFOUND)
+ return 0;
+
+ if (error < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_read(&reflog, stash)) < 0)
+ goto cleanup;
+
+ max = git_reflog_entrycount(reflog);
+ for (i = 0; i < max; i++) {
+ entry = git_reflog_entry_byindex(reflog, max - i - 1);
+
+ if (callback(i,
+ git_reflog_entry_msg(entry),
+ git_reflog_entry_oidnew(entry),
+ payload)) {
+ error = GIT_EUSER;
+ goto cleanup;
+ }
+ }
+
+ error = 0;
+
+cleanup:
+ git_reference_free(stash);
+ git_reflog_free(reflog);
+ return error;
+}
+
+int git_stash_drop(
+ git_repository *repo,
+ size_t index)
+{
+ git_reference *stash;
+ git_reflog *reflog = NULL;
+ size_t max;
+ int error;
+
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ return error;
+
+ if ((error = git_reflog_read(&reflog, stash)) < 0)
+ goto cleanup;
+
+ max = git_reflog_entrycount(reflog);
+
+ if (index > max - 1) {
+ error = GIT_ENOTFOUND;
+ giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
+ goto cleanup;
+ }
+
+ if ((error = git_reflog_drop(reflog, max - index - 1, true)) < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_write(reflog)) < 0)
+ goto cleanup;
+
+ if (max == 1) {
+ error = git_reference_delete(stash);
+ stash = NULL;
+ }
+
+cleanup:
+ git_reference_free(stash);
+ git_reflog_free(reflog);
+ return error;
+}
diff --git a/src/submodule.c b/src/submodule.c
index e3657f9ad..d69559dc2 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -332,7 +332,7 @@ int git_submodule_add_finalize(git_submodule *sm)
assert(sm);
if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
- (error = git_index_add(index, GIT_MODULES_FILE, 0)) < 0)
+ (error = git_index_add_from_workdir(index, GIT_MODULES_FILE)) < 0)
return error;
return git_submodule_add_to_index(sm, true);
@@ -393,7 +393,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
git_commit_free(head);
/* add it */
- error = git_index_add2(index, &entry);
+ error = git_index_add(index, &entry);
/* write it, if requested */
if (!error && write_index) {
@@ -733,7 +733,7 @@ int git_submodule_reload(git_submodule *submodule)
pos = git_index_find(index, submodule->path);
if (pos >= 0) {
- git_index_entry *entry = git_index_get(index, pos);
+ git_index_entry *entry = git_index_get_byindex(index, pos);
if (S_ISGITLINK(entry->mode)) {
if ((error = submodule_load_from_index(repo, entry)) < 0)
diff --git a/src/transport.h b/src/transport.h
index 4c944b9e7..1a3eee57d 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -113,7 +113,7 @@ struct git_transport {
/**
* Download the packfile
*/
- int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
+ int (*download_pack)(struct git_transport *transport, git_repository *repo, git_transfer_progress *stats);
/**
* Close the connection
*/
diff --git a/src/transports/http.c b/src/transports/http.c
index 93dd0c326..8042aeba1 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -379,6 +379,8 @@ static int http_recv_cb(gitno_buffer *buf)
#ifndef GIT_WINHTTP
gitno_buffer_setup(transport, &inner, buffer, sizeof(buffer));
+ inner.packetsize_cb = buf->packetsize_cb;
+ inner.packetsize_payload = buf->packetsize_payload;
if ((error = gitno_recv(&inner)) < 0)
return -1;
@@ -401,6 +403,7 @@ static int http_recv_cb(gitno_buffer *buf)
memcpy(buf->data + buf->offset, buffer, recvd);
buf->offset += recvd;
+ if (buf->packetsize_cb) buf->packetsize_cb(recvd, buf->packetsize_payload);
#endif
return (int)(buf->offset - old_len);
diff --git a/src/tree.c b/src/tree.c
index 8d3f2665c..9ecefbb61 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -353,7 +353,7 @@ static unsigned int find_next_dir(const char *dirname, git_index *index, unsigne
dirlen = strlen(dirname);
for (i = start; i < entries; ++i) {
- git_index_entry *entry = git_index_get(index, i);
+ git_index_entry *entry = git_index_get_byindex(index, i);
if (strlen(entry->path) < dirlen ||
memcmp(entry->path, dirname, dirlen) ||
(dirlen > 0 && entry->path[dirlen] != '/')) {
@@ -415,7 +415,7 @@ static int write_tree(
* need to keep track of the current position.
*/
for (i = start; i < entries; ++i) {
- git_index_entry *entry = git_index_get(index, i);
+ git_index_entry *entry = git_index_get_byindex(index, i);
char *filename, *next_slash;
/*
diff --git a/src/vector.c b/src/vector.c
index c6a644cc3..d9c4c9900 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -223,6 +223,20 @@ void git_vector_uniq(git_vector *v)
v->length -= j - i - 1;
}
+void git_vector_remove_matching(git_vector *v, int (*match)(git_vector *v, size_t idx))
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < v->length; ++j) {
+ v->contents[i] = v->contents[j];
+
+ if (!match(v, i))
+ i++;
+ }
+
+ v->length = i;
+}
+
void git_vector_clear(git_vector *v)
{
assert(v);
diff --git a/src/vector.h b/src/vector.h
index 49ba754f0..32f790c71 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -75,5 +75,6 @@ int git_vector_insert_sorted(git_vector *v, void *element,
int git_vector_remove(git_vector *v, unsigned int idx);
void git_vector_pop(git_vector *v);
void git_vector_uniq(git_vector *v);
+void git_vector_remove_matching(git_vector *v, int (*match)(git_vector *v, size_t idx));
#endif
diff --git a/tests-clar/attr/repo.c b/tests-clar/attr/repo.c
index 4a317e4f3..b51d5e335 100644
--- a/tests-clar/attr/repo.c
+++ b/tests-clar/attr/repo.c
@@ -270,12 +270,12 @@ static void assert_proper_normalization(git_index *index, const char *filename,
git_index_entry *entry;
add_to_workdir(filename, CONTENT);
- cl_git_pass(git_index_add(index, filename, 0));
+ cl_git_pass(git_index_add_from_workdir(index, filename));
index_pos = git_index_find(index, filename);
cl_assert(index_pos >= 0);
- entry = git_index_get(index, index_pos);
+ entry = git_index_get_byindex(index, index_pos);
cl_assert_equal_i(0, git_oid_streq(&entry->oid, expected_sha));
}
diff --git a/tests-clar/buf/splice.c b/tests-clar/buf/splice.c
new file mode 100644
index 000000000..e80c93105
--- /dev/null
+++ b/tests-clar/buf/splice.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+static git_buf _buf;
+
+void test_buf_splice__initialize(void) {
+ git_buf_init(&_buf, 16);
+}
+
+void test_buf_splice__cleanup(void) {
+ git_buf_free(&_buf);
+}
+
+void test_buf_splice__preprend(void)
+{
+ git_buf_sets(&_buf, "world!");
+
+ cl_git_pass(git_buf_splice(&_buf, 0, 0, "Hello Dolly", strlen("Hello ")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__append(void)
+{
+ git_buf_sets(&_buf, "Hello");
+
+ cl_git_pass(git_buf_splice(&_buf, git_buf_len(&_buf), 0, " world!", strlen(" world!")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__insert_at(void)
+{
+ git_buf_sets(&_buf, "Hell world!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hell"), 0, "o", strlen("o")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__remove_at(void)
+{
+ git_buf_sets(&_buf, "Hello world of warcraft!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hello world"), strlen(" of warcraft"), "", 0));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__replace(void)
+{
+ git_buf_sets(&_buf, "Hell0 w0rld!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hell"), strlen("0 w0"), "o wo", strlen("o wo")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__replace_with_longer(void)
+{
+ git_buf_sets(&_buf, "Hello you!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hello "), strlen("you"), "world", strlen("world")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__replace_with_shorter(void)
+{
+ git_buf_sets(&_buf, "Brave new world!");
+
+ cl_git_pass(git_buf_splice(&_buf, 0, strlen("Brave new"), "Hello", strlen("Hello")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__truncate(void)
+{
+ git_buf_sets(&_buf, "Hello world!!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hello world!"), strlen("!"), "", 0));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__dont_do_anything(void)
+{
+ git_buf_sets(&_buf, "Hello world!");
+
+ cl_git_pass(git_buf_splice(&_buf, 3, 0, "Hello", 0));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
diff --git a/tests-clar/checkout/head.c b/tests-clar/checkout/head.c
index d36034c52..103b9999e 100644
--- a/tests-clar/checkout/head.c
+++ b/tests-clar/checkout/head.c
@@ -18,5 +18,5 @@ void test_checkout_head__checking_out_an_orphaned_head_returns_GIT_EORPHANEDHEAD
{
make_head_orphaned(g_repo, NON_EXISTING_HEAD);
- cl_assert_equal_i(GIT_EORPHANEDHEAD, git_checkout_head(g_repo, NULL, NULL));
+ cl_assert_equal_i(GIT_EORPHANEDHEAD, git_checkout_head(g_repo, NULL));
}
diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c
index f017a0fe2..89bc1da15 100644
--- a/tests-clar/checkout/index.c
+++ b/tests-clar/checkout/index.c
@@ -14,7 +14,7 @@ static void reset_index_to_treeish(git_object *treeish)
cl_git_pass(git_object_peel(&tree, treeish, GIT_OBJ_TREE));
cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read_tree(index, (git_tree *)tree, NULL));
+ cl_git_pass(git_index_read_tree(index, (git_tree *)tree));
cl_git_pass(git_index_write(index));
git_object_free(tree);
@@ -69,7 +69,7 @@ void test_checkout_index__cannot_checkout_a_bare_repository(void)
memset(&g_opts, 0, sizeof(g_opts));
g_repo = cl_git_sandbox_init("testrepo.git");
- cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
+ cl_git_fail(git_checkout_index(g_repo, NULL));
}
void test_checkout_index__can_create_missing_files(void)
@@ -79,7 +79,7 @@ void test_checkout_index__can_create_missing_files(void)
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
g_opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/README", "hey there\n");
test_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
@@ -95,7 +95,7 @@ void test_checkout_index__can_remove_untracked_files(void)
cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));
g_opts.checkout_strategy = GIT_CHECKOUT_REMOVE_UNTRACKED;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
}
@@ -111,7 +111,7 @@ void test_checkout_index__honor_the_specified_pathspecs(void)
cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
test_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
@@ -142,7 +142,7 @@ void test_checkout_index__honor_the_gitattributes_directives(void)
cl_git_mkfile("./testrepo/.gitattributes", attributes);
set_core_autocrlf_to(false);
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/README", "hey there\n");
test_file_contents("./testrepo/new.txt", "my new file\n");
@@ -157,7 +157,7 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
cl_git_pass(p_unlink("./testrepo/.gitattributes"));
set_core_autocrlf_to(true);
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/README", expected_readme_text);
#endif
@@ -172,7 +172,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
set_repo_symlink_handling_cap_to(true);
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
#ifdef GIT_WIN32
test_file_contents("./testrepo/link_to_new.txt", "new.txt");
@@ -194,7 +194,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
set_repo_symlink_handling_cap_to(false);
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/link_to_new.txt", "new.txt");
}
@@ -204,7 +204,7 @@ void test_checkout_index__donot_overwrite_modified_file_by_default(void)
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
g_opts.checkout_strategy = 0;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "This isn't what's stored!");
}
@@ -214,7 +214,7 @@ void test_checkout_index__can_overwrite_modified_file(void)
cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
g_opts.checkout_strategy = GIT_CHECKOUT_OVERWRITE_MODIFIED;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "my new file\n");
}
@@ -224,14 +224,14 @@ void test_checkout_index__options_disable_filters(void)
cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
g_opts.disable_filters = false;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "my new file\r\n");
p_unlink("./testrepo/new.txt");
g_opts.disable_filters = true;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "my new file\n");
}
@@ -249,7 +249,7 @@ void test_checkout_index__options_dir_modes(void)
reset_index_to_treeish((git_object *)commit);
g_opts.dir_mode = 0701;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_git_pass(p_stat("./testrepo/a", &st));
cl_assert_equal_i(st.st_mode & 0777, 0701);
@@ -269,7 +269,7 @@ void test_checkout_index__options_override_file_modes(void)
g_opts.file_mode = 0700;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
cl_git_pass(p_stat("./testrepo/new.txt", &st));
cl_assert_equal_i(st.st_mode & 0777, 0700);
@@ -283,7 +283,7 @@ void test_checkout_index__options_open_flags(void)
g_opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
g_opts.checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
test_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
}
@@ -328,7 +328,7 @@ void test_checkout_index__can_notify_of_skipped_files(void)
g_opts.skipped_notify_cb = notify_cb;
g_opts.notify_payload = &data;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
}
static int dont_notify_cb(
@@ -358,5 +358,22 @@ void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
g_opts.skipped_notify_cb = dont_notify_cb;
g_opts.notify_payload = NULL;
- cl_git_pass(git_checkout_index(g_repo, &g_opts, NULL));
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
+}
+
+static void progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ *was_called = true;
+}
+
+void test_checkout_index__calls_progress_callback(void)
+{
+ bool was_called = 0;
+ g_opts.progress_cb = progress;
+ g_opts.progress_payload = &was_called;
+
+ cl_git_pass(git_checkout_index(g_repo, &g_opts));
+ cl_assert_equal_i(was_called, true);
}
diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c
index 6d573bfd7..c42aefc31 100644
--- a/tests-clar/checkout/tree.c
+++ b/tests-clar/checkout/tree.c
@@ -27,7 +27,7 @@ void test_checkout_tree__cannot_checkout_a_non_treeish(void)
/* blob */
cl_git_pass(git_revparse_single(&g_object, g_repo, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
- cl_git_fail(git_checkout_tree(g_repo, g_object, NULL, NULL));
+ cl_git_fail(git_checkout_tree(g_repo, g_object, NULL));
}
void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
@@ -41,7 +41,7 @@ void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts, NULL));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
@@ -58,8 +58,26 @@ void test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)
cl_assert_equal_i(false, git_path_isdir("./testrepo/de/"));
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts, NULL));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
cl_assert_equal_i(true, git_path_isfile("./testrepo/de/2.txt"));
cl_assert_equal_i(true, git_path_isfile("./testrepo/de/fgh/1.txt"));
}
+
+static void progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ *was_called = true;
+}
+
+void test_checkout_tree__calls_progress_callback(void)
+{
+ bool was_called = 0;
+ g_opts.progress_cb = progress;
+ g_opts.progress_payload = &was_called;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_assert_equal_i(was_called, true);
+}
diff --git a/tests-clar/checkout/typechange.c b/tests-clar/checkout/typechange.c
index f013617d5..e86af52ee 100644
--- a/tests-clar/checkout/typechange.c
+++ b/tests-clar/checkout/typechange.c
@@ -49,7 +49,7 @@ void test_checkout_typechange__checkout_typechanges(void)
cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
/* fprintf(stderr, "checking out '%s'\n", g_typechange_oids[i]); */
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts, NULL));
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
git_object_free(obj);
diff --git a/tests-clar/clone/network.c b/tests-clar/clone/network.c
index 1ebdfb5d1..19385f77a 100644
--- a/tests-clar/clone/network.c
+++ b/tests-clar/clone/network.c
@@ -43,7 +43,7 @@ void test_clone_network__network_bare(void)
cl_set_cleanup(&cleanup_repository, "./test");
- cl_git_pass(git_clone_bare(&g_repo, LIVE_REPO_URL, "./test", NULL));
+ cl_git_pass(git_clone_bare(&g_repo, LIVE_REPO_URL, "./test", NULL, NULL));
cl_assert(git_repository_is_bare(g_repo));
cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
@@ -91,18 +91,36 @@ void test_clone_network__can_prevent_the_checkout_of_a_standard_repo(void)
git_buf_free(&path);
}
+static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ (*was_called) = true;
+}
+
+static void fetch_progress(const git_transfer_progress *stats, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(stats);
+ (*was_called) = true;
+}
+
void test_clone_network__can_checkout_a_cloned_repo(void)
{
- git_checkout_opts opts;
+ git_checkout_opts opts = {0};
git_buf path = GIT_BUF_INIT;
git_reference *head;
+ bool checkout_progress_cb_was_called = false,
+ fetch_progress_cb_was_called = false;
- memset(&opts, 0, sizeof(opts));
opts.checkout_strategy = GIT_CHECKOUT_CREATE_MISSING;
+ opts.progress_cb = &checkout_progress;
+ opts.progress_payload = &checkout_progress_cb_was_called;
cl_set_cleanup(&cleanup_repository, "./default-checkout");
- cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./default-checkout", NULL, NULL, &opts));
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./default-checkout",
+ &fetch_progress, &fetch_progress_cb_was_called, &opts));
cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
@@ -111,6 +129,9 @@ void test_clone_network__can_checkout_a_cloned_repo(void)
cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
cl_assert_equal_s("refs/heads/master", git_reference_target(head));
+ cl_assert_equal_i(true, checkout_progress_cb_was_called);
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
git_reference_free(head);
git_buf_free(&path);
}
diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c
index 81f95b9b3..3984f3fe7 100644
--- a/tests-clar/clone/nonetwork.c
+++ b/tests-clar/clone/nonetwork.c
@@ -65,7 +65,7 @@ void test_clone_nonetwork__bad_url(void)
/* Clone should clean up the mess if the URL isn't a git repository */
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", NULL, NULL, NULL));
cl_assert(!git_path_exists("./foo"));
- cl_git_fail(git_clone_bare(&g_repo, "not_a_repo", "./foo.git", NULL));
+ cl_git_fail(git_clone_bare(&g_repo, "not_a_repo", "./foo.git", NULL, NULL));
cl_assert(!git_path_exists("./foo.git"));
}
@@ -91,7 +91,7 @@ void test_clone_nonetwork__local_bare(void)
#if DO_LOCAL_TEST
cl_set_cleanup(&cleanup_repository, "./local.git");
- cl_git_pass(git_clone_bare(&g_repo, git_buf_cstr(&src), "./local.git", NULL));
+ cl_git_pass(git_clone_bare(&g_repo, git_buf_cstr(&src), "./local.git", NULL, NULL));
#endif
git_buf_free(&src);
diff --git a/tests-clar/config/config_helpers.c b/tests-clar/config/config_helpers.c
new file mode 100644
index 000000000..53bd945a0
--- /dev/null
+++ b/tests-clar/config/config_helpers.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+#include "config_helpers.h"
+#include "repository.h"
+
+void assert_config_entry_existence(
+ git_repository *repo,
+ const char *name,
+ bool is_supposed_to_exist)
+{
+ git_config *config;
+ const char *out;
+ int result;
+
+ cl_git_pass(git_repository_config__weakptr(&config, repo));
+
+ result = git_config_get_string(&out, config, name);
+
+ if (is_supposed_to_exist)
+ cl_git_pass(result);
+ else
+ cl_assert_equal_i(GIT_ENOTFOUND, result);
+}
+
+void assert_config_entry_value(
+ git_repository *repo,
+ const char *name,
+ const char *expected_value)
+{
+ git_config *config;
+ const char *out;
+
+ cl_git_pass(git_repository_config__weakptr(&config, repo));
+
+ cl_git_pass(git_config_get_string(&out, config, name));
+
+ cl_assert_equal_s(expected_value, out);
+}
diff --git a/tests-clar/config/config_helpers.h b/tests-clar/config/config_helpers.h
new file mode 100644
index 000000000..b887b3d38
--- /dev/null
+++ b/tests-clar/config/config_helpers.h
@@ -0,0 +1,9 @@
+extern void assert_config_entry_existence(
+ git_repository *repo,
+ const char *name,
+ bool is_supposed_to_exist);
+
+extern void assert_config_entry_value(
+ git_repository *repo,
+ const char *name,
+ const char *expected_value);
diff --git a/tests-clar/core/vector.c b/tests-clar/core/vector.c
index ef3d6c36d..b165905ae 100644
--- a/tests-clar/core/vector.c
+++ b/tests-clar/core/vector.c
@@ -189,3 +189,87 @@ void test_core_vector__5(void)
git_vector_free(&x);
}
+
+static int remove_ones(git_vector *v, size_t idx)
+{
+ return (git_vector_get(v, idx) == (void *)0x001);
+}
+
+/* Test removal based on callback */
+void test_core_vector__remove_matching(void)
+{
+ git_vector x;
+ size_t i;
+ void *compare;
+
+ git_vector_init(&x, 1, NULL);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 1);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 3);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 4);
+
+ git_vector_free(&x);
+}
diff --git a/tests-clar/index/conflicts.c b/tests-clar/index/conflicts.c
new file mode 100644
index 000000000..59df257c5
--- /dev/null
+++ b/tests-clar/index/conflicts.c
@@ -0,0 +1,217 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define CONFLICTS_ONE_ANCESTOR_OID "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81"
+#define CONFLICTS_ONE_OUR_OID "6aea5f295304c36144ad6e9247a291b7f8112399"
+#define CONFLICTS_ONE_THEIR_OID "516bd85f78061e09ccc714561d7b504672cb52da"
+
+#define CONFLICTS_TWO_ANCESTOR_OID "84af62840be1b1c47b778a8a249f3ff45155038c"
+#define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2"
+#define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e"
+
+#define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f"
+#define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b"
+#define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567"
+
+// Fixture setup and teardown
+void test_index_conflicts__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_conflicts__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_conflicts__add(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ our_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, TEST_OUR_OID);
+
+ their_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+}
+
+void test_index_conflicts__add_fixes_incorrect_stage(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ our_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, TEST_OUR_OID);
+
+ their_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_index_entry_stage(conflict_entry[0]) == 1);
+ cl_assert(git_index_entry_stage(conflict_entry[1]) == 2);
+ cl_assert(git_index_entry_stage(conflict_entry[2]) == 3);
+}
+
+void test_index_conflicts__get(void)
+{
+ git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-one.txt"));
+
+ cl_assert(strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-two.txt"));
+
+ cl_assert(strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+}
+
+void test_index_conflicts__remove(void)
+{
+ git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-one.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-two.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-two.txt") != 0);
+ }
+}
+
+void test_index_conflicts__moved_to_reuc(void)
+{
+ git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_mkfile("./mergedrepo/conflicts-one.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_from_workdir(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 6);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+ if (strcmp(entry->path, "conflicts-one.txt") == 0)
+ cl_assert(git_index_entry_stage(entry) == 0);
+ }
+}
+
+void test_index_conflicts__remove_all_conflicts(void)
+{
+ size_t i;
+ git_index_entry *entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ git_index_conflict_cleanup(repo_index);
+
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(git_index_entry_stage(entry) == 0);
+ }
+}
+
+void test_index_conflicts__partial(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL));
+ cl_assert(git_index_entrycount(repo_index) == 9);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_oid_cmp(&ancestor_entry.oid, &conflict_entry[0]->oid) == 0);
+ cl_assert(conflict_entry[1] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+}
diff --git a/tests-clar/index/filemodes.c b/tests-clar/index/filemodes.c
index 75c94e8e7..882d41748 100644
--- a/tests-clar/index/filemodes.c
+++ b/tests-clar/index/filemodes.c
@@ -25,7 +25,7 @@ void test_index_filemodes__read(void)
cl_assert_equal_i(6, git_index_entrycount(index));
for (i = 0; i < 6; ++i) {
- git_index_entry *entry = git_index_get(index, i);
+ git_index_entry *entry = git_index_get_byindex(index, i);
cl_assert(entry != NULL);
cl_assert(((entry->mode & 0100) ? 1 : 0) == expected[i]);
}
@@ -56,35 +56,15 @@ static void add_and_check_mode(
int pos;
git_index_entry *entry;
- cl_git_pass(git_index_add(index, filename, 0));
+ cl_git_pass(git_index_add_from_workdir(index, filename));
pos = git_index_find(index, filename);
cl_assert(pos >= 0);
- entry = git_index_get(index, pos);
+ entry = git_index_get_byindex(index, pos);
cl_assert(entry->mode == expect_mode);
}
-static void append_and_check_mode(
- git_index *index, const char *filename, unsigned int expect_mode)
-{
- unsigned int before, after;
- git_index_entry *entry;
-
- before = git_index_entrycount(index);
-
- cl_git_pass(git_index_append(index, filename, 0));
-
- after = git_index_entrycount(index);
- cl_assert_equal_i(before + 1, after);
-
- /* bypass git_index_get since that resorts the index */
- entry = (git_index_entry *)git_vector_get(&index->entries, after - 1);
-
- cl_assert_equal_s(entry->path, filename);
- cl_assert(expect_mode == entry->mode);
-}
-
void test_index_filemodes__untrusted(void)
{
git_config *cfg;
@@ -114,23 +94,7 @@ void test_index_filemodes__untrusted(void)
replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
- /* 5 - append 0644 over existing 0644 -> expect 0644 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.2", 0644);
- append_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
-
- /* 6 - append 0644 over existing 0755 -> expect 0755 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.2", 0644);
- append_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 7 - append 0755 over existing 0644 -> expect 0644 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.3", 0755);
- append_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
-
- /* 8 - append 0755 over existing 0755 -> expect 0755 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.3", 0755);
- append_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 9 - add new 0644 -> expect 0644 */
+ /* 5 - add new 0644 -> expect 0644 */
cl_git_write2file("filemodes/new_off", "blah",
O_WRONLY | O_CREAT | O_TRUNC, 0644);
add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
@@ -139,7 +103,7 @@ void test_index_filemodes__untrusted(void)
* that doesn't support filemodes correctly, so skip it.
*/
if (can_filemode) {
- /* 10 - add 0755 -> expect 0755 */
+ /* 6 - add 0755 -> expect 0755 */
cl_git_write2file("filemodes/new_on", "blah",
O_WRONLY | O_CREAT | O_TRUNC, 0755);
add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
@@ -182,28 +146,12 @@ void test_index_filemodes__trusted(void)
replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
- /* 5 - append 0644 over existing 0644 -> expect 0644 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.2", 0644);
- append_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
-
- /* 6 - append 0644 over existing 0755 -> expect 0644 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.2", 0644);
- append_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB);
-
- /* 7 - append 0755 over existing 0644 -> expect 0755 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.3", 0755);
- append_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 8 - append 0755 over existing 0755 -> expect 0755 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.3", 0755);
- append_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 9 - add new 0644 -> expect 0644 */
+ /* 5 - add new 0644 -> expect 0644 */
cl_git_write2file("filemodes/new_off", "blah",
O_WRONLY | O_CREAT | O_TRUNC, 0644);
add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
- /* 10 - add 0755 -> expect 0755 */
+ /* 6 - add 0755 -> expect 0755 */
cl_git_write2file("filemodes/new_on", "blah",
O_WRONLY | O_CREAT | O_TRUNC, 0755);
add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
diff --git a/tests-clar/index/read_tree.c b/tests-clar/index/read_tree.c
index 0479332dc..f63a54bf2 100644
--- a/tests-clar/index/read_tree.c
+++ b/tests-clar/index/read_tree.c
@@ -24,16 +24,16 @@ void test_index_read_tree__read_write_involution(void)
cl_git_mkfile("./read_tree/abc/d", NULL);
cl_git_mkfile("./read_tree/abc_d", NULL);
- cl_git_pass(git_index_add(index, "abc-d", 0));
- cl_git_pass(git_index_add(index, "abc_d", 0));
- cl_git_pass(git_index_add(index, "abc/d", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "abc-d"));
+ cl_git_pass(git_index_add_from_workdir(index, "abc_d"));
+ cl_git_pass(git_index_add_from_workdir(index, "abc/d"));
/* write-tree */
cl_git_pass(git_tree_create_fromindex(&expected, index));
/* read-tree */
git_tree_lookup(&tree, repo, &expected);
- cl_git_pass(git_index_read_tree(index, tree, NULL));
+ cl_git_pass(git_index_read_tree(index, tree));
git_tree_free(tree);
cl_git_pass(git_tree_create_fromindex(&tree_oid, index));
diff --git a/tests-clar/index/rename.c b/tests-clar/index/rename.c
index eecd257fd..e16ec00c1 100644
--- a/tests-clar/index/rename.c
+++ b/tests-clar/index/rename.c
@@ -19,28 +19,28 @@ void test_index_rename__single_file(void)
cl_git_mkfile("./rename/lame.name.txt", "new_file\n");
/* This should add a new blob to the object database in 'd4/fa8600b4f37d7516bef4816ae2c64dbf029e3a' */
- cl_git_pass(git_index_add(index, "lame.name.txt", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "lame.name.txt"));
cl_assert(git_index_entrycount(index) == 1);
cl_git_pass(git_oid_fromstr(&expected, "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a"));
position = git_index_find(index, "lame.name.txt");
- entry = git_index_get(index, position);
+ entry = git_index_get_byindex(index, position);
cl_assert(git_oid_cmp(&expected, &entry->oid) == 0);
/* This removes the entry from the index, but not from the object database */
- cl_git_pass(git_index_remove(index, position));
+ cl_git_pass(git_index_remove(index, "lame.name.txt", 0));
cl_assert(git_index_entrycount(index) == 0);
p_rename("./rename/lame.name.txt", "./rename/fancy.name.txt");
- cl_git_pass(git_index_add(index, "fancy.name.txt", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "fancy.name.txt"));
cl_assert(git_index_entrycount(index) == 1);
position = git_index_find(index, "fancy.name.txt");
- entry = git_index_get(index, position);
+ entry = git_index_get_byindex(index, position);
cl_assert(git_oid_cmp(&expected, &entry->oid) == 0);
git_index_free(index);
diff --git a/tests-clar/index/reuc.c b/tests-clar/index/reuc.c
new file mode 100644
index 000000000..c7c394444
--- /dev/null
+++ b/tests-clar/index/reuc.c
@@ -0,0 +1,236 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ONE_ANCESTOR_OID "478871385b9cd03908c5383acfd568bef023c6b3"
+#define ONE_OUR_OID "4458b8bc9e72b6c8755ae456f60e9844d0538d8c"
+#define ONE_THEIR_OID "8b72416545c7e761b64cecad4f1686eae4078aa8"
+
+#define TWO_ANCESTOR_OID "9d81f82fccc7dcd7de7a1ffead1815294c2e092c"
+#define TWO_OUR_OID "8f3c06cff9a83757cec40c80bc9bf31a2582bde9"
+#define TWO_THEIR_OID "887b153b165d32409c70163e0f734c090f12f673"
+
+// Fixture setup and teardown
+void test_index_reuc__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_reuc__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_reuc__read_bypath(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "two.txt"));
+
+ cl_assert(strcmp(reuc->path, "two.txt") == 0);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt"));
+
+ cl_assert(strcmp(reuc->path, "one.txt") == 0);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__ignore_case(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+ int index_caps;
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps &= ~GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert(!git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ index_caps |= GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ cl_assert(strcmp(reuc->path, "two.txt") == 0);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__read_byindex(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert(strcmp(reuc->path, "one.txt") == 0);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+
+ cl_assert(strcmp(reuc->path, "two.txt") == 0);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__updates_existing(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid ancestor_oid, our_oid, their_oid, oid;
+ int index_caps;
+
+ git_index_clear(repo_index);
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps |= GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, TWO_OUR_OID);
+ git_oid_fromstr(&their_oid, TWO_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_reuc_add(repo_index, "TWO.txt",
+ 0100644, &our_oid,
+ 0100644, &their_oid,
+ 0100644, &ancestor_oid));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert(strcmp(reuc->path, "TWO.txt") == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__remove(void)
+{
+ git_oid oid;
+ const git_index_reuc_entry *reuc;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_git_pass(git_index_reuc_remove(repo_index, 0));
+ cl_git_fail(git_index_reuc_remove(repo_index, 1));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert(strcmp(reuc->path, "two.txt") == 0);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__write(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_index_clear(repo_index);
+
+ /* Write out of order to ensure sorting is correct */
+ git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, TWO_OUR_OID);
+ git_oid_fromstr(&their_oid, TWO_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "one.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_write(repo_index));
+
+ cl_git_pass(git_index_read(repo_index));
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ /* ensure sort order was round-tripped correct */
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+ cl_assert(strcmp(reuc->path, "one.txt") == 0);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+ cl_assert(strcmp(reuc->path, "two.txt") == 0);
+}
+
diff --git a/tests-clar/index/stage.c b/tests-clar/index/stage.c
new file mode 100644
index 000000000..ba6f1b806
--- /dev/null
+++ b/tests-clar/index/stage.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+// Fixture setup and teardown
+void test_index_stage__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_stage__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_index_stage__add_always_adds_stage_0(void)
+{
+ int entry_idx;
+ git_index_entry *entry;
+
+ cl_git_mkfile("./mergedrepo/new-file.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_from_workdir(repo_index, "new-file.txt"));
+
+ cl_assert((entry_idx = git_index_find(repo_index, "new-file.txt")) >= 0);
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+}
+
+void test_index_stage__find_gets_first_stage(void)
+{
+ int entry_idx;
+ git_index_entry *entry;
+
+ cl_assert((entry_idx = git_index_find(repo_index, "one.txt")) >= 0);
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert((entry_idx = git_index_find(repo_index, "two.txt")) >= 0);
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert((entry_idx = git_index_find(repo_index, "conflicts-one.txt")) >= 0);
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+
+ cl_assert((entry_idx = git_index_find(repo_index, "conflicts-two.txt")) >= 0);
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+}
+
diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c
index a535d6815..cf971e1dd 100644
--- a/tests-clar/index/tests.c
+++ b/tests-clar/index/tests.c
@@ -231,15 +231,19 @@ void test_index_tests__add(void)
cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
/* Add the new file to the index */
- cl_git_pass(git_index_add(index, "test.txt", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "test.txt"));
/* Wow... it worked! */
cl_assert(git_index_entrycount(index) == 1);
- entry = git_index_get(index, 0);
+ entry = git_index_get_byindex(index, 0);
/* And the built-in hashing mechanism worked as expected */
cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+
git_index_free(index);
git_repository_free(repo);
}
diff --git a/tests-clar/network/fetch.c b/tests-clar/network/fetch.c
index 5ff7b0af8..be1a21c54 100644
--- a/tests-clar/network/fetch.c
+++ b/tests-clar/network/fetch.c
@@ -28,12 +28,17 @@ static int update_tips(const char *refname, const git_oid *a, const git_oid *b,
return 0;
}
+static void progress(const git_transfer_progress *stats, void *payload)
+{
+ int *bytes_received = (int*)payload;
+ *bytes_received = stats->received_bytes;
+}
+
static void do_fetch(const char *url, int flag, int n)
{
git_remote *remote;
- git_off_t bytes;
- git_indexer_stats stats;
git_remote_callbacks callbacks;
+ int bytes_received = 0;
memset(&callbacks, 0, sizeof(git_remote_callbacks));
callbacks.update_tips = update_tips;
@@ -43,10 +48,11 @@ static void do_fetch(const char *url, int flag, int n)
git_remote_set_callbacks(remote, &callbacks);
git_remote_set_autotag(remote, flag);
cl_git_pass(git_remote_connect(remote, GIT_DIR_FETCH));
- cl_git_pass(git_remote_download(remote, &bytes, &stats));
+ cl_git_pass(git_remote_download(remote, progress, &bytes_received));
git_remote_disconnect(remote);
cl_git_pass(git_remote_update_tips(remote));
cl_assert_equal_i(counter, n);
+ cl_assert(bytes_received > 0);
git_remote_free(remote);
}
diff --git a/tests-clar/network/remoterename.c b/tests-clar/network/remoterename.c
new file mode 100644
index 000000000..70041f45d
--- /dev/null
+++ b/tests-clar/network/remoterename.c
@@ -0,0 +1,201 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+#include "repository.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+
+void test_network_remoterename__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+}
+
+void test_network_remoterename__cleanup(void)
+{
+ git_remote_free(_remote);
+
+ cl_git_sandbox_cleanup();
+}
+
+static int dont_call_me_cb(const char *fetch_refspec, void *payload)
+{
+ GIT_UNUSED(fetch_refspec);
+ GIT_UNUSED(payload);
+
+ cl_assert(false);
+
+ return -1;
+}
+
+void test_network_remoterename__renaming_a_remote_moves_related_configuration_section(void)
+{
+ assert_config_entry_existence(_repo, "remote.test.fetch", true);
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", false);
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", true);
+}
+
+void test_network_remoterename__renaming_a_remote_updates_branch_related_configuration_entries(void)
+{
+ assert_config_entry_value(_repo, "branch.master.remote", "test");
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_value(_repo, "branch.master.remote", "just/renamed");
+}
+
+void test_network_remoterename__renaming_a_remote_updates_default_fetchrefspec(void)
+{
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/heads/*:refs/remotes/just/renamed/*");
+}
+
+void test_network_remoterename__renaming_a_remote_without_a_fetchrefspec_doesnt_create_one(void)
+{
+ git_config *config;
+
+ git_remote_free(_remote);
+ cl_git_pass(git_repository_config__weakptr(&config, _repo));
+ cl_git_pass(git_config_delete(config, "remote.test.fetch"));
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", false);
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
+}
+
+static int ensure_refspecs(const char* refspec_name, void *payload)
+{
+ int i = 0;
+ bool found = false;
+ const char ** exp = (const char **)payload;
+
+ while (exp[i]) {
+ if (strcmp(exp[i++], refspec_name))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ cl_assert(found);
+
+ return 0;
+}
+
+void test_network_remoterename__renaming_a_remote_notifies_of_non_default_fetchrefspec(void)
+{
+ git_config *config;
+
+ char *expected_refspecs[] = {
+ "+refs/*:refs/*",
+ NULL
+ };
+
+ git_remote_free(_remote);
+ cl_git_pass(git_repository_config__weakptr(&config, _repo));
+ cl_git_pass(git_config_set_string(config, "remote.test.fetch", "+refs/*:refs/*"));
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", ensure_refspecs, &expected_refspecs));
+
+ assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/*:refs/*");
+}
+
+void test_network_remoterename__new_name_can_contain_dots(void)
+{
+ cl_git_pass(git_remote_rename(_remote, "just.renamed", dont_call_me_cb, NULL));
+ cl_assert_equal_s("just.renamed", git_remote_name(_remote));
+}
+
+void test_network_remoterename__new_name_must_conform_to_reference_naming_conventions(void)
+{
+ cl_git_fail(git_remote_rename(_remote, "new@{name", dont_call_me_cb, NULL));
+}
+
+void test_network_remoterename__renamed_name_is_persisted(void)
+{
+ git_remote *renamed;
+ git_repository *another_repo;
+
+ cl_git_fail(git_remote_load(&renamed, _repo, "just/renamed"));
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ cl_git_pass(git_repository_open(&another_repo, "testrepo.git"));
+ cl_git_pass(git_remote_load(&renamed, _repo, "just/renamed"));
+
+ git_remote_free(renamed);
+ git_repository_free(another_repo);
+}
+
+void test_network_remoterename__cannot_overwrite_an_existing_remote(void)
+{
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test", dont_call_me_cb, NULL));
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test_with_pushurl", dont_call_me_cb, NULL));
+}
+
+void test_network_remoterename__renaming_an_inmemory_remote_persists_it(void)
+{
+ git_remote *remote;
+
+ assert_config_entry_existence(_repo, "remote.durable.url", false);
+
+ cl_git_pass(git_remote_new(&remote, _repo, NULL, "git://github.com/libgit2/durable.git", NULL));
+
+ assert_config_entry_existence(_repo, "remote.durable.url", false);
+
+ cl_git_pass(git_remote_rename(remote, "durable", dont_call_me_cb, NULL));
+
+ assert_config_entry_value(_repo, "remote.durable.url", "git://github.com/libgit2/durable.git");
+
+ git_remote_free(remote);
+}
+
+void test_network_remoterename__renaming_an_inmemory_nameless_remote_notifies_the_inability_to_update_the_fetch_refspec(void)
+{
+ git_remote *remote;
+
+ char *expected_refspecs[] = {
+ "+refs/heads/*:refs/remotes/volatile/*",
+ NULL
+ };
+
+ assert_config_entry_existence(_repo, "remote.volatile.url", false);
+
+ cl_git_pass(git_remote_new(
+ &remote,
+ _repo,
+ NULL,
+ "git://github.com/libgit2/volatile.git",
+ "+refs/heads/*:refs/remotes/volatile/*"));
+
+ cl_git_pass(git_remote_rename(remote, "durable", ensure_refspecs, &expected_refspecs));
+
+ git_remote_free(remote);
+}
+
+void test_network_remoterename__renaming_a_remote_moves_the_underlying_reference(void)
+{
+ git_reference *underlying;
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed"));
+ cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
+ git_reference_free(underlying);
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
+ cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed/master"));
+ git_reference_free(underlying);
+}
diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c
index 91c3e879d..9fe67d856 100644
--- a/tests-clar/network/remotes.c
+++ b/tests-clar/network/remotes.c
@@ -10,9 +10,8 @@ static const git_refspec *_refspec;
void test_network_remotes__initialize(void)
{
- cl_fixture_sandbox("testrepo.git");
+ _repo = cl_git_sandbox_init("testrepo.git");
- cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
cl_git_pass(git_remote_load(&_remote, _repo, "test"));
_refspec = git_remote_fetchspec(_remote);
@@ -22,8 +21,7 @@ void test_network_remotes__initialize(void)
void test_network_remotes__cleanup(void)
{
git_remote_free(_remote);
- git_repository_free(_repo);
- cl_fixture_cleanup("testrepo.git");
+ cl_git_sandbox_cleanup();
}
void test_network_remotes__parsing(void)
@@ -73,7 +71,7 @@ void test_network_remotes__parsing_local_path_fails_if_path_not_found(void)
void test_network_remotes__supported_transport_methods_are_supported(void)
{
- cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
+ cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
}
void test_network_remotes__unsupported_transport_methods_are_unsupported(void)
@@ -226,6 +224,29 @@ void test_network_remotes__add(void)
cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
}
+void test_network_remotes__cannot_add_a_nameless_remote(void)
+{
+ git_remote *remote;
+
+ cl_git_fail(git_remote_add(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
+ cl_git_fail(git_remote_add(&remote, _repo, "", "git://github.com/libgit2/libgit2"));
+}
+
+void test_network_remotes__cannot_save_a_nameless_remote(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_new(&remote, _repo, NULL, "git://github.com/libgit2/libgit2", NULL));
+
+ cl_git_fail(git_remote_save(remote));
+ git_remote_free(remote);
+
+ cl_git_pass(git_remote_new(&remote, _repo, "", "git://github.com/libgit2/libgit2", NULL));
+
+ cl_git_fail(git_remote_save(remote));
+ git_remote_free(remote);
+}
+
void test_network_remotes__tagopt(void)
{
const char *opt;
diff --git a/tests-clar/object/commit/commitstagedfile.c b/tests-clar/object/commit/commitstagedfile.c
index 882fb49ae..ac38acf5b 100644
--- a/tests-clar/object/commit/commitstagedfile.c
+++ b/tests-clar/object/commit/commitstagedfile.c
@@ -71,9 +71,9 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
*/
cl_git_mkfile("treebuilder/test.txt", "test\n");
cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add(index, "test.txt", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "test.txt"));
- entry = git_index_get(index, 0);
+ entry = git_index_get_byindex(index, 0);
cl_assert(git_oid_cmp(&expected_blob_oid, &entry->oid) == 0);
@@ -128,68 +128,3 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
git_tree_free(tree);
git_index_free(index);
}
-
-void test_object_commit_commitstagedfile__message_prettify(void)
-{
- char buffer[100];
-
- cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 0) == 1);
- cl_assert_equal_s(buffer, "");
- cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 1) == 1);
- cl_assert_equal_s(buffer, "");
-
- cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 0));
- cl_assert_equal_s("Short\n", buffer);
- cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 1));
- cl_assert_equal_s("Short\n", buffer);
-
- cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 0) > 0);
- cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n# with some comments still in\n");
-
- cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 1) > 0);
- cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n");
-
- /* try out overflow */
- cl_assert(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "12345678",
- 0) > 0);
- cl_assert_equal_s(buffer,
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n");
-
- cl_assert(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n",
- 0) > 0);
- cl_assert_equal_s(buffer,
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n");
-
- cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "123456789",
- 0));
- cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "123456789\n",
- 0));
- cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890",
- 0));
- cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890""x",
- 0));
-
- cl_assert(git_message_prettify(buffer, sizeof(buffer),
- "1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n"
- "# 1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n"
- "1234567890",
- 1) > 0);
-
- cl_assert(git_message_prettify(NULL, 0, "", 0) == 1);
- cl_assert(git_message_prettify(NULL, 0, "Short test", 0) == 12);
- cl_assert(git_message_prettify(NULL, 0, "Test\n# with\nComments", 1) == 15);
-}
diff --git a/tests-clar/object/message.c b/tests-clar/object/message.c
index 43be8b152..7ef6374b3 100644
--- a/tests-clar/object/message.c
+++ b/tests-clar/object/message.c
@@ -169,3 +169,68 @@ void test_object_message__keep_comments(void)
assert_message_prettifying("# comment\n" ttt "\n", "# comment\n" ttt "\n", 0);
assert_message_prettifying(ttt "\n" "# comment\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 0);
}
+
+void test_object_message__message_prettify(void)
+{
+ char buffer[100];
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 0) == 1);
+ cl_assert_equal_s(buffer, "");
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 1) == 1);
+ cl_assert_equal_s(buffer, "");
+
+ cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 0));
+ cl_assert_equal_s("Short\n", buffer);
+ cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 1));
+ cl_assert_equal_s("Short\n", buffer);
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 0) > 0);
+ cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n# with some comments still in\n");
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 1) > 0);
+ cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n");
+
+ /* try out overflow */
+ cl_assert(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678",
+ 0) > 0);
+ cl_assert_equal_s(buffer,
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n");
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n",
+ 0) > 0);
+ cl_assert_equal_s(buffer,
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n");
+
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "123456789",
+ 0));
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "123456789\n",
+ 0));
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890",
+ 0));
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890""x",
+ 0));
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n"
+ "# 1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n"
+ "1234567890",
+ 1) > 0);
+
+ cl_assert(git_message_prettify(NULL, 0, "", 0) == 1);
+ cl_assert(git_message_prettify(NULL, 0, "Short test", 0) == 12);
+ cl_assert(git_message_prettify(NULL, 0, "Test\n# with\nComments", 1) == 15);
+}
diff --git a/tests-clar/pack/packbuilder.c b/tests-clar/pack/packbuilder.c
index fa7bec14e..6d17a709f 100644
--- a/tests-clar/pack/packbuilder.c
+++ b/tests-clar/pack/packbuilder.c
@@ -33,7 +33,7 @@ void test_pack_packbuilder__cleanup(void)
void test_pack_packbuilder__create_pack(void)
{
- git_indexer_stats stats;
+ git_transfer_progress stats;
git_oid oid, *o;
unsigned int i;
diff --git a/tests-clar/refs/branches/delete.c b/tests-clar/refs/branches/delete.c
index 4e9c70904..da7db13fc 100644
--- a/tests-clar/refs/branches/delete.c
+++ b/tests-clar/refs/branches/delete.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "refs.h"
#include "repo/repo_helpers.h"
+#include "config/config_helpers.h"
static git_repository *repo;
static git_reference *fake_remote;
@@ -90,3 +91,17 @@ void test_refs_branches_delete__can_delete_a_remote_branch(void)
cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE));
cl_git_pass(git_branch_delete(branch));
}
+
+void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void)
+{
+ git_reference *branch;
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+} \ No newline at end of file
diff --git a/tests-clar/refs/branches/move.c b/tests-clar/refs/branches/move.c
index 62b6042c6..042469016 100644
--- a/tests-clar/refs/branches/move.c
+++ b/tests-clar/refs/branches/move.c
@@ -1,5 +1,6 @@
#include "clar_libgit2.h"
#include "refs.h"
+#include "config/config_helpers.h"
static git_repository *repo;
static git_reference *ref;
@@ -63,6 +64,27 @@ void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
cl_git_pass(git_branch_move(ref, "master", 1));
}
+void test_refs_branches_move__moving_a_branch_moves_related_configuration_data(void)
+{
+ git_reference *branch;
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+ assert_config_entry_existence(repo, "branch.moved.remote", false);
+ assert_config_entry_existence(repo, "branch.moved.merge", false);
+
+ cl_git_pass(git_branch_move(branch, "moved", 0));
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+ assert_config_entry_existence(repo, "branch.moved.remote", true);
+ assert_config_entry_existence(repo, "branch.moved.merge", true);
+
+ git_reference_free(branch);
+}
+
void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(void)
{
git_reference *branch;
diff --git a/tests-clar/refs/reflog/drop.c b/tests-clar/refs/reflog/drop.c
index 86781c041..4be857b42 100644
--- a/tests-clar/refs/reflog/drop.c
+++ b/tests-clar/refs/reflog/drop.c
@@ -43,57 +43,48 @@ void test_refs_reflog_drop__can_drop_an_entry(void)
void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void)
{
- const git_reflog_entry *before_previous, *before_next;
- const git_reflog_entry *after_next;
- git_oid before_next_old_oid;
+ const git_reflog_entry *before_current;
+ const git_reflog_entry *after_current;
+ git_oid before_current_old_oid, before_current_cur_oid;
cl_assert(entrycount > 4);
- before_previous = git_reflog_entry_byindex(g_reflog, 3);
- before_next = git_reflog_entry_byindex(g_reflog, 1);
- git_oid_cpy(&before_next_old_oid, &before_next->oid_old);
+ before_current = git_reflog_entry_byindex(g_reflog, 2);
+ git_oid_cpy(&before_current_old_oid, &before_current->oid_old);
+ git_oid_cpy(&before_current_cur_oid, &before_current->oid_cur);
cl_git_pass(git_reflog_drop(g_reflog, 2, 1));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
- after_next = git_reflog_entry_byindex(g_reflog, 1);
+ after_current = git_reflog_entry_byindex(g_reflog, 2);
- cl_assert_equal_i(0, git_oid_cmp(&before_next->oid_cur, &after_next->oid_cur));
- cl_assert(git_oid_cmp(&before_next_old_oid, &after_next->oid_old) != 0);
- cl_assert_equal_i(0, git_oid_cmp(&before_previous->oid_cur, &after_next->oid_old));
+ cl_assert_equal_i(0, git_oid_cmp(&before_current_old_oid, &after_current->oid_old));
+ cl_assert(0 != git_oid_cmp(&before_current_cur_oid, &after_current->oid_cur));
}
-void test_refs_reflog_drop__can_drop_the_first_entry(void)
-{
- cl_assert(entrycount > 2);
-
- cl_git_pass(git_reflog_drop(g_reflog, 0, 0));
- cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
-}
-
-void test_refs_reflog_drop__can_drop_the_last_entry(void)
+void test_refs_reflog_drop__can_drop_the_oldest_entry(void)
{
const git_reflog_entry *entry;
cl_assert(entrycount > 2);
- cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0));
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 0));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
- entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ entry = git_reflog_entry_byindex(g_reflog, 0);
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) != 0);
}
-void test_refs_reflog_drop__can_drop_the_last_entry_and_rewrite_the_log_history(void)
+void test_refs_reflog_drop__can_drop_the_oldest_entry_and_rewrite_the_log_history(void)
{
const git_reflog_entry *entry;
cl_assert(entrycount > 2);
- cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
- entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ entry = git_reflog_entry_byindex(g_reflog, 0);
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
}
diff --git a/tests-clar/repo/state.c b/tests-clar/repo/state.c
new file mode 100644
index 000000000..c0aba1987
--- /dev/null
+++ b/tests-clar/repo/state.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "refs.h"
+#include "posix.h"
+#include "fileops.h"
+
+static git_repository *_repo;
+static git_buf _path;
+
+void test_repo_state__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_repo_state__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ git_buf_free(&_path);
+}
+
+static void setup_simple_state(const char *filename)
+{
+ cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), filename));
+ git_futils_mkpath2file(git_buf_cstr(&_path), 0777);
+ cl_git_mkfile(git_buf_cstr(&_path), "dummy");
+
+ cl_git_pass(git_repository_detach_head(_repo));
+}
+
+static void assert_repo_state(git_repository_state_t state)
+{
+ cl_assert_equal_i(state, git_repository_state(_repo));
+}
+
+void test_repo_state__none_with_HEAD_attached(void)
+{
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__none_with_HEAD_detached(void)
+{
+ cl_git_pass(git_repository_detach_head(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__merge(void)
+{
+ setup_simple_state(GIT_MERGE_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_MERGE);
+}
+
+void test_repo_state__revert(void)
+{
+ setup_simple_state(GIT_REVERT_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REVERT);
+}
+
+void test_repo_state__cherry_pick(void)
+{
+ setup_simple_state(GIT_CHERRY_PICK_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_CHERRY_PICK);
+}
+
+void test_repo_state__bisect(void)
+{
+ setup_simple_state(GIT_BISECT_LOG_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_BISECT);
+}
+
+void test_repo_state__rebase_interactive(void)
+{
+ setup_simple_state(GIT_REBASE_MERGE_INTERACTIVE_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE_INTERACTIVE);
+}
+
+void test_repo_state__rebase_merge(void)
+{
+ setup_simple_state(GIT_REBASE_MERGE_DIR "whatever");
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE_MERGE);
+}
+
+void test_repo_state__rebase(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_REBASING_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE);
+}
+
+void test_repo_state__apply_mailbox(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_APPLYING_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX);
+}
+
+void test_repo_state__apply_mailbox_or_rebase(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_DIR "whatever");
+ assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE);
+}
diff --git a/tests-clar/reset/hard.c b/tests-clar/reset/hard.c
index fdab9c536..c3f041817 100644
--- a/tests-clar/reset/hard.c
+++ b/tests-clar/reset/hard.c
@@ -58,3 +58,38 @@ void test_reset_hard__cannot_reset_in_a_bare_repository(void)
git_repository_free(bare);
}
+
+void test_reset_hard__cleans_up_merge(void)
+{
+ git_buf merge_head_path = GIT_BUF_INIT,
+ merge_msg_path = GIT_BUF_INIT,
+ merge_mode_path = GIT_BUF_INIT,
+ orig_head_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n");
+
+ cl_git_pass(git_buf_joinpath(&merge_msg_path, git_repository_path(repo), "MERGE_MSG"));
+ cl_git_mkfile(git_buf_cstr(&merge_head_path), "Merge commit 0017bd4ab1ec30440b17bae1680cff124ab5f1f6\n");
+
+ cl_git_pass(git_buf_joinpath(&merge_msg_path, git_repository_path(repo), "MERGE_MODE"));
+ cl_git_mkfile(git_buf_cstr(&merge_head_path), "");
+
+ cl_git_pass(git_buf_joinpath(&orig_head_path, git_repository_path(repo), "ORIG_HEAD"));
+ cl_git_mkfile(git_buf_cstr(&orig_head_path), "0017bd4ab1ec30440b17bae1680cff124ab5f1f6");
+
+ retrieve_target_from_oid(&target, repo, "0017bd4ab1ec30440b17bae1680cff124ab5f1f6");
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+
+ cl_assert(!git_path_exists(git_buf_cstr(&merge_head_path)));
+ cl_assert(!git_path_exists(git_buf_cstr(&merge_msg_path)));
+ cl_assert(!git_path_exists(git_buf_cstr(&merge_mode_path)));
+
+ cl_assert(git_path_exists(git_buf_cstr(&orig_head_path)));
+ cl_git_pass(p_unlink(git_buf_cstr(&orig_head_path)));
+
+ git_buf_free(&merge_head_path);
+ git_buf_free(&merge_msg_path);
+ git_buf_free(&merge_mode_path);
+ git_buf_free(&orig_head_path);
+}
diff --git a/tests-clar/reset/soft.c b/tests-clar/reset/soft.c
index 1872baf3b..d51e3f1f1 100644
--- a/tests-clar/reset/soft.c
+++ b/tests-clar/reset/soft.c
@@ -1,5 +1,7 @@
#include "clar_libgit2.h"
+#include "posix.h"
#include "reset_helpers.h"
+#include "path.h"
#include "repo/repo_helpers.h"
static git_repository *repo;
@@ -110,3 +112,19 @@ void test_reset_soft__resetting_against_an_orphaned_head_repo_makes_the_head_no_
git_reference_free(head);
}
+
+void test_reset_soft__fails_when_merging(void)
+{
+ git_buf merge_head_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_detach_head(repo));
+ cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n");
+
+ retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT));
+ cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path)));
+
+ git_buf_free(&merge_head_path);
+}
diff --git a/tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG b/tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..1f7391f92
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+master
diff --git a/tests-clar/resources/mergedrepo/.gitted/HEAD b/tests-clar/resources/mergedrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD b/tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD
new file mode 100644
index 000000000..a5bdf6e40
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD
@@ -0,0 +1 @@
+e2809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_MODE b/tests-clar/resources/mergedrepo/.gitted/MERGE_MODE
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/MERGE_MODE
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_MSG b/tests-clar/resources/mergedrepo/.gitted/MERGE_MSG
new file mode 100644
index 000000000..7c4d1f5a9
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/MERGE_MSG
@@ -0,0 +1,5 @@
+Merge branch 'branch'
+
+Conflicts:
+ conflicts-one.txt
+ conflicts-two.txt
diff --git a/tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD b/tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD
new file mode 100644
index 000000000..13d4d6721
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+3a34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests-clar/resources/mergedrepo/.gitted/config b/tests-clar/resources/mergedrepo/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests-clar/resources/mergedrepo/.gitted/description b/tests-clar/resources/mergedrepo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/mergedrepo/.gitted/index b/tests-clar/resources/mergedrepo/.gitted/index
new file mode 100644
index 000000000..3d29f78e7
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/info/exclude b/tests-clar/resources/mergedrepo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/HEAD b/tests-clar/resources/mergedrepo/.gitted/logs/HEAD
new file mode 100644
index 000000000..a385da67b
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/logs/HEAD
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371828 -0500 commit (initial): initial
+9a05ccb4e0f948de03128e095f39dae6976751c5 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371835 -0500 checkout: moving from master to branch
+9a05ccb4e0f948de03128e095f39dae6976751c5 e2809157a7766f272e4cfe26e61ef2678a5357ff Edward Thomson <ethomson@edwardthomson.com> 1351371872 -0500 commit: branch
+e2809157a7766f272e4cfe26e61ef2678a5357ff 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371873 -0500 checkout: moving from branch to master
+9a05ccb4e0f948de03128e095f39dae6976751c5 3a34580a35add43a4cf361e8e9a30060a905c876 Edward Thomson <ethomson@edwardthomson.com> 1351372106 -0500 commit: master
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch b/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch
new file mode 100644
index 000000000..26a5e8dc5
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371835 -0500 branch: Created from HEAD
+9a05ccb4e0f948de03128e095f39dae6976751c5 e2809157a7766f272e4cfe26e61ef2678a5357ff Edward Thomson <ethomson@edwardthomson.com> 1351371872 -0500 commit: branch
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master b/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..425f7bd89
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371828 -0500 commit (initial): initial
+9a05ccb4e0f948de03128e095f39dae6976751c5 3a34580a35add43a4cf361e8e9a30060a905c876 Edward Thomson <ethomson@edwardthomson.com> 1351372106 -0500 commit: master
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712 b/tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
new file mode 100644
index 000000000..9232f79d9
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91 b/tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
new file mode 100644
index 000000000..3e124d9a4
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81 b/tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
new file mode 100644
index 000000000..7bb19c873
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e b/tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
new file mode 100644
index 000000000..487bcffb1
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6 b/tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
new file mode 100644
index 000000000..2eb3954fc
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534 b/tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
new file mode 100644
index 000000000..ebe83ccb2
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876 b/tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
new file mode 100644
index 000000000..0d4095ffc
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
@@ -0,0 +1,2 @@
+xK
+1D]}Dx/O"F2oo<*ZoљuIhhrl"r YT8'#vm0.¨.:.#+9R^nG~[=VjR"IjD۔7|N` \ No newline at end of file
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c b/tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
new file mode 100644
index 000000000..33389c302
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3 b/tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
new file mode 100644
index 000000000..5361ea685
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da b/tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
new file mode 100644
index 000000000..a60da877c
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad b/tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
new file mode 100644
index 000000000..85e84d71e
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399 b/tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
new file mode 100644
index 000000000..b16b521e6
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8 b/tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
new file mode 100644
index 000000000..7c4e85ffb
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4 b/tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
new file mode 100644
index 000000000..65173fc4d
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71 b/tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
new file mode 100644
index 000000000..162fa4455
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c b/tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
new file mode 100644
index 000000000..77a519f55
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda b/tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
new file mode 100644
index 000000000..f624cd4f1
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673 b/tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
new file mode 100644
index 000000000..096474c03
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a b/tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
new file mode 100644
index 000000000..a413bc6b0
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2 b/tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
new file mode 100644
index 000000000..3ac8f6018
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8 b/tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
new file mode 100644
index 000000000..589a5ae9b
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9 b/tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
new file mode 100644
index 000000000..6503985e3
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50 b/tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
new file mode 100644
index 000000000..2eaa80838
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5 b/tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
new file mode 100644
index 000000000..7373a80d8
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
@@ -0,0 +1 @@
+x !Dm@ c6q##Ay/ ܁:#$ltH:闄*DXhV} ˷n[-K_;Z@J GԈbq3"go@I \ No newline at end of file
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c b/tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
new file mode 100644
index 000000000..c5a651f97
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d b/tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
new file mode 100644
index 000000000..3e14b5dc8
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51 b/tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
new file mode 100644
index 000000000..a641adc2e
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff b/tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
new file mode 100644
index 000000000..fa86662e0
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
@@ -0,0 +1,3 @@
+xK
+1D]t> xNq1(]{Pe mٍ.S0[Dcd
+ŅbMԝCgd@>glX].$!0*zu})/E_<ڪO:WځrơqѤh@mt;;5uZyVo\M \ No newline at end of file
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478 b/tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
new file mode 100644
index 000000000..c9841c698
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406 b/tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
new file mode 100644
index 000000000..cd587dbec
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/refs/heads/branch b/tests-clar/resources/mergedrepo/.gitted/refs/heads/branch
new file mode 100644
index 000000000..a5bdf6e40
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/refs/heads/branch
@@ -0,0 +1 @@
+e2809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests-clar/resources/mergedrepo/.gitted/refs/heads/master b/tests-clar/resources/mergedrepo/.gitted/refs/heads/master
new file mode 100644
index 000000000..13d4d6721
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3a34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests-clar/resources/mergedrepo/conflicts-one.txt b/tests-clar/resources/mergedrepo/conflicts-one.txt
new file mode 100644
index 000000000..8aad34cc8
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/conflicts-one.txt
@@ -0,0 +1,5 @@
+<<<<<<< HEAD
+This is most certainly a conflict!
+=======
+This is a conflict!!!
+>>>>>>> branch
diff --git a/tests-clar/resources/mergedrepo/conflicts-two.txt b/tests-clar/resources/mergedrepo/conflicts-two.txt
new file mode 100644
index 000000000..e62cac5c8
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/conflicts-two.txt
@@ -0,0 +1,5 @@
+<<<<<<< HEAD
+This is without question another conflict!
+=======
+This is another conflict!!!
+>>>>>>> branch
diff --git a/tests-clar/resources/mergedrepo/one.txt b/tests-clar/resources/mergedrepo/one.txt
new file mode 100644
index 000000000..75938de1e
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/one.txt
@@ -0,0 +1,10 @@
+This is file one!
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one!
diff --git a/tests-clar/resources/mergedrepo/two.txt b/tests-clar/resources/mergedrepo/two.txt
new file mode 100644
index 000000000..7b26923aa
--- /dev/null
+++ b/tests-clar/resources/mergedrepo/two.txt
@@ -0,0 +1,12 @@
+This is file two!
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two!
diff --git a/tests-clar/stash/drop.c b/tests-clar/stash/drop.c
new file mode 100644
index 000000000..5bbc7452a
--- /dev/null
+++ b/tests-clar/stash/drop.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+
+void test_stash_drop__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "stash", 0));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+}
+
+void test_stash_drop__cleanup(void)
+{
+ git_signature_free(signature);
+ git_repository_free(repo);
+ cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
+
+void test_stash_drop__cannot_drop_from_an_empty_stash(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_stash_drop(repo, 0));
+}
+
+static void push_three_states(void)
+{
+ git_oid oid;
+ git_index *index;
+
+ cl_git_mkfile("stash/zero.txt", "content\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_from_workdir(index, "zero.txt"));
+ commit_staged_files(&oid, index, signature);
+
+ cl_git_mkfile("stash/one.txt", "content\n");
+ cl_git_pass(git_stash_save(&oid, repo, signature, "First", GIT_STASH_INCLUDE_UNTRACKED));
+
+ cl_git_mkfile("stash/two.txt", "content\n");
+ cl_git_pass(git_stash_save(&oid, repo, signature, "Second", GIT_STASH_INCLUDE_UNTRACKED));
+
+ cl_git_mkfile("stash/three.txt", "content\n");
+ cl_git_pass(git_stash_save(&oid, repo, signature, "Third", GIT_STASH_INCLUDE_UNTRACKED));
+
+ git_index_free(index);
+}
+
+void test_stash_drop__cannot_drop_a_non_existing_stashed_state(void)
+{
+ push_three_states();
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_stash_drop(repo, 666));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_stash_drop(repo, 42));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_stash_drop(repo, 3));
+}
+
+void test_stash_drop__can_purge_the_stash_from_the_top(void)
+{
+ push_three_states();
+
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_stash_drop(repo, 0));
+}
+
+void test_stash_drop__can_purge_the_stash_from_the_bottom(void)
+{
+ push_three_states();
+
+ cl_git_pass(git_stash_drop(repo, 2));
+ cl_git_pass(git_stash_drop(repo, 1));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_stash_drop(repo, 0));
+}
+
+void test_stash_drop__dropping_an_entry_rewrites_reflog_history(void)
+{
+ git_reference *stash;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+ git_oid oid;
+ size_t count;
+
+ push_three_states();
+
+ cl_git_pass(git_reference_lookup(&stash, repo, "refs/stash"));
+
+ cl_git_pass(git_reflog_read(&reflog, stash));
+ entry = git_reflog_entry_byindex(reflog, 1);
+
+ git_oid_cpy(&oid, git_reflog_entry_oidold(entry));
+ count = git_reflog_entrycount(reflog);
+
+ git_reflog_free(reflog);
+
+ cl_git_pass(git_stash_drop(repo, 1));
+
+ cl_git_pass(git_reflog_read(&reflog, stash));
+ entry = git_reflog_entry_byindex(reflog, 1);
+
+ cl_assert_equal_i(0, git_oid_cmp(&oid, git_reflog_entry_oidold(entry)));
+ cl_assert_equal_i(count - 1, git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+
+ git_reference_free(stash);
+}
+
+void test_stash_drop__dropping_the_last_entry_removes_the_stash(void)
+{
+ git_reference *stash;
+
+ push_three_states();
+
+ cl_git_pass(git_reference_lookup(&stash, repo, "refs/stash"));
+ git_reference_free(stash);
+
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&stash, repo, "refs/stash"));
+}
diff --git a/tests-clar/stash/foreach.c b/tests-clar/stash/foreach.c
new file mode 100644
index 000000000..818d906e7
--- /dev/null
+++ b/tests-clar/stash/foreach.c
@@ -0,0 +1,120 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+struct callback_data
+{
+ char **oids;
+ int invokes;
+};
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+struct callback_data data;
+
+#define REPO_NAME "stash"
+
+void test_stash_foreach__initialize(void)
+{
+ cl_git_pass(git_signature_new(
+ &signature,
+ "nulltoken",
+ "emeric.fermas@gmail.com",
+ 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ memset(&data, 0, sizeof(struct callback_data));
+}
+
+void test_stash_foreach__cleanup(void)
+{
+ git_signature_free(signature);
+ git_repository_free(repo);
+ cl_git_pass(git_futils_rmdir_r(REPO_NAME, NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
+
+static int callback_cb(
+ size_t index,
+ const char* message,
+ const git_oid *stash_oid,
+ void *payload)
+{
+ struct callback_data *data = (struct callback_data *)payload;
+
+ GIT_UNUSED(index);
+ GIT_UNUSED(message);
+
+ cl_assert_equal_i(0, git_oid_streq(stash_oid, data->oids[data->invokes++]));
+
+ return 0;
+}
+
+void test_stash_foreach__enumerating_a_empty_repository_doesnt_fail(void)
+{
+ char *oids[] = { NULL };
+
+ data.oids = oids;
+
+ cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+
+ cl_assert_equal_i(0, data.invokes);
+}
+
+void test_stash_foreach__can_enumerate_a_repository(void)
+{
+ char *oids_default[] = {
+ "1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+ char *oids_untracked[] = {
+ "7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+ "1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+ char *oids_ignored[] = {
+ "c95599a8fef20a7e57582c6727b1a0d02e0a5828",
+ "7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+ "1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+ cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+ setup_stash(repo, signature);
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_DEFAULT));
+
+ data.oids = oids_default;
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(1, data.invokes);
+
+ data.oids = oids_untracked;
+ data.invokes = 0;
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_INCLUDE_UNTRACKED));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(2, data.invokes);
+
+ data.oids = oids_ignored;
+ data.invokes = 0;
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_INCLUDE_IGNORED));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(3, data.invokes);
+}
diff --git a/tests-clar/stash/save.c b/tests-clar/stash/save.c
new file mode 100644
index 000000000..01acf672c
--- /dev/null
+++ b/tests-clar/stash/save.c
@@ -0,0 +1,370 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+
+/*
+ * Friendly reminder, in order to ease the reading of the following tests:
+ *
+ * "stash" points to the worktree commit
+ * "stash^1" points to the base commit (HEAD when the stash was created)
+ * "stash^2" points to the index commit
+ * "stash^3" points to the untracked commit
+ */
+
+void test_stash_save__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "stash", 0));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ setup_stash(repo, signature);
+}
+
+void test_stash_save__cleanup(void)
+{
+ git_signature_free(signature);
+ git_repository_free(repo);
+ cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
+
+static void assert_object_oid(const char* revision, const char* expected_oid, git_otype type)
+{
+ git_object *object;
+ int result;
+
+ result = git_revparse_single(&object, repo, revision);
+
+ if (!expected_oid) {
+ cl_assert_equal_i(GIT_ENOTFOUND, result);
+ return;
+ } else
+ cl_assert_equal_i(0, result);
+
+ cl_assert_equal_i(type, git_object_type(object));
+ cl_git_pass(git_oid_streq(git_object_id(object), expected_oid));
+
+ git_object_free(object);
+}
+
+static void assert_blob_oid(const char* revision, const char* expected_oid)
+{
+ assert_object_oid(revision, expected_oid, GIT_OBJ_BLOB);
+}
+
+void test_stash_save__does_not_keep_index_by_default(void)
+{
+/*
+$ git stash
+
+$ git show refs/stash:what
+see you later
+
+$ git show refs/stash:how
+not so small and
+
+$ git show refs/stash:who
+funky world
+
+$ git show refs/stash:when
+fatal: Path 'when' exists on disk, but not in 'stash'.
+
+$ git show refs/stash^2:what
+goodbye
+
+$ git show refs/stash^2:how
+not so small and
+
+$ git show refs/stash^2:who
+world
+
+$ git show refs/stash^2:when
+fatal: Path 'when' exists on disk, but not in 'stash^2'.
+
+$ git status --short
+?? when
+
+*/
+ unsigned int status;
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ cl_git_pass(git_status_file(&status, repo, "when"));
+
+ assert_blob_oid("refs/stash:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
+ assert_blob_oid("refs/stash:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
+ assert_blob_oid("refs/stash:when", NULL);
+ assert_blob_oid("refs/stash:just.ignore", NULL);
+
+ assert_blob_oid("refs/stash^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
+ assert_blob_oid("refs/stash^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("refs/stash^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("refs/stash^2:when", NULL);
+ assert_blob_oid("refs/stash^2:just.ignore", NULL);
+
+ assert_blob_oid("refs/stash^3", NULL);
+
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+}
+
+static void assert_status(
+ const char *path,
+ int status_flags)
+{
+ unsigned int status;
+ int error;
+
+ error = git_status_file(&status, repo, path);
+
+ if (status_flags < 0) {
+ cl_assert_equal_i(status_flags, error);
+ return;
+ }
+
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i((unsigned int)status_flags, status);
+}
+
+void test_stash_save__can_keep_index(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
+
+ assert_status("what", GIT_STATUS_INDEX_MODIFIED);
+ assert_status("how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status("who", GIT_STATUS_CURRENT);
+ assert_status("when", GIT_STATUS_WT_NEW);
+ assert_status("just.ignore", GIT_STATUS_IGNORED);
+}
+
+static void assert_commit_message_contains(const char *revision, const char *fragment)
+{
+ git_commit *commit;
+
+ cl_git_pass(git_revparse_single(((git_object **)&commit), repo, revision));
+
+ cl_assert(strstr(git_commit_message(commit), fragment) != NULL);
+
+ git_commit_free(commit);
+}
+
+void test_stash_save__can_include_untracked_files(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
+
+ assert_blob_oid("refs/stash^3:what", NULL);
+ assert_blob_oid("refs/stash^3:how", NULL);
+ assert_blob_oid("refs/stash^3:who", NULL);
+ assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
+ assert_blob_oid("refs/stash^3:just.ignore", NULL);
+}
+
+void test_stash_save__can_include_untracked_and_ignored_files(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
+
+ assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
+
+ assert_blob_oid("refs/stash^3:what", NULL);
+ assert_blob_oid("refs/stash^3:how", NULL);
+ assert_blob_oid("refs/stash^3:who", NULL);
+ assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
+ assert_blob_oid("refs/stash^3:just.ignore", "78925fb1236b98b37a35e9723033e627f97aa88b");
+}
+
+#define MESSAGE "Look Ma! I'm on TV!"
+void test_stash_save__can_accept_a_message(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, MESSAGE, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash^2", "index on master: ");
+ assert_commit_message_contains("refs/stash", "On master: " MESSAGE);
+}
+
+void test_stash_save__cannot_stash_against_an_unborn_branch(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_lookup(&head, repo, "HEAD"));
+ cl_git_pass(git_reference_set_target(head, "refs/heads/unborn"));
+
+ cl_assert_equal_i(GIT_EORPHANEDHEAD,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ git_reference_free(head);
+}
+
+void test_stash_save__cannot_stash_against_a_bare_repository(void)
+{
+ git_repository *local;
+
+ cl_git_pass(git_repository_init(&local, "sorry-it-is-a-non-bare-only-party", 1));
+
+ cl_assert_equal_i(GIT_EBAREREPO,
+ git_stash_save(&stash_tip_oid, local, signature, NULL, GIT_STASH_DEFAULT));
+
+ git_repository_free(local);
+}
+
+void test_stash_save__can_stash_against_a_detached_head(void)
+{
+ git_repository_detach_head(repo);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash^2", "index on (no branch): ");
+ assert_commit_message_contains("refs/stash", "WIP on (no branch): ");
+}
+
+void test_stash_save__stashing_updates_the_reflog(void)
+{
+ char *sha;
+
+ assert_object_oid("refs/stash@{0}", NULL, GIT_OBJ_COMMIT);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ sha = git_oid_allocfmt(&stash_tip_oid);
+
+ assert_object_oid("refs/stash@{0}", sha, GIT_OBJ_COMMIT);
+ assert_object_oid("refs/stash@{1}", NULL, GIT_OBJ_COMMIT);
+
+ git__free(sha);
+}
+
+void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
+{
+ git_index *index;
+ git_oid commit_oid, stash_tip_oid;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /*
+ * 'what' and 'who' are being committed.
+ * 'when' remain untracked.
+ */
+ git_index_add_from_workdir(index, "what");
+ git_index_add_from_workdir(index, "who");
+ cl_git_pass(git_index_write(index));
+ commit_staged_files(&commit_oid, index, signature);
+ git_index_free(index);
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ p_unlink("stash/when");
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+}
+
+void test_stash_save__can_stage_normal_then_stage_untracked(void)
+{
+ /*
+ * $ git ls-tree stash@{1}^0
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
+ * 100644 blob bc99dc98b3eba0e9157e94769cd4d49cb49de449 what
+ * 100644 blob a0400d4954659306a976567af43125a0b1aa8595 who
+ *
+ * $ git ls-tree stash@{1}^1
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{1}^2
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
+ * 100644 blob dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{1}^3
+ * fatal: Not a valid object name stash@{1}^3
+ *
+ * $ git ls-tree stash@{0}^0
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^1
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^2
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^3
+ * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
+ */
+
+ assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status("how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status("who", GIT_STATUS_WT_MODIFIED);
+ assert_status("when", GIT_STATUS_WT_NEW);
+ assert_status("just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ assert_status("what", GIT_STATUS_CURRENT);
+ assert_status("how", GIT_STATUS_CURRENT);
+ assert_status("who", GIT_STATUS_CURRENT);
+ assert_status("when", GIT_STATUS_WT_NEW);
+ assert_status("just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+ assert_status("what", GIT_STATUS_CURRENT);
+ assert_status("how", GIT_STATUS_CURRENT);
+ assert_status("who", GIT_STATUS_CURRENT);
+ assert_status("when", GIT_ENOTFOUND);
+ assert_status("just.ignore", GIT_STATUS_IGNORED);
+
+
+ assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
+ assert_blob_oid("stash@{1}^0:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("stash@{1}^0:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
+ assert_blob_oid("stash@{1}^0:when", NULL);
+
+ assert_blob_oid("stash@{1}^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
+ assert_blob_oid("stash@{1}^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("stash@{1}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{1}^2:when", NULL);
+
+ assert_object_oid("stash@{1}^3", NULL, GIT_OBJ_COMMIT);
+
+ assert_blob_oid("stash@{0}^0:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
+ assert_blob_oid("stash@{0}^0:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
+ assert_blob_oid("stash@{0}^0:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{0}^0:when", NULL);
+
+ assert_blob_oid("stash@{0}^2:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
+ assert_blob_oid("stash@{0}^2:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
+ assert_blob_oid("stash@{0}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{0}^2:when", NULL);
+
+ assert_blob_oid("stash@{0}^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); /* now */
+}
+
+#define EMPTY_TREE "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
+
+void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
+{
+ p_unlink("stash/when");
+
+ assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status("how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status("who", GIT_STATUS_WT_MODIFIED);
+ assert_status("when", GIT_ENOTFOUND);
+ assert_status("just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJ_TREE);
+}
diff --git a/tests-clar/stash/stash_helpers.c b/tests-clar/stash/stash_helpers.c
new file mode 100644
index 000000000..0e93ecff0
--- /dev/null
+++ b/tests-clar/stash/stash_helpers.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+void commit_staged_files(
+ git_oid *commit_oid,
+ git_index *index,
+ git_signature *signature)
+{
+ git_tree *tree;
+ git_oid tree_oid;
+ git_repository *repo;
+
+ repo = git_index_owner(index);
+
+ cl_git_pass(git_tree_create_fromindex(&tree_oid, index));
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+ cl_git_pass(git_commit_create_v(
+ commit_oid,
+ repo,
+ "HEAD",
+ signature,
+ signature,
+ NULL,
+ "Initial commit",
+ tree,
+ 0));
+
+ git_tree_free(tree);
+}
+
+void setup_stash(git_repository *repo, git_signature *signature)
+{
+ git_oid commit_oid;
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_mkfile("stash/what", "hello\n"); /* ce013625030ba8dba906f756967f9e9ca394464a */
+ cl_git_mkfile("stash/how", "small\n"); /* ac790413e2d7a26c3767e78c57bb28716686eebc */
+ cl_git_mkfile("stash/who", "world\n"); /* cc628ccd10742baea8241c5924df992b5c019f71 */
+ cl_git_mkfile("stash/when", "now\n"); /* b6ed15e81e2593d7bb6265eb4a991d29dc3e628b */
+ cl_git_mkfile("stash/just.ignore", "me\n"); /* 78925fb1236b98b37a35e9723033e627f97aa88b */
+
+ cl_git_mkfile("stash/.gitignore", "*.ignore\n");
+
+ cl_git_pass(git_index_add_from_workdir(index, "what"));
+ cl_git_pass(git_index_add_from_workdir(index, "how"));
+ cl_git_pass(git_index_add_from_workdir(index, "who"));
+ cl_git_pass(git_index_add_from_workdir(index, ".gitignore"));
+ cl_git_pass(git_index_write(index));
+
+ commit_staged_files(&commit_oid, index, signature);
+
+ cl_git_rewritefile("stash/what", "goodbye\n"); /* dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 */
+ cl_git_rewritefile("stash/how", "not so small and\n"); /* e6d64adb2c7f3eb8feb493b556cc8070dca379a3 */
+ cl_git_rewritefile("stash/who", "funky world\n"); /* a0400d4954659306a976567af43125a0b1aa8595 */
+
+ cl_git_pass(git_index_add_from_workdir(index, "what"));
+ cl_git_pass(git_index_add_from_workdir(index, "how"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_rewritefile("stash/what", "see you later\n"); /* bc99dc98b3eba0e9157e94769cd4d49cb49de449 */
+
+ git_index_free(index);
+}
diff --git a/tests-clar/stash/stash_helpers.h b/tests-clar/stash/stash_helpers.h
new file mode 100644
index 000000000..bb7fec4f5
--- /dev/null
+++ b/tests-clar/stash/stash_helpers.h
@@ -0,0 +1,8 @@
+void setup_stash(
+ git_repository *repo,
+ git_signature *signature);
+
+void commit_staged_files(
+ git_oid *commit_oid,
+ git_index *index,
+ git_signature *signature); \ No newline at end of file
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index 908d34510..116286f67 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -438,7 +438,7 @@ void test_status_worktree__first_commit_in_progress(void)
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add(index, "testfile.txt", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "testfile.txt"));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
@@ -486,7 +486,7 @@ static void fill_index_wth_head_entries(git_repository *repo, git_index *index)
cl_git_pass(git_commit_lookup(&commit, repo, &oid));
cl_git_pass(git_commit_tree(&tree, commit));
- cl_git_pass(git_index_read_tree(index, tree, NULL));
+ cl_git_pass(git_index_read_tree(index, tree));
cl_git_pass(git_index_write(index));
git_tree_free(tree);
@@ -570,7 +570,7 @@ void test_status_worktree__bracket_in_filename(void)
/* add the file to the index */
cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add(index, FILE_WITH_BRACKET, 0));
+ cl_git_pass(git_index_add_from_workdir(index, FILE_WITH_BRACKET));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
@@ -648,7 +648,7 @@ void test_status_worktree__space_in_filename(void)
/* add the file to the index */
cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add(index, FILE_WITH_SPACE, 0));
+ cl_git_pass(git_index_add_from_workdir(index, FILE_WITH_SPACE));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
@@ -816,7 +816,7 @@ void test_status_worktree__new_staged_file_must_handle_crlf(void)
cl_git_mkfile("getting_started/testfile.txt", "content\r\n"); // Content with CRLF
cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add(index, "testfile.txt", 0));
+ cl_git_pass(git_index_add_from_workdir(index, "testfile.txt"));
cl_git_pass(git_index_write(index));
cl_git_pass(git_status_file(&status, repo, "testfile.txt"));
diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c
index 63073ceca..eec028c40 100644
--- a/tests-clar/submodule/status.c
+++ b/tests-clar/submodule/status.c
@@ -105,7 +105,7 @@ void test_submodule_status__ignore_none(void)
cl_git_pass(git_repository_index(&index, g_repo));
pos = git_index_find(index, "sm_changed_head");
cl_assert(pos >= 0);
- cl_git_pass(git_index_remove(index, pos));
+ cl_git_pass(git_index_remove(index, "sm_changed_head", 0));
cl_git_pass(git_index_write(index));
git_index_free(index);