summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md4
-rw-r--r--examples/network/fetch.c101
-rw-r--r--include/git2/errors.h1
-rw-r--r--include/git2/remote.h10
-rw-r--r--include/git2/sys/refdb_backend.h5
-rw-r--r--src/blob.c6
-rw-r--r--src/branch.c13
-rw-r--r--src/filter.c16
-rw-r--r--src/global.c2
-rw-r--r--src/index.c24
-rw-r--r--src/path.c11
-rw-r--r--src/refdb_fs.c7
-rw-r--r--src/submodule.c2
-rw-r--r--src/thread-utils.c2
-rw-r--r--tests/index/bypath.c35
-rw-r--r--tests/refs/branches/delete.c2
-rw-r--r--tests/submodule/add.c1
-rw-r--r--tests/submodule/lookup.c18
18 files changed, 157 insertions, 103 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0a819ed7..243b696d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,10 @@ v0.23 + 1
* `git_cert` descendent types now have a proper `parent` member
+* It is the responsibility fo the refdb backend to decide what to do
+ with the reflog on ref deletion. The file-based backend must delete
+ it, a database-backed one may wish to archive it.
+
v0.23
------
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 6be12406b..bc7a5a05e 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -23,32 +23,6 @@ static int progress_cb(const char *str, int len, void *data)
return 0;
}
-static void *download(void *ptr)
-{
- struct dl_data *data = (struct dl_data *)ptr;
-
- // Connect to the remote end specifying that we want to fetch
- // information from it.
- if (git_remote_connect(data->remote, GIT_DIRECTION_FETCH, &data->fetch_opts->callbacks) < 0) {
- data->ret = -1;
- goto exit;
- }
-
- // 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, NULL, data->fetch_opts) < 0) {
- data->ret = -1;
- goto exit;
- }
-
- data->ret = 0;
-
-exit:
- data->finished = 1;
- return &data->ret;
-}
-
/**
* This function gets called for each remote-tracking branch that gets
* updated. The message we output depends on whether it's a new one or
@@ -73,6 +47,24 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
return 0;
}
+/**
+ * This gets called during the download and indexing. Here we show
+ * processed and total objects in the pack and the amount of received
+ * data. Most frontends will probably want to show a percentage and
+ * the download rate.
+ */
+static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
+{
+ if (stats->received_objects == stats->total_objects) {
+ printf("Resolving deltas %d/%d\r",
+ stats->indexed_deltas, stats->total_deltas);
+ } else if (stats->total_objects > 0) {
+ printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
+ stats->received_objects, stats->total_objects,
+ stats->indexed_objects, stats->received_bytes);
+ }
+}
+
/** Entry point for this command */
int fetch(git_repository *repo, int argc, char **argv)
{
@@ -80,9 +72,6 @@ int fetch(git_repository *repo, int argc, char **argv)
const git_transfer_progress *stats;
struct dl_data data;
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
-#ifndef _WIN32
- pthread_t worker;
-#endif
if (argc < 2) {
fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]);
@@ -99,49 +88,23 @@ int fetch(git_repository *repo, int argc, char **argv)
// Set up the callbacks (only update_tips for now)
fetch_opts.callbacks.update_tips = &update_cb;
fetch_opts.callbacks.sideband_progress = &progress_cb;
+ fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
fetch_opts.callbacks.credentials = cred_acquire_cb;
- // Set up the information for the background worker thread
- data.remote = remote;
- data.fetch_opts = &fetch_opts;
- data.ret = 0;
- data.finished = 0;
-
- stats = git_remote_stats(remote);
-
-#ifdef _WIN32
- download(&data);
-#else
- pthread_create(&worker, NULL, download, &data);
-
- // Loop while the worker thread is still running. Here we show processed
- // and total objects in the pack and the amount of received
- // data. Most frontends will probably want to show a percentage and
- // the download rate.
- do {
- usleep(10000);
-
- if (stats->received_objects == stats->total_objects) {
- printf("Resolving deltas %d/%d\r",
- stats->indexed_deltas, stats->total_deltas);
- } else if (stats->total_objects > 0) {
- printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
- 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);
-#endif
+ /**
+ * Perform the fetch with the configured refspecs from the
+ * config. Update the reflog for the updated references with
+ * "fetch".
+ */
+ if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
+ return -1;
/**
* If there are local objects (we got a thin pack), then tell
* the user how many objects we saved from having to cross the
* network.
*/
+ stats = git_remote_stats(remote);
if (stats->local_objects > 0) {
printf("\rReceived %d/%d objects in %" PRIuZ " bytes (used %d local objects)\n",
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
@@ -150,16 +113,6 @@ int fetch(git_repository *repo, int argc, char **argv)
stats->indexed_objects, stats->total_objects, stats->received_bytes);
}
- // Disconnect the underlying connection to prevent from idling.
- git_remote_disconnect(remote);
-
- // Update the references in the remote's namespace to point to the
- // right commits. This may be needed even if there was no packfile
- // to download, which can happen e.g. when the branches have been
- // changed but all the needed objects are available locally.
- if (git_remote_update_tips(remote, &fetch_opts.callbacks, 1, fetch_opts.download_tags, NULL) < 0)
- return -1;
-
git_remote_free(remote);
return 0;
diff --git a/include/git2/errors.h b/include/git2/errors.h
index a81aa05d9..e189e55f1 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -48,6 +48,7 @@ typedef enum {
GIT_EEOF = -20, /**< Unexpected EOF */
GIT_EINVALID = -21, /**< Invalid operation or input */
GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
+ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
diff --git a/include/git2/remote.h b/include/git2/remote.h
index e47f00881..444fe5276 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -511,6 +511,14 @@ typedef enum {
GIT_REMOTE_DOWNLOAD_TAGS_ALL,
} git_remote_autotag_option_t;
+/**
+ * Fetch options structure.
+ *
+ * Zero out for defaults. Initialize with `GIT_FETCH_OPTIONS_INIT` macro to
+ * correctly set the `version` field. E.g.
+ *
+ * git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
+ */
typedef struct {
int version;
@@ -739,7 +747,7 @@ GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote);
* stored here for further processing by the caller. Always free this
* strarray on successful return.
* @param repo the repository in which to rename
- * @param name the current name of the reamote
+ * @param name the current name of the remote
* @param new_name the new name the remote should bear
* @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
*/
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index d943e550f..9f2a99b7e 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -103,8 +103,9 @@ struct git_refdb_backend {
const git_signature *who, const char *message);
/**
- * Deletes the given reference from the refdb. A refdb implementation
- * must provide this function.
+ * Deletes the given reference (and if necessary its reflog)
+ * from the refdb. A refdb implementation must provide this
+ * function.
*/
int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
diff --git a/src/blob.c b/src/blob.c
index 07c4d92c8..ad0f4ac62 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -185,6 +185,12 @@ int git_blob__create_from_paths(
(error = git_repository_odb(&odb, repo)) < 0)
goto done;
+ if (S_ISDIR(st.st_mode)) {
+ giterr_set(GITERR_ODB, "cannot create blob from '%s'; it is a directory", content_path);
+ error = GIT_EDIRECTORY;
+ goto done;
+ }
+
if (out_st)
memcpy(out_st, &st, sizeof(st));
diff --git a/src/branch.c b/src/branch.c
index 791d55106..0dcc14c29 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -155,18 +155,7 @@ int git_branch_delete(git_reference *branch)
git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
goto on_error;
- if (git_reference_delete(branch) < 0)
- goto on_error;
-
- if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) {
- if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
- goto on_error;
- }
-
- error = 0;
+ error = git_reference_delete(branch);
on_error:
git_buf_free(&config_section);
diff --git a/src/filter.c b/src/filter.c
index 70c4fa382..60473e4e1 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -950,18 +950,20 @@ int git_filter_list_stream_data(
{
git_vector filter_streams = GIT_VECTOR_INIT;
git_writestream *stream_start;
- int error = 0;
+ int error = 0, close_error;
git_buf_sanitize(data);
- if ((error = stream_list_init(
- &stream_start, &filter_streams, filters, target)) == 0 &&
- (error =
- stream_start->write(stream_start, data->ptr, data->size)) == 0)
- error = stream_start->close(stream_start);
+ if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
+ goto out;
+ error = stream_start->write(stream_start, data->ptr, data->size);
+
+out:
+ close_error = stream_start->close(stream_start);
stream_list_free(&filter_streams);
- return error;
+ /* propagate the stream init or write error */
+ return error < 0 ? error : close_error;
}
int git_filter_list_stream_blob(
diff --git a/src/global.c b/src/global.c
index fc6337adc..37a47bd27 100644
--- a/src/global.c
+++ b/src/global.c
@@ -344,8 +344,8 @@ int git_libgit2_init(void)
{
int ret;
- pthread_once(&_once_init, init_once);
ret = git_atomic_inc(&git__n_inits);
+ pthread_once(&_once_init, init_once);
return init_error ? init_error : ret;
}
diff --git a/src/index.c b/src/index.c
index 5ce5522f8..501498e9e 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1236,9 +1236,29 @@ int git_index_add_bypath(git_index *index, const char *path)
assert(index && path);
- if ((ret = index_entry_init(&entry, index, path)) < 0 ||
- (ret = index_insert(index, &entry, 1, false)) < 0)
+ if ((ret = index_entry_init(&entry, index, path)) == 0)
+ ret = index_insert(index, &entry, 1, false);
+
+ /* If we were given a directory, let's see if it's a submodule */
+ if (ret < 0 && ret != GIT_EDIRECTORY)
+ return ret;
+
+ if (ret == GIT_EDIRECTORY) {
+ git_submodule *sm;
+ git_error_state err;
+
+ giterr_capture(&err, ret);
+
+ ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path);
+ if (ret == GIT_ENOTFOUND)
+ return giterr_restore(&err);
+ else
+ git__free(err.error_msg.message);
+
+ ret = git_submodule_add_to_index(sm, false);
+ git_submodule_free(sm);
return ret;
+ }
/* Adding implies conflict was resolved, move conflict entries to REUC */
if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
diff --git a/src/path.c b/src/path.c
index 16283cab2..8317aaaa7 100644
--- a/src/path.c
+++ b/src/path.c
@@ -12,6 +12,7 @@
#include "win32/posix.h"
#include "win32/buffer.h"
#include "win32/w32_util.h"
+#include "win32/version.h"
#else
#include <dirent.h>
#endif
@@ -1085,7 +1086,7 @@ int git_path_direach(
#if defined(GIT_WIN32) && !defined(__MINGW32__)
/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
- * and better. Prior versions will ignore this.
+ * and better.
*/
#ifndef FIND_FIRST_EX_LARGE_FETCH
# define FIND_FIRST_EX_LARGE_FETCH 2
@@ -1099,6 +1100,10 @@ int git_path_diriter_init(
git_win32_path path_filter;
git_buf hack = {0};
+ static int is_win7_or_later = -1;
+ if (is_win7_or_later < 0)
+ is_win7_or_later = git_has_win32_version(6, 1, 0);
+
assert(diriter && path);
memset(diriter, 0, sizeof(git_path_diriter));
@@ -1122,11 +1127,11 @@ int git_path_diriter_init(
diriter->handle = FindFirstFileExW(
path_filter,
- FindExInfoBasic,
+ is_win7_or_later ? FindExInfoBasic : FindExInfoStandard,
&diriter->current,
FindExSearchNameMatch,
NULL,
- FIND_FIRST_EX_LARGE_FETCH);
+ is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
if (diriter->handle == INVALID_HANDLE_VALUE) {
giterr_set(GITERR_OS, "Could not open directory '%s'", path);
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index e1a77f3ff..55d535eb4 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -63,6 +63,8 @@ typedef struct refdb_fs_backend {
uint32_t direach_flags;
} refdb_fs_backend;
+static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
+
static int packref_cmp(const void *a_, const void *b_)
{
const struct packref *a = a_, *b = b_;
@@ -1217,6 +1219,11 @@ static int refdb_fs_backend__delete(
if ((error = loose_lock(&file, backend, ref_name)) < 0)
return error;
+ if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
+ git_filebuf_cleanup(&file);
+ return error;
+ }
+
return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
}
diff --git a/src/submodule.c b/src/submodule.c
index f16d3c5ab..991ebc8f3 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -1658,7 +1658,7 @@ static int submodule_load_from_config(
} else {
khiter_t pos;
git_strmap *map = data->map;
- pos = git_strmap_lookup_index(map, name.ptr);
+ pos = git_strmap_lookup_index(map, path ? path : name.ptr);
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
} else {
diff --git a/src/thread-utils.c b/src/thread-utils.c
index c3baf411a..dc9b2f09e 100644
--- a/src/thread-utils.c
+++ b/src/thread-utils.c
@@ -8,7 +8,9 @@
#include "thread-utils.h"
#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
+#endif
# include <windows.h>
#elif defined(hpux) || defined(__hpux) || defined(_hpux)
# include <sys/pstat.h>
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
new file mode 100644
index 000000000..ddb766a22
--- /dev/null
+++ b/tests/index/bypath.c
@@ -0,0 +1,35 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo;
+static git_index *g_idx;
+
+void test_index_bypath__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+ cl_git_pass(git_repository_index__weakptr(&g_idx, g_repo));
+}
+
+void test_index_bypath__cleanup(void)
+{
+ g_repo = NULL;
+ g_idx = NULL;
+}
+
+void test_index_bypath__add_directory(void)
+{
+ cl_git_fail_with(GIT_EDIRECTORY, git_index_add_bypath(g_idx, "just_a_dir"));
+}
+
+void test_index_bypath__add_submodule(void)
+{
+ unsigned int status;
+ const char *sm_name = "sm_changed_head";
+
+ cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0));
+ cl_assert_equal_i(GIT_SUBMODULE_STATUS_WD_MODIFIED, status & GIT_SUBMODULE_STATUS_WD_MODIFIED);
+ cl_git_pass(git_index_add_bypath(g_idx, sm_name));
+ cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0));
+ cl_assert_equal_i(0, status & GIT_SUBMODULE_STATUS_WD_MODIFIED);
+}
diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c
index 343ff0f50..8807db231 100644
--- a/tests/refs/branches/delete.c
+++ b/tests/refs/branches/delete.c
@@ -132,6 +132,8 @@ void test_refs_branches_delete__removes_reflog(void)
cl_git_pass(git_branch_delete(branch));
git_reference_free(branch);
+ cl_assert_equal_i(false, git_reference_has_log(repo, "refs/heads/track-local"));
+
/* Reading a nonexistant reflog creates it, but it should be empty */
cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local"));
cl_assert_equal_i(0, git_reflog_entrycount(log));
diff --git a/tests/submodule/add.c b/tests/submodule/add.c
index 01625d3aa..c3b3e6364 100644
--- a/tests/submodule/add.c
+++ b/tests/submodule/add.c
@@ -4,6 +4,7 @@
#include "submodule_helpers.h"
#include "config/config_helpers.h"
#include "fileops.h"
+#include "repository.h"
static git_repository *g_repo = NULL;
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
index 38f06abbc..ecea694e5 100644
--- a/tests/submodule/lookup.c
+++ b/tests/submodule/lookup.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "submodule_helpers.h"
#include "git2/sys/repository.h"
+#include "repository.h"
#include "fileops.h"
static git_repository *g_repo = NULL;
@@ -103,8 +104,25 @@ static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
void test_submodule_lookup__foreach(void)
{
+ git_config *cfg;
sm_lookup_data data;
+
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(8, data.count);
+
memset(&data, 0, sizeof(data));
+
+ /* Change the path for a submodule so it doesn't match the name */
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+
+ cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index"));
+ cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target"));
+ cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path"));
+ cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url"));
+
+ git_config_free(cfg);
+
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
cl_assert_equal_i(8, data.count);
}