summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlhchavez <lhchavez@lhchavez.com>2021-08-29 18:50:49 -0700
committerlhchavez <lhchavez@lhchavez.com>2021-08-29 18:50:49 -0700
commit7d9ebdc840cf338bdd3d314504f1748ddc5c56d5 (patch)
tree73f6c34782f0d13f7b157dc344375f3260d64f9b
parent94008e6aae0b4f1b4fcaa2e772da99764072cd7f (diff)
parent2998a84ab644ad39b62553baf9c4b30269be7d75 (diff)
downloadlibgit2-7d9ebdc840cf338bdd3d314504f1748ddc5c56d5.tar.gz
Merge remote-tracking branch 'origin/main' into cgraph-write
-rw-r--r--.devcontainer/devcontainer.json3
-rwxr-xr-x.devcontainer/setup.sh9
-rw-r--r--.vscode/launch.json27
-rw-r--r--.vscode/tasks.json27
-rw-r--r--cmake/SelectHashes.cmake1
-rw-r--r--docs/changelog.md3
-rw-r--r--include/git2/checkout.h6
-rw-r--r--include/git2/errors.h12
-rw-r--r--include/git2/odb.h14
-rw-r--r--include/git2/sys/midx.h74
-rw-r--r--include/git2/sys/odb_backend.h7
-rw-r--r--include/git2/types.h7
-rw-r--r--src/array.h16
-rw-r--r--src/attrcache.c13
-rw-r--r--src/checkout.c3
-rw-r--r--src/clone.c75
-rw-r--r--src/diff_driver.c25
-rw-r--r--src/midx.c398
-rw-r--r--src/midx.h16
-rw-r--r--src/odb.c29
-rw-r--r--src/odb_pack.c84
-rw-r--r--src/pack.c67
-rw-r--r--src/pack.h19
-rw-r--r--src/repository.c12
-rw-r--r--src/thread.h111
-rw-r--r--src/transports/auth.c2
-rw-r--r--src/transports/auth_negotiate.c2
-rw-r--r--src/transports/auth_ntlm.c2
-rw-r--r--src/transports/http.c12
-rw-r--r--src/transports/httpclient.c1
-rw-r--r--src/transports/ssh.c8
-rw-r--r--src/transports/winhttp.c8
-rw-r--r--tests/checkout/tree.c46
-rw-r--r--tests/clone/nonetwork.c2
-rw-r--r--tests/config/read.c4
-rw-r--r--tests/pack/midx.c64
-rw-r--r--tests/remote/fetch.c14
-rw-r--r--tests/threads/atomic.c125
38 files changed, 1205 insertions, 143 deletions
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 000000000..bc6344b93
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,3 @@
+{
+ "postCreateCommand": "bash .devcontainer/setup.sh"
+}
diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh
new file mode 100755
index 000000000..c328bf3b9
--- /dev/null
+++ b/.devcontainer/setup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+
+sudo apt-get update
+sudo apt-get -y --no-install-recommends install cmake
+
+mkdir build
+cd build
+cmake .. \ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..d47d93aab
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/build/libgit2_clar",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${fileDirname}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..64142d341
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,27 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build",
+ "type": "shell",
+ "command": "cd build && cmake --build . --parallel",
+ "group": "build",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ },
+ {
+ "label": "Run Tests",
+ "type": "shell",
+ "command": "build/libgit2_clar -v",
+ "group": "test",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ }
+ ]
+ } \ No newline at end of file
diff --git a/cmake/SelectHashes.cmake b/cmake/SelectHashes.cmake
index 06672ab03..092cdfd05 100644
--- a/cmake/SelectHashes.cmake
+++ b/cmake/SelectHashes.cmake
@@ -56,6 +56,7 @@ ELSE()
MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
ENDIF()
+list(APPEND SRC_SHA1 "hash/sha1.h")
list(SORT SRC_SHA1)
ADD_FEATURE_INFO(SHA ON "using ${USE_SHA1}")
diff --git a/docs/changelog.md b/docs/changelog.md
index edd2c3f42..1bbbe607d 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -509,6 +509,9 @@ with v0.28.0.
The breaking change is that the `username` member of the underlying struct
is now hidden, and a new `git_cred_get_username` function has been provided.
+* Some errors of class `GIT_ERROR_NET` now have class `GIT_ERROR_HTTP`.
+ Most authentication failures now have error code `GIT_EAUTH` instead of `GIT_ERROR`.
+
### Breaking CMake configuration changes
* The CMake option to use a system http-parser library, instead of the
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index ca6f17aa6..c7aeee431 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -178,6 +178,12 @@ typedef enum {
GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23),
/**
+ * Show what would be done by a checkout. Stop after sending
+ * notifications; don't update the working directory or index.
+ */
+ GIT_CHECKOUT_DRY_RUN = (1u << 24),
+
+ /**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 8887b3299..de51582d5 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -42,14 +42,14 @@ typedef enum {
GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */
GIT_ELOCKED = -14, /**< Lock file prevented operation */
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
- GIT_EAUTH = -16, /**< Authentication error */
- GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
+ GIT_EAUTH = -16, /**< Authentication error */
+ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */
- GIT_EPEEL = -19, /**< The requested peel operation is not possible */
- GIT_EEOF = -20, /**< Unexpected EOF */
- GIT_EINVALID = -21, /**< Invalid operation or input */
+ GIT_EPEEL = -19, /**< The requested peel operation is not possible */
+ 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_EDIRECTORY = -23, /**< The operation is not valid for a directory */
GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */
diff --git a/include/git2/odb.h b/include/git2/odb.h
index 702e1bd30..dd484553f 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -391,6 +391,20 @@ GIT_EXTERN(int) git_odb_write_pack(
void *progress_payload);
/**
+ * Write a `multi-pack-index` file from all the `.pack` files in the ODB.
+ *
+ * If the ODB layer understands pack files, then this will create a file called
+ * `multi-pack-index` next to the `.pack` and `.idx` files, which will contain
+ * an index of all objects stored in `.pack` files. This will allow for
+ * O(log n) lookup for n objects (regardless of how many packfiles there
+ * exist).
+ *
+ * @param db object database where the `multi-pack-index` file will be written.
+ */
+GIT_EXTERN(int) git_odb_write_multi_pack_index(
+ git_odb *db);
+
+/**
* Determine the object-ID (sha1 hash) of a data buffer
*
* The resulting SHA-1 OID will be the identifier for the data
diff --git a/include/git2/sys/midx.h b/include/git2/sys/midx.h
new file mode 100644
index 000000000..e3d749829
--- /dev/null
+++ b/include/git2/sys/midx.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_midx_h__
+#define INCLUDE_sys_git_midx_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/midx.h
+ * @brief Git multi-pack-index routines
+ * @defgroup git_midx Git multi-pack-index routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new writer for `multi-pack-index` files.
+ *
+ * @param out location to store the writer pointer.
+ * @param pack_dir the directory where the `.pack` and `.idx` files are. The
+ * `multi-pack-index` file will be written in this directory, too.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_new(
+ git_midx_writer **out,
+ const char *pack_dir);
+
+/**
+ * Free the multi-pack-index writer and its resources.
+ *
+ * @param w the writer to free. If NULL no action is taken.
+ */
+GIT_EXTERN(void) git_midx_writer_free(git_midx_writer *w);
+
+/**
+ * Add an `.idx` file to the writer.
+ *
+ * @param w the writer
+ * @param idx_path the path of an `.idx` file.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_add(
+ git_midx_writer *w,
+ const char *idx_path);
+
+/**
+ * Write a `multi-pack-index` file to a file.
+ *
+ * @param w the writer
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_commit(
+ git_midx_writer *w);
+
+/**
+ * Dump the contents of the `multi-pack-index` to an in-memory buffer.
+ *
+ * @param midx Buffer where to store the contents of the `multi-pack-index`.
+ * @param w the writer
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_dump(
+ git_buf *midx,
+ git_midx_writer *w);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h
index 4dba460af..9ae0ed9b3 100644
--- a/include/git2/sys/odb_backend.h
+++ b/include/git2/sys/odb_backend.h
@@ -85,6 +85,13 @@ struct git_odb_backend {
git_indexer_progress_cb progress_cb, void *progress_payload);
/**
+ * If the backend supports pack files, this will create a
+ * `multi-pack-index` file which will contain an index of all objects
+ * across all the `.pack` files.
+ */
+ int GIT_CALLBACK(writemidx)(git_odb_backend *);
+
+ /**
* "Freshens" an already existing object, updating its last-used
* time. This occurs when `git_odb_write` was called, but the
* object already existed (and will not be re-written). The
diff --git a/include/git2/types.h b/include/git2/types.h
index 4d5c72324..aac8e42e2 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -96,8 +96,8 @@ typedef struct git_odb_stream git_odb_stream;
/** A stream to write a packfile to the ODB */
typedef struct git_odb_writepack git_odb_writepack;
-/** a writer for commit-graph files. */
-typedef struct git_commit_graph_writer git_commit_graph_writer;
+/** a writer for multi-pack-index files. */
+typedef struct git_midx_writer git_midx_writer;
/** An open refs database handle. */
typedef struct git_refdb git_refdb;
@@ -108,6 +108,9 @@ typedef struct git_refdb_backend git_refdb_backend;
/** A git commit-graph */
typedef struct git_commit_graph git_commit_graph;
+/** a writer for commit-graph files. */
+typedef struct git_commit_graph_writer git_commit_graph_writer;
+
/**
* Representation of an existing git repository,
* including all its object contents
diff --git a/src/array.h b/src/array.h
index e649d845b..e97688b36 100644
--- a/src/array.h
+++ b/src/array.h
@@ -41,8 +41,8 @@
typedef git_array_t(char) git_array_generic_t;
-/* use a generic array for growth so this can return the new item */
-GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
+/* use a generic array for growth, return 0 on success */
+GIT_INLINE(int) git_array_grow(void *_a, size_t item_size)
{
volatile git_array_generic_t *a = _a;
size_t new_size;
@@ -59,18 +59,18 @@ GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
goto on_oom;
- a->ptr = new_array; a->asize = new_size; a->size++;
- return a->ptr + (a->size - 1) * item_size;
+ a->ptr = new_array;
+ a->asize = new_size;
+ return 0;
on_oom:
git_array_clear(*a);
- return NULL;
+ return -1;
}
#define git_array_alloc(a) \
- (((a).size >= (a).asize) ? \
- git_array_grow(&(a), sizeof(*(a).ptr)) : \
- ((a).ptr ? &(a).ptr[(a).size++] : (void *)NULL))
+ (((a).size < (a).asize || git_array_grow(&(a), sizeof(*(a).ptr)) == 0) ? \
+ &(a).ptr[(a).size++] : (void *)NULL)
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL)
diff --git a/src/attrcache.c b/src/attrcache.c
index 7fe2bfbdb..2b36b7a9c 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -127,7 +127,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
{
int error = 0;
git_attr_file_entry *entry;
- git_attr_file *old = NULL;
+ git_attr_file *oldfile = NULL;
if (!file)
return 0;
@@ -136,13 +136,13 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
- old = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
+ oldfile = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
attr_cache_unlock(cache);
- if (old) {
- GIT_REFCOUNT_OWN(old, NULL);
- git_attr_file__free(old);
+ if (oldfile == file) {
+ GIT_REFCOUNT_OWN(file, NULL);
+ git_attr_file__free(file);
}
return error;
@@ -401,8 +401,7 @@ int git_attr_cache__init(git_repository *repo)
(ret = git_pool_init(&cache->pool, 1)) < 0)
goto cancel;
- cache = git_atomic_compare_and_swap(&repo->attrcache, NULL, cache);
- if (cache)
+ if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL)
goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg);
diff --git a/src/checkout.c b/src/checkout.c
index 31d473ec8..713159152 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -2622,6 +2622,9 @@ int git_checkout_iterator(
if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0)
goto cleanup;
+ if (data.strategy & GIT_CHECKOUT_DRY_RUN)
+ goto cleanup;
+
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
counts[CHECKOUT_ACTION__REMOVE_CONFLICT] +
counts[CHECKOUT_ACTION__UPDATE_BLOB] +
diff --git a/src/clone.c b/src/clone.c
index 6d5ebf6b0..e29a4aab3 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -162,37 +162,6 @@ done:
return error;
}
-static int update_remote_head_byname(
- git_repository *repo,
- const char *remote_name,
- const char *tracking_branch_name,
- const char *reflog_message)
-{
- git_buf tracking_head_name = GIT_BUF_INIT;
- git_reference *remote_head = NULL;
- int error;
-
- if ((error = git_buf_printf(&tracking_head_name,
- "%s%s/%s",
- GIT_REFS_REMOTES_DIR,
- remote_name,
- GIT_HEAD_FILE)) < 0)
- goto cleanup;
-
- error = git_reference_symbolic_create(
- &remote_head,
- repo,
- git_buf_cstr(&tracking_head_name),
- tracking_branch_name,
- true,
- reflog_message);
-
-cleanup:
- git_reference_free(remote_head);
- git_buf_dispose(&tracking_head_name);
- return error;
-}
-
static int update_remote_head(
git_repository *repo,
git_remote *remote,
@@ -200,7 +169,9 @@ static int update_remote_head(
const char *reflog_message)
{
git_refspec *refspec;
- git_buf tracking_branch_name = GIT_BUF_INIT;
+ git_reference *remote_head = NULL;
+ git_buf remote_head_name = GIT_BUF_INIT;
+ git_buf remote_branch_name = GIT_BUF_INIT;
int error;
/* Determine the remote tracking ref name from the local branch */
@@ -213,19 +184,30 @@ static int update_remote_head(
}
if ((error = git_refspec_transform(
- &tracking_branch_name,
+ &remote_branch_name,
refspec,
git_buf_cstr(target))) < 0)
goto cleanup;
- error = update_remote_head_byname(
- repo,
+ if ((error = git_buf_printf(&remote_head_name,
+ "%s%s/%s",
+ GIT_REFS_REMOTES_DIR,
git_remote_name(remote),
- git_buf_cstr(&tracking_branch_name),
+ GIT_HEAD_FILE)) < 0)
+ goto cleanup;
+
+ error = git_reference_symbolic_create(
+ &remote_head,
+ repo,
+ git_buf_cstr(&remote_head_name),
+ git_buf_cstr(&remote_branch_name),
+ true,
reflog_message);
cleanup:
- git_buf_dispose(&tracking_branch_name);
+ git_reference_free(remote_head);
+ git_buf_dispose(&remote_branch_name);
+ git_buf_dispose(&remote_head_name);
return error;
}
@@ -277,19 +259,20 @@ cleanup:
static int update_head_to_branch(
git_repository *repo,
- const char *remote_name,
+ git_remote *remote,
const char *branch,
const char *reflog_message)
{
int retcode;
git_buf remote_branch_name = GIT_BUF_INIT;
git_reference* remote_ref = NULL;
+ git_buf default_branch = GIT_BUF_INIT;
- GIT_ASSERT_ARG(remote_name);
+ GIT_ASSERT_ARG(remote);
GIT_ASSERT_ARG(branch);
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
- remote_name, branch)) < 0 )
+ git_remote_name(remote), branch)) < 0 )
goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
@@ -299,11 +282,18 @@ static int update_head_to_branch(
reflog_message)) < 0)
goto cleanup;
- retcode = update_remote_head_byname(repo, remote_name, remote_branch_name.ptr, reflog_message);
+ if ((retcode = git_remote_default_branch(&default_branch, remote)) < 0)
+ goto cleanup;
+
+ if (!git_remote__matching_refspec(remote, git_buf_cstr(&default_branch)))
+ goto cleanup;
+
+ retcode = update_remote_head(repo, remote, &default_branch, reflog_message);
cleanup:
git_reference_free(remote_ref);
git_buf_dispose(&remote_branch_name);
+ git_buf_dispose(&default_branch);
return retcode;
}
@@ -388,8 +378,7 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c
int error;
if (branch)
- error = update_head_to_branch(repo, git_remote_name(remote), branch,
- reflog_message);
+ error = update_head_to_branch(repo, remote, branch, reflog_message);
/* Point HEAD to the same ref as the remote's head */
else
error = update_head_to_remote(repo, remote, reflog_message);
diff --git a/src/diff_driver.c b/src/diff_driver.c
index 8e9131feb..a3892d35e 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -141,18 +141,23 @@ static int diff_driver_funcname(const git_config_entry *entry, void *payload)
static git_diff_driver_registry *git_repository_driver_registry(
git_repository *repo)
{
- if (!repo->diff_drivers) {
- git_diff_driver_registry *reg = git_diff_driver_registry_new();
- reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, reg);
+ git_diff_driver_registry *reg = git_atomic_load(repo->diff_drivers), *newreg;
+ if (reg)
+ return reg;
- if (reg != NULL) /* if we race, free losing allocation */
- git_diff_driver_registry_free(reg);
- }
-
- if (!repo->diff_drivers)
+ newreg = git_diff_driver_registry_new();
+ if (!newreg) {
git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry");
-
- return repo->diff_drivers;
+ return newreg;
+ }
+ reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, newreg);
+ if (!reg) {
+ reg = newreg;
+ } else {
+ /* if we race, free losing allocation */
+ git_diff_driver_registry_free(newreg);
+ }
+ return reg;
}
static int diff_driver_alloc(
diff --git a/src/midx.c b/src/midx.c
index d6bb9c1e7..9aab8b588 100644
--- a/src/midx.c
+++ b/src/midx.c
@@ -7,11 +7,15 @@
#include "midx.h"
+#include "array.h"
#include "buffer.h"
+#include "filebuf.h"
#include "futils.h"
#include "hash.h"
#include "odb.h"
#include "pack.h"
+#include "path.h"
+#include "repository.h"
#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
#define MIDX_VERSION 1
@@ -36,6 +40,8 @@ struct git_midx_chunk {
size_t length;
};
+typedef int (*midx_write_cb)(const char *buf, size_t size, void *cb_data);
+
static int midx_error(const char *message)
{
git_error_set(GIT_ERROR_ODB, "invalid multi-pack-index file - %s", message);
@@ -475,3 +481,395 @@ void git_midx_free(git_midx_file *idx)
git_midx_close(idx);
git__free(idx);
}
+
+static int packfile__cmp(const void *a_, const void *b_)
+{
+ const struct git_pack_file *a = a_;
+ const struct git_pack_file *b = b_;
+
+ return strcmp(a->pack_name, b->pack_name);
+}
+
+int git_midx_writer_new(
+ git_midx_writer **out,
+ const char *pack_dir)
+{
+ git_midx_writer *w = git__calloc(1, sizeof(git_midx_writer));
+ GIT_ERROR_CHECK_ALLOC(w);
+
+ if (git_buf_sets(&w->pack_dir, pack_dir) < 0) {
+ git__free(w);
+ return -1;
+ }
+ git_path_squash_slashes(&w->pack_dir);
+
+ if (git_vector_init(&w->packs, 0, packfile__cmp) < 0) {
+ git_buf_dispose(&w->pack_dir);
+ git__free(w);
+ return -1;
+ }
+
+ *out = w;
+ return 0;
+}
+
+void git_midx_writer_free(git_midx_writer *w)
+{
+ struct git_pack_file *p;
+ size_t i;
+
+ if (!w)
+ return;
+
+ git_vector_foreach (&w->packs, i, p)
+ git_mwindow_put_pack(p);
+ git_vector_free(&w->packs);
+ git_buf_dispose(&w->pack_dir);
+ git__free(w);
+}
+
+int git_midx_writer_add(
+ git_midx_writer *w,
+ const char *idx_path)
+{
+ git_buf idx_path_buf = GIT_BUF_INIT;
+ int error;
+ struct git_pack_file *p;
+
+ error = git_path_prettify(&idx_path_buf, idx_path, git_buf_cstr(&w->pack_dir));
+ if (error < 0)
+ return error;
+
+ error = git_mwindow_get_pack(&p, git_buf_cstr(&idx_path_buf));
+ git_buf_dispose(&idx_path_buf);
+ if (error < 0)
+ return error;
+
+ error = git_vector_insert(&w->packs, p);
+ if (error < 0) {
+ git_mwindow_put_pack(p);
+ return error;
+ }
+
+ return 0;
+}
+
+typedef git_array_t(git_midx_entry) object_entry_array_t;
+
+struct object_entry_cb_state {
+ uint32_t pack_index;
+ object_entry_array_t *object_entries_array;
+};
+
+static int object_entry__cb(const git_oid *oid, off64_t offset, void *data)
+{
+ struct object_entry_cb_state *state = (struct object_entry_cb_state *)data;
+
+ git_midx_entry *entry = git_array_alloc(*state->object_entries_array);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ git_oid_cpy(&entry->sha1, oid);
+ entry->offset = offset;
+ entry->pack_index = state->pack_index;
+
+ return 0;
+}
+
+static int object_entry__cmp(const void *a_, const void *b_)
+{
+ const git_midx_entry *a = (const git_midx_entry *)a_;
+ const git_midx_entry *b = (const git_midx_entry *)b_;
+
+ return git_oid_cmp(&a->sha1, &b->sha1);
+}
+
+static int write_offset(off64_t offset, midx_write_cb write_cb, void *cb_data)
+{
+ int error;
+ uint32_t word;
+
+ word = htonl((uint32_t)((offset >> 32) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ word = htonl((uint32_t)((offset >> 0) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int write_chunk_header(int chunk_id, off64_t offset, midx_write_cb write_cb, void *cb_data)
+{
+ uint32_t word = htonl(chunk_id);
+ int error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ return write_offset(offset, write_cb, cb_data);
+
+ return 0;
+}
+
+static int midx_write_buf(const char *buf, size_t size, void *data)
+{
+ git_buf *b = (git_buf *)data;
+ return git_buf_put(b, buf, size);
+}
+
+struct midx_write_hash_context {
+ midx_write_cb write_cb;
+ void *cb_data;
+ git_hash_ctx *ctx;
+};
+
+static int midx_write_hash(const char *buf, size_t size, void *data)
+{
+ struct midx_write_hash_context *ctx = (struct midx_write_hash_context *)data;
+ int error;
+
+ error = git_hash_update(ctx->ctx, buf, size);
+ if (error < 0)
+ return error;
+
+ return ctx->write_cb(buf, size, ctx->cb_data);
+}
+
+static int midx_write(
+ git_midx_writer *w,
+ midx_write_cb write_cb,
+ void *cb_data)
+{
+ int error = 0;
+ size_t i;
+ struct git_pack_file *p;
+ struct git_midx_header hdr = {0};
+ uint32_t oid_fanout_count;
+ uint32_t object_large_offsets_count;
+ uint32_t oid_fanout[256];
+ off64_t offset;
+ git_buf packfile_names = GIT_BUF_INIT,
+ oid_lookup = GIT_BUF_INIT,
+ object_offsets = GIT_BUF_INIT,
+ object_large_offsets = GIT_BUF_INIT;
+ git_oid idx_checksum = {{0}};
+ git_midx_entry *entry;
+ object_entry_array_t object_entries_array = GIT_ARRAY_INIT;
+ git_vector object_entries = GIT_VECTOR_INIT;
+ git_hash_ctx ctx;
+ struct midx_write_hash_context hash_cb_data = {0};
+
+ hdr.signature = htonl(MIDX_SIGNATURE);
+ hdr.version = MIDX_VERSION;
+ hdr.object_id_version = MIDX_OBJECT_ID_VERSION;
+ hdr.base_midx_files = 0;
+
+ hash_cb_data.write_cb = write_cb;
+ hash_cb_data.cb_data = cb_data;
+ hash_cb_data.ctx = &ctx;
+
+ error = git_hash_ctx_init(&ctx);
+ if (error < 0)
+ return error;
+ cb_data = &hash_cb_data;
+ write_cb = midx_write_hash;
+
+ git_vector_sort(&w->packs);
+ git_vector_foreach (&w->packs, i, p) {
+ git_buf relative_index = GIT_BUF_INIT;
+ struct object_entry_cb_state state = {0};
+ size_t path_len;
+
+ state.pack_index = (uint32_t)i;
+ state.object_entries_array = &object_entries_array;
+
+ error = git_buf_sets(&relative_index, p->pack_name);
+ if (error < 0)
+ goto cleanup;
+ error = git_path_make_relative(&relative_index, git_buf_cstr(&w->pack_dir));
+ if (error < 0) {
+ git_buf_dispose(&relative_index);
+ goto cleanup;
+ }
+ path_len = git_buf_len(&relative_index);
+ if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(&relative_index), ".pack") != 0) {
+ git_buf_dispose(&relative_index);
+ git_error_set(GIT_ERROR_INVALID, "invalid packfile name: '%s'", p->pack_name);
+ error = -1;
+ goto cleanup;
+ }
+ path_len -= strlen(".pack");
+
+ git_buf_put(&packfile_names, git_buf_cstr(&relative_index), path_len);
+ git_buf_puts(&packfile_names, ".idx");
+ git_buf_putc(&packfile_names, '\0');
+ git_buf_dispose(&relative_index);
+
+ error = git_pack_foreach_entry_offset(p, object_entry__cb, &state);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Sort the object entries. */
+ error = git_vector_init(&object_entries, git_array_size(object_entries_array), object_entry__cmp);
+ if (error < 0)
+ goto cleanup;
+ git_array_foreach (object_entries_array, i, entry)
+ error = git_vector_set(NULL, &object_entries, i, entry);
+ git_vector_set_sorted(&object_entries, 0);
+ git_vector_sort(&object_entries);
+ git_vector_uniq(&object_entries, NULL);
+
+ /* Pad the packfile names so it is a multiple of four. */
+ while (git_buf_len(&packfile_names) & 3)
+ git_buf_putc(&packfile_names, '\0');
+
+ /* Fill the OID Fanout table. */
+ oid_fanout_count = 0;
+ for (i = 0; i < 256; i++) {
+ while (oid_fanout_count < git_vector_length(&object_entries) &&
+ ((const git_midx_entry *)git_vector_get(&object_entries, oid_fanout_count))->sha1.id[0] <= i)
+ ++oid_fanout_count;
+ oid_fanout[i] = htonl(oid_fanout_count);
+ }
+
+ /* Fill the OID Lookup table. */
+ git_vector_foreach (&object_entries, i, entry) {
+ error = git_buf_put(&oid_lookup, (const char *)&entry->sha1, sizeof(entry->sha1));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Fill the Object Offsets and Object Large Offsets tables. */
+ object_large_offsets_count = 0;
+ git_vector_foreach (&object_entries, i, entry) {
+ uint32_t word;
+
+ word = htonl((uint32_t)entry->pack_index);
+ error = git_buf_put(&object_offsets, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ if (entry->offset >= 0x80000000l) {
+ word = htonl(0x80000000u | object_large_offsets_count++);
+ error = write_offset(entry->offset, midx_write_buf, &object_large_offsets);
+ } else {
+ word = htonl((uint32_t)entry->offset & 0x7fffffffu);
+ }
+ error = git_buf_put(&object_offsets, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Write the header. */
+ hdr.packfiles = htonl((uint32_t)git_vector_length(&w->packs));
+ hdr.chunks = 4;
+ if (git_buf_len(&object_large_offsets) > 0)
+ hdr.chunks++;
+ error = write_cb((const char *)&hdr, sizeof(hdr), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write the chunk headers. */
+ offset = sizeof(hdr) + (hdr.chunks + 1) * 12;
+ error = write_chunk_header(MIDX_PACKFILE_NAMES_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&packfile_names);
+ error = write_chunk_header(MIDX_OID_FANOUT_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += sizeof(oid_fanout);
+ error = write_chunk_header(MIDX_OID_LOOKUP_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&oid_lookup);
+ error = write_chunk_header(MIDX_OBJECT_OFFSETS_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&object_offsets);
+ if (git_buf_len(&object_large_offsets) > 0) {
+ error = write_chunk_header(MIDX_OBJECT_LARGE_OFFSETS_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_buf_len(&object_large_offsets);
+ }
+ error = write_chunk_header(0, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write all the chunks. */
+ error = write_cb(git_buf_cstr(&packfile_names), git_buf_len(&packfile_names), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&oid_lookup), git_buf_len(&oid_lookup), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&object_offsets), git_buf_len(&object_offsets), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_buf_cstr(&object_large_offsets), git_buf_len(&object_large_offsets), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Finalize the checksum and write the trailer. */
+ error = git_hash_final(&idx_checksum, &ctx);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((const char *)&idx_checksum, sizeof(idx_checksum), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_array_clear(object_entries_array);
+ git_vector_free(&object_entries);
+ git_buf_dispose(&packfile_names);
+ git_buf_dispose(&oid_lookup);
+ git_buf_dispose(&object_offsets);
+ git_buf_dispose(&object_large_offsets);
+ git_hash_ctx_cleanup(&ctx);
+ return error;
+}
+
+static int midx_write_filebuf(const char *buf, size_t size, void *data)
+{
+ git_filebuf *f = (git_filebuf *)data;
+ return git_filebuf_write(f, buf, size);
+}
+
+int git_midx_writer_commit(
+ git_midx_writer *w)
+{
+ int error;
+ int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER;
+ git_buf midx_path = GIT_BUF_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+
+ error = git_buf_joinpath(&midx_path, git_buf_cstr(&w->pack_dir), "multi-pack-index");
+ if (error < 0)
+ return error;
+
+ if (git_repository__fsync_gitdir)
+ filebuf_flags |= GIT_FILEBUF_FSYNC;
+ error = git_filebuf_open(&output, git_buf_cstr(&midx_path), filebuf_flags, 0644);
+ git_buf_dispose(&midx_path);
+ if (error < 0)
+ return error;
+
+ error = midx_write(w, midx_write_filebuf, &output);
+ if (error < 0) {
+ git_filebuf_cleanup(&output);
+ return error;
+ }
+
+ return git_filebuf_commit(&output);
+}
+
+int git_midx_writer_dump(
+ git_buf *midx,
+ git_midx_writer *w)
+{
+ return midx_write(w, midx_write_buf, midx);
+}
diff --git a/src/midx.h b/src/midx.h
index 543ff2178..4ce17ce73 100644
--- a/src/midx.h
+++ b/src/midx.h
@@ -12,6 +12,8 @@
#include <ctype.h>
+#include "git2/sys/midx.h"
+
#include "map.h"
#include "mwindow.h"
#include "odb.h"
@@ -67,6 +69,20 @@ typedef struct git_midx_entry {
git_oid sha1;
} git_midx_entry;
+/*
+ * A writer for `multi-pack-index` files.
+ */
+struct git_midx_writer {
+ /*
+ * The path of the directory where the .pack/.idx files are stored. The
+ * `multi-pack-index` file will be written to the same directory.
+ */
+ git_buf pack_dir;
+
+ /* The list of `git_pack_file`s. */
+ git_vector packs;
+};
+
int git_midx_open(
git_midx_file **idx_out,
const char *path);
diff --git a/src/odb.c b/src/odb.c
index e3a5381e6..7834e5f15 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -1703,6 +1703,35 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_
return error;
}
+int git_odb_write_multi_pack_index(git_odb *db)
+{
+ size_t i, writes = 0;
+ int error = GIT_ERROR;
+
+ GIT_ASSERT_ARG(db);
+
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
+
+ if (b->writemidx != NULL) {
+ ++writes;
+ error = b->writemidx(b);
+ }
+ }
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ if (error < 0 && !writes)
+ error = git_odb__error_unsupported_in_backend("write multi-pack-index");
+
+ return error;
+}
+
void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len)
{
GIT_UNUSED(backend);
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 3df8a4267..f4cb9a558 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -402,7 +402,6 @@ static int process_multi_pack_index_pack(
const char *packfile_name)
{
int error;
- size_t cmp_len = strlen(packfile_name);
struct git_pack_file *pack;
size_t found_position;
git_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT;
@@ -411,12 +410,11 @@ static int process_multi_pack_index_pack(
if (error < 0)
return error;
- /* This is ensured by midx__parse_packfile_name() */
- if (cmp_len <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0)
+ /* This is ensured by midx_parse_packfile_name() */
+ if (git_buf_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0)
return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
- cmp_len -= strlen(".idx");
- git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), cmp_len);
+ git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), git_buf_len(&pack_path) - strlen(".idx"));
if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) {
/* Pack was found in the packs list. Moving it to the midx_packs list. */
@@ -744,6 +742,81 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
return 0;
}
+static int get_idx_path(
+ git_buf *idx_path,
+ struct pack_backend *backend,
+ struct git_pack_file *p)
+{
+ size_t path_len;
+ int error;
+
+ error = git_path_prettify(idx_path, p->pack_name, backend->pack_folder);
+ if (error < 0)
+ return error;
+ path_len = git_buf_len(idx_path);
+ if (path_len <= strlen(".pack") || git__suffixcmp(git_buf_cstr(idx_path), ".pack") != 0)
+ return git_odb__error_notfound("packfile does not end in .pack", NULL, 0);
+ path_len -= strlen(".pack");
+ error = git_buf_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int pack_backend__writemidx(git_odb_backend *_backend)
+{
+ struct pack_backend *backend;
+ git_midx_writer *w = NULL;
+ struct git_pack_file *p;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(_backend);
+
+ backend = (struct pack_backend *)_backend;
+
+ error = git_midx_writer_new(&w, backend->pack_folder);
+ if (error < 0)
+ return error;
+
+ git_vector_foreach(&backend->midx_packs, i, p) {
+ git_buf idx_path = GIT_BUF_INIT;
+ error = get_idx_path(&idx_path, backend, p);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_add(w, git_buf_cstr(&idx_path));
+ git_buf_dispose(&idx_path);
+ if (error < 0)
+ goto cleanup;
+ }
+ git_vector_foreach(&backend->packs, i, p) {
+ git_buf idx_path = GIT_BUF_INIT;
+ error = get_idx_path(&idx_path, backend, p);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_add(w, git_buf_cstr(&idx_path));
+ git_buf_dispose(&idx_path);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /*
+ * Invalidate the previous midx before writing the new one.
+ */
+ error = remove_multi_pack_index(backend);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_commit(w);
+ if (error < 0)
+ goto cleanup;
+ error = refresh_multi_pack_index(backend);
+
+cleanup:
+ git_midx_writer_free(w);
+ return error;
+}
+
static void pack_backend__free(git_odb_backend *_backend)
{
struct pack_backend *backend;
@@ -792,6 +865,7 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
backend->parent.refresh = &pack_backend__refresh;
backend->parent.foreach = &pack_backend__foreach;
backend->parent.writepack = &pack_backend__writepack;
+ backend->parent.writemidx = &pack_backend__writemidx;
backend->parent.freshen = &pack_backend__freshen;
backend->parent.free = &pack_backend__free;
diff --git a/src/pack.c b/src/pack.c
index 5d284ca9e..94b1ecd9d 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -1368,6 +1368,73 @@ int git_pack_foreach_entry(
return error;
}
+int git_pack_foreach_entry_offset(
+ struct git_pack_file *p,
+ git_pack_foreach_entry_offset_cb cb,
+ void *data)
+{
+ const unsigned char *index;
+ off64_t current_offset;
+ const git_oid *current_oid;
+ uint32_t i;
+ int error = 0;
+
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for git_pack_foreach_entry_offset");
+
+ index = p->index_map.data;
+ if (index == NULL) {
+ if ((error = pack_index_open_locked(p)) < 0)
+ goto cleanup;
+
+ GIT_ASSERT(p->index_map.data);
+ index = p->index_map.data;
+ }
+
+ if (p->index_version > 1)
+ index += 8;
+
+ index += 4 * 256;
+
+ /* all offsets should have been validated by pack_index_check_locked */
+ if (p->index_version > 1) {
+ const unsigned char *offsets = index + 24 * p->num_objects;
+ const unsigned char *large_offset_ptr;
+ const unsigned char *large_offsets = index + 28 * p->num_objects;
+ const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - 20;
+ for (i = 0; i < p->num_objects; i++) {
+ current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i));
+ if (current_offset & 0x80000000) {
+ large_offset_ptr = large_offsets + (current_offset & 0x7fffffff) * 8;
+ if (large_offset_ptr >= large_offsets_end) {
+ error = packfile_error("invalid large offset");
+ goto cleanup;
+ }
+ current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) |
+ ntohl(*((uint32_t *)(large_offset_ptr + 4)));
+ }
+ current_oid = (const git_oid *)(index + 20 * i);
+ if ((error = cb(current_oid, current_offset, data)) != 0) {
+ error = git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ } else {
+ for (i = 0; i < p->num_objects; i++) {
+ current_offset = ntohl(*(const uint32_t *)(index + 24 * i));
+ current_oid = (const git_oid *)(index + 24 * i + 4);
+ if ((error = cb(current_oid, current_offset, data)) != 0) {
+ error = git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ git_mutex_unlock(&p->lock);
+ return error;
+}
+
int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
unsigned hi, const unsigned char *oid_prefix)
{
diff --git a/src/pack.h b/src/pack.h
index 1d077240d..bf279c6b6 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -20,6 +20,14 @@
#include "oidmap.h"
#include "zstream.h"
+/**
+ * Function type for callbacks from git_pack_foreach_entry_offset.
+ */
+typedef int git_pack_foreach_entry_offset_cb(
+ const git_oid *id,
+ off64_t offset,
+ void *payload);
+
#define GIT_PACK_FILE_MODE 0444
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
@@ -176,5 +184,16 @@ int git_pack_foreach_entry(
struct git_pack_file *p,
git_odb_foreach_cb cb,
void *data);
+/**
+ * Similar to git_pack_foreach_entry, but:
+ * - It also provides the offset of the object within the
+ * packfile.
+ * - It does not sort the objects in any order.
+ * - It retains the lock while invoking the callback.
+ */
+int git_pack_foreach_entry_offset(
+ struct git_pack_file *p,
+ git_pack_foreach_entry_offset_cb cb,
+ void *data);
#endif
diff --git a/src/repository.c b/src/repository.c
index 692f71861..9ed46bf4d 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1093,8 +1093,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
if (!error) {
GIT_REFCOUNT_OWN(config, repo);
- config = git_atomic_compare_and_swap(&repo->_config, NULL, config);
- if (config != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_config, NULL, config) != NULL) {
GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config);
}
@@ -1164,8 +1163,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
return error;
}
- odb = git_atomic_compare_and_swap(&repo->_odb, NULL, odb);
- if (odb != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_odb, NULL, odb) != NULL) {
GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb);
}
@@ -1209,8 +1207,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
if (!error) {
GIT_REFCOUNT_OWN(refdb, repo);
- refdb = git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb);
- if (refdb != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb) != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb);
}
@@ -1257,8 +1254,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
if (!error) {
GIT_REFCOUNT_OWN(index, repo);
- index = git_atomic_compare_and_swap(&repo->_index, NULL, index);
- if (index != NULL) {
+ if (git_atomic_compare_and_swap(&repo->_index, NULL, index) != NULL) {
GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index);
}
diff --git a/src/thread.h b/src/thread.h
index b4f869243..4b091c0a2 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -74,6 +74,9 @@ typedef git_atomic32 git_atomic_ssize;
# include "unix/pthread.h"
#endif
+/*
+ * Atomically sets the contents of *a to be val.
+ */
GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
{
#if defined(GIT_WIN32)
@@ -87,6 +90,10 @@ GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
#endif
}
+/*
+ * Atomically increments the contents of *a by 1, and stores the result back into *a.
+ * @return the result of the operation.
+ */
GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
{
#if defined(GIT_WIN32)
@@ -100,10 +107,14 @@ GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
#endif
}
+/*
+ * Atomically adds the contents of *a and addend, and stores the result back into *a.
+ * @return the result of the operation.
+ */
GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
{
#if defined(GIT_WIN32)
- return InterlockedExchangeAdd(&a->val, addend);
+ return InterlockedAdd(&a->val, addend);
#elif defined(GIT_BUILTIN_ATOMIC)
return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC)
@@ -113,6 +124,10 @@ GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
#endif
}
+/*
+ * Atomically decrements the contents of *a by 1, and stores the result back into *a.
+ * @return the result of the operation.
+ */
GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
{
#if defined(GIT_WIN32)
@@ -126,6 +141,10 @@ GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
#endif
}
+/*
+ * Atomically gets the contents of *a.
+ * @return the contents of *a.
+ */
GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
{
#if defined(GIT_WIN32)
@@ -143,16 +162,13 @@ GIT_INLINE(void *) git_atomic__compare_and_swap(
void * volatile *ptr, void *oldval, void *newval)
{
#if defined(GIT_WIN32)
- volatile void *foundval;
- foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
- return (foundval == oldval) ? oldval : newval;
+ return InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
#elif defined(GIT_BUILTIN_ATOMIC)
- bool success = __atomic_compare_exchange(ptr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
- return success ? oldval : newval;
+ void *foundval = oldval;
+ __atomic_compare_exchange(ptr, &foundval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+ return foundval;
#elif defined(GIT_BUILTIN_SYNC)
- volatile void *foundval;
- foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
- return (foundval == oldval) ? oldval : newval;
+ return __sync_val_compare_and_swap(ptr, oldval, newval);
#else
# error "Unsupported architecture for atomic operations"
#endif
@@ -164,11 +180,11 @@ GIT_INLINE(volatile void *) git_atomic__swap(
#if defined(GIT_WIN32)
return InterlockedExchangePointer(ptr, newval);
#elif defined(GIT_BUILTIN_ATOMIC)
- void * volatile foundval;
+ void * volatile foundval = NULL;
__atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST);
return foundval;
#elif defined(GIT_BUILTIN_SYNC)
- return __sync_lock_test_and_set(ptr, newval);
+ return (volatile void *)__sync_lock_test_and_set(ptr, newval);
#else
# error "Unsupported architecture for atomic operations"
#endif
@@ -178,9 +194,7 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
{
#if defined(GIT_WIN32)
void *newval = NULL, *oldval = NULL;
- volatile void *foundval = NULL;
- foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
- return foundval;
+ return (volatile void *)InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
#elif defined(GIT_BUILTIN_ATOMIC)
return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC)
@@ -192,10 +206,14 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
#ifdef GIT_ARCH_64
+/*
+ * Atomically adds the contents of *a and addend, and stores the result back into *a.
+ * @return the result of the operation.
+ */
GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
{
#if defined(GIT_WIN32)
- return InterlockedExchangeAdd64(&a->val, addend);
+ return InterlockedAdd64(&a->val, addend);
#elif defined(GIT_BUILTIN_ATOMIC)
return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC)
@@ -205,6 +223,9 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#endif
}
+/*
+ * Atomically sets the contents of *a to be val.
+ */
GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
{
#if defined(GIT_WIN32)
@@ -218,6 +239,10 @@ GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
#endif
}
+/*
+ * Atomically gets the contents of *a.
+ * @return the contents of *a.
+ */
GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
{
#if defined(GIT_WIN32)
@@ -297,11 +322,10 @@ GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
GIT_INLINE(void *) git_atomic__compare_and_swap(
void * volatile *ptr, void *oldval, void *newval)
{
- if (*ptr == oldval)
+ void *foundval = *ptr;
+ if (foundval == oldval)
*ptr = newval;
- else
- oldval = newval;
- return oldval;
+ return foundval;
}
GIT_INLINE(volatile void *) git_atomic__swap(
@@ -339,17 +363,50 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
#endif
-/* Atomically replace oldval with newval
- * @return oldval if it was replaced or newval if it was not
+/*
+ * Atomically replace the contents of *ptr (if they are equal to oldval) with
+ * newval. ptr must point to a pointer or a value that is the same size as a
+ * pointer. This is semantically compatible with:
+ *
+ * #define git_atomic_compare_and_swap(ptr, oldval, newval) \
+ * ({ \
+ * void *foundval = *ptr; \
+ * if (foundval == oldval) \
+ * *ptr = newval; \
+ * foundval; \
+ * })
+ *
+ * @return the original contents of *ptr.
*/
-#define git_atomic_compare_and_swap(P,O,N) \
- git_atomic__compare_and_swap((void * volatile *)P, O, N)
+#define git_atomic_compare_and_swap(ptr, oldval, newval) \
+ git_atomic__compare_and_swap((void * volatile *)ptr, oldval, newval)
-#define git_atomic_swap(ptr, val) \
- (void *)git_atomic__swap((void * volatile *)&ptr, val)
+/*
+ * Atomically replace the contents of v with newval. v must be the same size as
+ * a pointer. This is semantically compatible with:
+ *
+ * #define git_atomic_swap(v, newval) \
+ * ({ \
+ * volatile void *old = v; \
+ * v = newval; \
+ * old; \
+ * })
+ *
+ * @return the original contents of v.
+ */
+#define git_atomic_swap(v, newval) \
+ (void *)git_atomic__swap((void * volatile *)&(v), newval)
-#define git_atomic_load(ptr) \
- (void *)git_atomic__load((void * volatile *)&ptr)
+/*
+ * Atomically reads the contents of v. v must be the same size as a pointer.
+ * This is semantically compatible with:
+ *
+ * #define git_atomic_load(v) v
+ *
+ * @return the contents of v.
+ */
+#define git_atomic_load(v) \
+ (void *)git_atomic__load((void * volatile *)&(v))
#if defined(GIT_THREADS)
diff --git a/src/transports/auth.c b/src/transports/auth.c
index 4aa3df021..51763e359 100644
--- a/src/transports/auth.c
+++ b/src/transports/auth.c
@@ -18,7 +18,7 @@ static int basic_next_token(
{
git_credential_userpass_plaintext *cred;
git_buf raw = GIT_BUF_INIT;
- int error = -1;
+ int error = GIT_EAUTH;
GIT_UNUSED(ctx);
diff --git a/src/transports/auth_negotiate.c b/src/transports/auth_negotiate.c
index c538dbbca..31469933e 100644
--- a/src/transports/auth_negotiate.c
+++ b/src/transports/auth_negotiate.c
@@ -267,7 +267,7 @@ static int negotiate_init_context(
if (!ctx->oid) {
git_error_set(GIT_ERROR_NET, "negotiate authentication is not supported");
- return -1;
+ return GIT_EAUTH;
}
git_buf_puts(&ctx->target, "HTTP@");
diff --git a/src/transports/auth_ntlm.c b/src/transports/auth_ntlm.c
index b5110616b..742db75b3 100644
--- a/src/transports/auth_ntlm.c
+++ b/src/transports/auth_ntlm.c
@@ -85,7 +85,7 @@ static int ntlm_next_token(
git_buf input_buf = GIT_BUF_INIT;
const unsigned char *msg;
size_t challenge_len, msg_len;
- int error = -1;
+ int error = GIT_EAUTH;
GIT_ASSERT_ARG(buf);
GIT_ASSERT_ARG(ctx);
diff --git a/src/transports/http.c b/src/transports/http.c
index 9871be5ad..691bceb75 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -162,7 +162,7 @@ static int handle_auth(
if (error > 0) {
git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type);
- error = -1;
+ error = GIT_EAUTH;
}
if (!error)
@@ -179,7 +179,7 @@ GIT_INLINE(int) handle_remote_auth(
if (response->server_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
- return -1;
+ return GIT_EAUTH;
}
/* Otherwise, prompt for credentials. */
@@ -201,7 +201,7 @@ GIT_INLINE(int) handle_proxy_auth(
if (response->proxy_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
- return -1;
+ return GIT_EAUTH;
}
/* Otherwise, prompt for credentials. */
@@ -259,7 +259,7 @@ static int handle_response(
} else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
- return -1;
+ return GIT_EAUTH;
}
if (response->status != GIT_HTTP_STATUS_OK) {
@@ -416,7 +416,7 @@ static int http_stream_read(
if (stream->state == HTTP_STATE_SENDING_REQUEST) {
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
- error = -1;
+ error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */
goto done;
}
@@ -554,7 +554,7 @@ static int http_stream_write(
if (stream->state == HTTP_STATE_NONE) {
git_error_set(GIT_ERROR_HTTP,
"too many redirects or authentication replays");
- error = -1;
+ error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
goto done;
}
diff --git a/src/transports/httpclient.c b/src/transports/httpclient.c
index ba86184df..4612b4334 100644
--- a/src/transports/httpclient.c
+++ b/src/transports/httpclient.c
@@ -597,6 +597,7 @@ static int apply_credentials(
free_auth_context(server);
} else if (!token.size) {
git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge");
+ error = GIT_EAUTH;
error = -1;
goto done;
}
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 71c37e758..efa77a798 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -461,13 +461,13 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
if (no_callback) {
git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
- return -1;
+ return GIT_EAUTH;
}
if (!(cred->credtype & auth_methods)) {
cred->free(cred);
- git_error_set(GIT_ERROR_SSH, "callback returned unsupported credentials type");
- return -1;
+ git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type");
+ return GIT_EAUTH;
}
*out = cred;
@@ -840,7 +840,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
if (list == NULL && !libssh2_userauth_authenticated(session)) {
ssh_error(session, "Failed to retrieve list of SSH authentication methods");
- return -1;
+ return GIT_EAUTH;
}
ptr = list;
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index d21c50651..ea2195a99 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -154,7 +154,7 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha
native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
} else {
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
- error = -1;
+ error = GIT_EAUTH;
goto done;
}
@@ -193,7 +193,7 @@ static int apply_default_credentials(HINTERNET request, DWORD target, int mechan
native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
} else {
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
- return -1;
+ return GIT_EAUTH;
}
/*
@@ -616,7 +616,7 @@ static int parse_unauthorized_response(
*/
if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
git_error_set(GIT_ERROR_OS, "failed to parse supported auth schemes");
- return -1;
+ return GIT_EAUTH;
}
if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
@@ -1040,7 +1040,7 @@ replay:
/* Enforce a reasonable cap on the number of replays */
if (replay_count++ >= GIT_HTTP_REPLAY_MAX) {
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
- return -1;
+ return GIT_ERROR; /* not GIT_EAUTH because the exact cause is not clear */
}
/* Connect if necessary */
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index f8d9abe55..3241a3eb7 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -1636,3 +1636,49 @@ void test_checkout_tree__no_index_refresh(void)
modify_index_and_checkout_tree(&opts);
assert_status_entrycount(g_repo, 0);
}
+
+void test_checkout_tree__dry_run(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+ checkout_counts ct;
+
+ /* first let's get things into a known state - by checkout out the HEAD */
+
+ assert_on_branch(g_repo, "master");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_path_isdir("testrepo/a"));
+
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* now checkout branch but with dry run enabled */
+
+ memset(&ct, 0, sizeof(ct));
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DRY_RUN;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &ct;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ assert_on_branch(g_repo, "dir");
+
+ /* these normally would have been created and updated, but with
+ * DRY_RUN they will be unchanged.
+ */
+ cl_assert(!git_path_isdir("testrepo/a"));
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* check that notify callback was invoked */
+ cl_assert_equal_i(ct.n_updates, 2);
+
+ git_object_free(obj);
+}
diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c
index d4da3d3af..ec12fee18 100644
--- a/tests/clone/nonetwork.c
+++ b/tests/clone/nonetwork.c
@@ -172,7 +172,7 @@ void test_clone_nonetwork__can_checkout_given_branch(void)
cl_git_pass(git_reference_lookup(&remote_head, g_repo, "refs/remotes/origin/HEAD"));
cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(remote_head));
- cl_assert_equal_s("refs/remotes/origin/test", git_reference_symbolic_target(remote_head));
+ cl_assert_equal_s("refs/remotes/origin/master", git_reference_symbolic_target(remote_head));
git_reference_free(remote_head);
}
diff --git a/tests/config/read.c b/tests/config/read.c
index 109908750..badf5118e 100644
--- a/tests/config/read.c
+++ b/tests/config/read.c
@@ -964,7 +964,7 @@ void test_config_read__get_mapped(void)
" key9 = off\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
- // check parsing bool and string
+ /* check parsing bool and string */
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map1, ARRAY_SIZE(_test_map1)));
cl_assert_equal_i(val, MAP_TRUE);
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key2", _test_map1, ARRAY_SIZE(_test_map1)));
@@ -986,7 +986,7 @@ void test_config_read__get_mapped(void)
cl_git_fail(git_config_get_mapped(&val, cfg, "header.key7", _test_map1, ARRAY_SIZE(_test_map1)));
- // check parsing int values
+ /* check parsing int values */
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map2, ARRAY_SIZE(_test_map2)));
cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key1"));
cl_assert_equal_i(val, known_good);
diff --git a/tests/pack/midx.c b/tests/pack/midx.c
index 0d7efbef1..d5b91d28b 100644
--- a/tests/pack/midx.c
+++ b/tests/pack/midx.c
@@ -1,7 +1,9 @@
#include "clar_libgit2.h"
#include <git2.h>
+#include <git2/sys/midx.h>
+#include "futils.h"
#include "midx.h"
void test_pack_midx__parse(void)
@@ -44,3 +46,65 @@ void test_pack_midx__lookup(void)
git_commit_free(commit);
git_repository_free(repo);
}
+
+void test_pack_midx__writer(void)
+{
+ git_repository *repo;
+ git_midx_writer *w = NULL;
+ git_buf midx = GIT_BUF_INIT, expected_midx = GIT_BUF_INIT, path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/pack"));
+ cl_git_pass(git_midx_writer_new(&w, git_buf_cstr(&path)));
+
+ cl_git_pass(git_midx_writer_add(w, "pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx"));
+ cl_git_pass(git_midx_writer_add(w, "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx"));
+ cl_git_pass(git_midx_writer_add(w, "pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"));
+
+ cl_git_pass(git_midx_writer_dump(&midx, w));
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_pass(git_futils_readbuffer(&expected_midx, git_buf_cstr(&path)));
+
+ cl_assert_equal_i(git_buf_len(&midx), git_buf_len(&expected_midx));
+ cl_assert_equal_strn(git_buf_cstr(&midx), git_buf_cstr(&expected_midx), git_buf_len(&midx));
+
+ git_buf_dispose(&midx);
+ git_buf_dispose(&expected_midx);
+ git_buf_dispose(&path);
+ git_midx_writer_free(w);
+ git_repository_free(repo);
+}
+
+void test_pack_midx__odb_create(void)
+{
+ git_repository *repo;
+ git_odb *odb;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_buf midx = GIT_BUF_INIT, expected_midx = GIT_BUF_INIT, midx_path = GIT_BUF_INIT;
+ struct stat st;
+
+ opts.bare = true;
+ opts.local = GIT_CLONE_LOCAL;
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo/.gitted"), "./clone.git", &opts));
+ cl_git_pass(git_buf_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_fail(p_stat(git_buf_cstr(&midx_path), &st));
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_git_pass(git_odb_write_multi_pack_index(odb));
+ git_odb_free(odb);
+
+ cl_git_pass(p_stat(git_buf_cstr(&midx_path), &st));
+
+ cl_git_pass(git_futils_readbuffer(&expected_midx, cl_fixture("testrepo.git/objects/pack/multi-pack-index")));
+ cl_git_pass(git_futils_readbuffer(&midx, git_buf_cstr(&midx_path)));
+ cl_assert_equal_i(git_buf_len(&midx), git_buf_len(&expected_midx));
+ cl_assert_equal_strn(git_buf_cstr(&midx), git_buf_cstr(&expected_midx), git_buf_len(&midx));
+
+ git_repository_free(repo);
+ git_buf_dispose(&midx);
+ git_buf_dispose(&midx_path);
+ git_buf_dispose(&expected_midx);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+} \ No newline at end of file
diff --git a/tests/remote/fetch.c b/tests/remote/fetch.c
index 392801e0b..7e825ca16 100644
--- a/tests/remote/fetch.c
+++ b/tests/remote/fetch.c
@@ -72,7 +72,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
.strings = &refspec_strs,
};
- // create two commits in repo 1 and a reference to them
+ /* create two commits in repo 1 and a reference to them */
{
git_oid empty_tree_id;
git_tree *empty_tree;
@@ -92,7 +92,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_treebuilder_free(tb);
}
- // fetch the reference via the remote
+ /* fetch the reference via the remote */
{
git_remote *remote;
@@ -103,7 +103,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_remote_free(remote);
}
- // assert that repo2 references the second commit
+ /* assert that repo2 references the second commit */
{
const git_oid *target;
git_reference *ref;
@@ -113,7 +113,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_reference_free(ref);
}
- // set the reference in repo1 to point to the older commit
+ /* set the reference in repo1 to point to the older commit */
{
git_reference *ref;
git_reference *ref2;
@@ -124,7 +124,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_reference_free(ref2);
}
- // fetch the reference again
+ /* fetch the reference again */
{
git_remote *remote;
@@ -144,7 +144,7 @@ void test_remote_fetch__dont_update_refs_if_not_descendant_and_not_force(void) {
do_time_travelling_fetch(&commit1id, &commit2id, false);
- // assert that the reference in repo2 has not changed
+ /* assert that the reference in repo2 has not changed */
cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
target = git_reference_target(ref);
cl_assert_equal_b(git_oid_cmp(target, &commit2id), 0);
@@ -160,7 +160,7 @@ void test_remote_fetch__do_update_refs_if_not_descendant_and_force(void) {
do_time_travelling_fetch(&commit1id, &commit2id, true);
- // assert that the reference in repo2 has changed
+ /* assert that the reference in repo2 has changed */
cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
target = git_reference_target(ref);
cl_assert_equal_b(git_oid_cmp(target, &commit1id), 0);
diff --git a/tests/threads/atomic.c b/tests/threads/atomic.c
new file mode 100644
index 000000000..4d04a777a
--- /dev/null
+++ b/tests/threads/atomic.c
@@ -0,0 +1,125 @@
+#include "clar_libgit2.h"
+
+void test_threads_atomic__atomic32_set(void)
+{
+ git_atomic32 v = {0};
+ git_atomic32_set(&v, 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_get(void)
+{
+ git_atomic32 v = {1};
+ cl_assert_equal_i(git_atomic32_get(&v), 1);
+}
+
+void test_threads_atomic__atomic32_inc(void)
+{
+ git_atomic32 v = {0};
+ cl_assert_equal_i(git_atomic32_inc(&v), 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_add(void)
+{
+ git_atomic32 v = {0};
+ cl_assert_equal_i(git_atomic32_add(&v, 1), 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_dec(void)
+{
+ git_atomic32 v = {1};
+ cl_assert_equal_i(git_atomic32_dec(&v), 0);
+ cl_assert_equal_i(v.val, 0);
+}
+
+void test_threads_atomic__atomic64_set(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {0};
+ git_atomic64_set(&v, 1);
+ cl_assert_equal_i(v.val, 1);
+#endif
+}
+
+void test_threads_atomic__atomic64_get(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {1};
+ cl_assert_equal_i(git_atomic64_get(&v), 1);
+#endif
+}
+
+void test_threads_atomic__atomic64_add(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {0};
+ cl_assert_equal_i(git_atomic64_add(&v, 1), 1);
+ cl_assert_equal_i(v.val, 1);
+#endif
+}
+
+void test_threads_atomic__cas_pointer(void)
+{
+ int *value = NULL;
+ int newvalue1 = 1, newvalue2 = 2;
+
+ /* value is updated */
+ cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue1), NULL);
+ cl_assert_equal_p(value, &newvalue1);
+
+ /* value is not updated */
+ cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue2), &newvalue1);
+ cl_assert_equal_p(value, &newvalue1);
+}
+
+void test_threads_atomic__cas_intptr(void)
+{
+ intptr_t value = 0;
+ intptr_t oldvalue;
+ intptr_t newvalue;
+
+ /* value is updated */
+ oldvalue = 0;
+ newvalue = 1;
+ cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 0);
+ cl_assert_equal_i(value, 1);
+
+ /* value is not updated */
+ oldvalue = 0;
+ newvalue = 2;
+ cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 1);
+ cl_assert_equal_i(value, 1);
+}
+
+void test_threads_atomic__swap(void)
+{
+ int *value = NULL;
+ int newvalue = 1;
+
+ cl_assert_equal_p(git_atomic_swap(value, &newvalue), NULL);
+ cl_assert_equal_p(value, &newvalue);
+
+ cl_assert_equal_p(git_atomic_swap(value, NULL), &newvalue);
+ cl_assert_equal_p(value, NULL);
+}
+
+void test_threads_atomic__load_ptr(void)
+{
+ int value = 1;
+ int *ptr = &value;
+ cl_assert_equal_p(git_atomic_load(ptr), &value);
+}
+
+void test_threads_atomic__load_intptr(void)
+{
+ intptr_t value = 1;
+ cl_assert_equal_i((intptr_t)git_atomic_load(value), 1);
+}