Commit 7d9ebdc8 by lhchavez

Merge remote-tracking branch 'origin/main' into cgraph-write

parents 94008e6a 2998a84a
{
"postCreateCommand": "bash .devcontainer/setup.sh"
}
#!/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
{
// 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
{
// 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
...@@ -56,6 +56,7 @@ ELSE() ...@@ -56,6 +56,7 @@ ELSE()
MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}") MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
ENDIF() ENDIF()
list(APPEND SRC_SHA1 "hash/sha1.h")
list(SORT SRC_SHA1) list(SORT SRC_SHA1)
ADD_FEATURE_INFO(SHA ON "using ${USE_SHA1}") ADD_FEATURE_INFO(SHA ON "using ${USE_SHA1}")
...@@ -509,6 +509,9 @@ with v0.28.0. ...@@ -509,6 +509,9 @@ with v0.28.0.
The breaking change is that the `username` member of the underlying struct 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. 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 ### Breaking CMake configuration changes
* The CMake option to use a system http-parser library, instead of the * The CMake option to use a system http-parser library, instead of the
......
...@@ -178,6 +178,12 @@ typedef enum { ...@@ -178,6 +178,12 @@ typedef enum {
GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23), 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 * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/ */
......
...@@ -42,14 +42,14 @@ typedef enum { ...@@ -42,14 +42,14 @@ typedef enum {
GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */ GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */
GIT_ELOCKED = -14, /**< Lock file prevented operation */ GIT_ELOCKED = -14, /**< Lock file prevented operation */
GIT_EMODIFIED = -15, /**< Reference value does not match expected */ GIT_EMODIFIED = -15, /**< Reference value does not match expected */
GIT_EAUTH = -16, /**< Authentication error */ GIT_EAUTH = -16, /**< Authentication error */
GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */ GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */
GIT_EPEEL = -19, /**< The requested peel operation is not possible */ GIT_EPEEL = -19, /**< The requested peel operation is not possible */
GIT_EEOF = -20, /**< Unexpected EOF */ GIT_EEOF = -20, /**< Unexpected EOF */
GIT_EINVALID = -21, /**< Invalid operation or input */ GIT_EINVALID = -21, /**< Invalid operation or input */
GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */ 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_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */ GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */
......
...@@ -391,6 +391,20 @@ GIT_EXTERN(int) git_odb_write_pack( ...@@ -391,6 +391,20 @@ GIT_EXTERN(int) git_odb_write_pack(
void *progress_payload); 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 * Determine the object-ID (sha1 hash) of a data buffer
* *
* The resulting SHA-1 OID will be the identifier for the data * The resulting SHA-1 OID will be the identifier for the data
......
/*
* 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
...@@ -85,6 +85,13 @@ struct git_odb_backend { ...@@ -85,6 +85,13 @@ struct git_odb_backend {
git_indexer_progress_cb progress_cb, void *progress_payload); 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 * "Freshens" an already existing object, updating its last-used
* time. This occurs when `git_odb_write` was called, but the * time. This occurs when `git_odb_write` was called, but the
* object already existed (and will not be re-written). The * object already existed (and will not be re-written). The
......
...@@ -96,8 +96,8 @@ typedef struct git_odb_stream git_odb_stream; ...@@ -96,8 +96,8 @@ typedef struct git_odb_stream git_odb_stream;
/** A stream to write a packfile to the ODB */ /** A stream to write a packfile to the ODB */
typedef struct git_odb_writepack git_odb_writepack; typedef struct git_odb_writepack git_odb_writepack;
/** a writer for commit-graph files. */ /** a writer for multi-pack-index files. */
typedef struct git_commit_graph_writer git_commit_graph_writer; typedef struct git_midx_writer git_midx_writer;
/** An open refs database handle. */ /** An open refs database handle. */
typedef struct git_refdb git_refdb; typedef struct git_refdb git_refdb;
...@@ -108,6 +108,9 @@ typedef struct git_refdb_backend git_refdb_backend; ...@@ -108,6 +108,9 @@ typedef struct git_refdb_backend git_refdb_backend;
/** A git commit-graph */ /** A git commit-graph */
typedef struct git_commit_graph 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, * Representation of an existing git repository,
* including all its object contents * including all its object contents
......
...@@ -41,8 +41,8 @@ ...@@ -41,8 +41,8 @@
typedef git_array_t(char) git_array_generic_t; typedef git_array_t(char) git_array_generic_t;
/* use a generic array for growth so this can return the new item */ /* use a generic array for growth, return 0 on success */
GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size) GIT_INLINE(int) git_array_grow(void *_a, size_t item_size)
{ {
volatile git_array_generic_t *a = _a; volatile git_array_generic_t *a = _a;
size_t new_size; size_t new_size;
...@@ -59,18 +59,18 @@ GIT_INLINE(void *) git_array_grow(void *_a, size_t item_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) if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
goto on_oom; goto on_oom;
a->ptr = new_array; a->asize = new_size; a->size++; a->ptr = new_array;
return a->ptr + (a->size - 1) * item_size; a->asize = new_size;
return 0;
on_oom: on_oom:
git_array_clear(*a); git_array_clear(*a);
return NULL; return -1;
} }
#define git_array_alloc(a) \ #define git_array_alloc(a) \
(((a).size >= (a).asize) ? \ (((a).size < (a).asize || git_array_grow(&(a), sizeof(*(a).ptr)) == 0) ? \
git_array_grow(&(a), sizeof(*(a).ptr)) : \ &(a).ptr[(a).size++] : (void *)NULL)
((a).ptr ? &(a).ptr[(a).size++] : (void *)NULL))
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL) #define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL)
......
...@@ -127,7 +127,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) ...@@ -127,7 +127,7 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
{ {
int error = 0; int error = 0;
git_attr_file_entry *entry; git_attr_file_entry *entry;
git_attr_file *old = NULL; git_attr_file *oldfile = NULL;
if (!file) if (!file)
return 0; return 0;
...@@ -136,13 +136,13 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) ...@@ -136,13 +136,13 @@ static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
return error; return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) 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); attr_cache_unlock(cache);
if (old) { if (oldfile == file) {
GIT_REFCOUNT_OWN(old, NULL); GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(old); git_attr_file__free(file);
} }
return error; return error;
...@@ -401,8 +401,7 @@ int git_attr_cache__init(git_repository *repo) ...@@ -401,8 +401,7 @@ int git_attr_cache__init(git_repository *repo)
(ret = git_pool_init(&cache->pool, 1)) < 0) (ret = git_pool_init(&cache->pool, 1)) < 0)
goto cancel; goto cancel;
cache = git_atomic_compare_and_swap(&repo->attrcache, NULL, cache); if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL)
if (cache)
goto cancel; /* raced with another thread, free this but no error */ goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg); git_config_free(cfg);
......
...@@ -2622,6 +2622,9 @@ int git_checkout_iterator( ...@@ -2622,6 +2622,9 @@ int git_checkout_iterator(
if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0) if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0)
goto cleanup; goto cleanup;
if (data.strategy & GIT_CHECKOUT_DRY_RUN)
goto cleanup;
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] + data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
counts[CHECKOUT_ACTION__REMOVE_CONFLICT] + counts[CHECKOUT_ACTION__REMOVE_CONFLICT] +
counts[CHECKOUT_ACTION__UPDATE_BLOB] + counts[CHECKOUT_ACTION__UPDATE_BLOB] +
......
...@@ -162,37 +162,6 @@ done: ...@@ -162,37 +162,6 @@ done:
return error; 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( static int update_remote_head(
git_repository *repo, git_repository *repo,
git_remote *remote, git_remote *remote,
...@@ -200,7 +169,9 @@ static int update_remote_head( ...@@ -200,7 +169,9 @@ static int update_remote_head(
const char *reflog_message) const char *reflog_message)
{ {
git_refspec *refspec; 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; int error;
/* Determine the remote tracking ref name from the local branch */ /* Determine the remote tracking ref name from the local branch */
...@@ -213,19 +184,30 @@ static int update_remote_head( ...@@ -213,19 +184,30 @@ static int update_remote_head(
} }
if ((error = git_refspec_transform( if ((error = git_refspec_transform(
&tracking_branch_name, &remote_branch_name,
refspec, refspec,
git_buf_cstr(target))) < 0) git_buf_cstr(target))) < 0)
goto cleanup; goto cleanup;
error = update_remote_head_byname( if ((error = git_buf_printf(&remote_head_name,
repo, "%s%s/%s",
GIT_REFS_REMOTES_DIR,
git_remote_name(remote), 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); reflog_message);
cleanup: 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; return error;
} }
...@@ -277,19 +259,20 @@ cleanup: ...@@ -277,19 +259,20 @@ cleanup:
static int update_head_to_branch( static int update_head_to_branch(
git_repository *repo, git_repository *repo,
const char *remote_name, git_remote *remote,
const char *branch, const char *branch,
const char *reflog_message) const char *reflog_message)
{ {
int retcode; int retcode;
git_buf remote_branch_name = GIT_BUF_INIT; git_buf remote_branch_name = GIT_BUF_INIT;
git_reference* remote_ref = NULL; 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); GIT_ASSERT_ARG(branch);
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", 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; goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0) 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( ...@@ -299,11 +282,18 @@ static int update_head_to_branch(
reflog_message)) < 0) reflog_message)) < 0)
goto cleanup; 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: cleanup:
git_reference_free(remote_ref); git_reference_free(remote_ref);
git_buf_dispose(&remote_branch_name); git_buf_dispose(&remote_branch_name);
git_buf_dispose(&default_branch);
return retcode; return retcode;
} }
...@@ -388,8 +378,7 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c ...@@ -388,8 +378,7 @@ static int checkout_branch(git_repository *repo, git_remote *remote, const git_c
int error; int error;
if (branch) if (branch)
error = update_head_to_branch(repo, git_remote_name(remote), branch, error = update_head_to_branch(repo, remote, branch, reflog_message);
reflog_message);
/* Point HEAD to the same ref as the remote's head */ /* Point HEAD to the same ref as the remote's head */
else else
error = update_head_to_remote(repo, remote, reflog_message); error = update_head_to_remote(repo, remote, reflog_message);
......
...@@ -141,18 +141,23 @@ static int diff_driver_funcname(const git_config_entry *entry, void *payload) ...@@ -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( static git_diff_driver_registry *git_repository_driver_registry(
git_repository *repo) git_repository *repo)
{ {
if (!repo->diff_drivers) { git_diff_driver_registry *reg = git_atomic_load(repo->diff_drivers), *newreg;
git_diff_driver_registry *reg = git_diff_driver_registry_new(); if (reg)
reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, reg); return reg;
if (reg != NULL) /* if we race, free losing allocation */ newreg = git_diff_driver_registry_new();
git_diff_driver_registry_free(reg); if (!newreg) {
}
if (!repo->diff_drivers)
git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry"); git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry");
return newreg;
return repo->diff_drivers; }
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( static int diff_driver_alloc(
......
...@@ -7,11 +7,15 @@ ...@@ -7,11 +7,15 @@
#include "midx.h" #include "midx.h"
#include "array.h"
#include "buffer.h" #include "buffer.h"
#include "filebuf.h"
#include "futils.h" #include "futils.h"
#include "hash.h" #include "hash.h"
#include "odb.h" #include "odb.h"
#include "pack.h" #include "pack.h"
#include "path.h"
#include "repository.h"
#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */ #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
#define MIDX_VERSION 1 #define MIDX_VERSION 1
...@@ -36,6 +40,8 @@ struct git_midx_chunk { ...@@ -36,6 +40,8 @@ struct git_midx_chunk {
size_t length; size_t length;
}; };
typedef int (*midx_write_cb)(const char *buf, size_t size, void *cb_data);
static int midx_error(const char *message) static int midx_error(const char *message)
{ {
git_error_set(GIT_ERROR_ODB, "invalid multi-pack-index file - %s", 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) ...@@ -475,3 +481,395 @@ void git_midx_free(git_midx_file *idx)
git_midx_close(idx); git_midx_close(idx);
git__free(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);
}
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include <ctype.h> #include <ctype.h>
#include "git2/sys/midx.h"
#include "map.h" #include "map.h"
#include "mwindow.h" #include "mwindow.h"
#include "odb.h" #include "odb.h"
...@@ -67,6 +69,20 @@ typedef struct git_midx_entry { ...@@ -67,6 +69,20 @@ typedef struct git_midx_entry {
git_oid sha1; git_oid sha1;
} git_midx_entry; } 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( int git_midx_open(
git_midx_file **idx_out, git_midx_file **idx_out,
const char *path); const char *path);
......
...@@ -1703,6 +1703,35 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_ ...@@ -1703,6 +1703,35 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_
return error; 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) void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len)
{ {
GIT_UNUSED(backend); GIT_UNUSED(backend);
......
...@@ -402,7 +402,6 @@ static int process_multi_pack_index_pack( ...@@ -402,7 +402,6 @@ static int process_multi_pack_index_pack(
const char *packfile_name) const char *packfile_name)
{ {
int error; int error;
size_t cmp_len = strlen(packfile_name);
struct git_pack_file *pack; struct git_pack_file *pack;
size_t found_position; size_t found_position;
git_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT; git_buf pack_path = GIT_BUF_INIT, index_prefix = GIT_BUF_INIT;
...@@ -411,12 +410,11 @@ static int process_multi_pack_index_pack( ...@@ -411,12 +410,11 @@ static int process_multi_pack_index_pack(
if (error < 0) if (error < 0)
return error; return error;
/* This is ensured by midx__parse_packfile_name() */ /* This is ensured by midx_parse_packfile_name() */
if (cmp_len <= strlen(".idx") || git__suffixcmp(git_buf_cstr(&pack_path), ".idx") != 0) 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); 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), git_buf_len(&pack_path) - strlen(".idx"));
git_buf_attach_notowned(&index_prefix, git_buf_cstr(&pack_path), cmp_len);
if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) { 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. */ /* 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, ...@@ -744,6 +742,81 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
return 0; 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) static void pack_backend__free(git_odb_backend *_backend)
{ {
struct pack_backend *backend; struct pack_backend *backend;
...@@ -792,6 +865,7 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size) ...@@ -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.refresh = &pack_backend__refresh;
backend->parent.foreach = &pack_backend__foreach; backend->parent.foreach = &pack_backend__foreach;
backend->parent.writepack = &pack_backend__writepack; backend->parent.writepack = &pack_backend__writepack;
backend->parent.writemidx = &pack_backend__writemidx;
backend->parent.freshen = &pack_backend__freshen; backend->parent.freshen = &pack_backend__freshen;
backend->parent.free = &pack_backend__free; backend->parent.free = &pack_backend__free;
......
...@@ -1368,6 +1368,73 @@ int git_pack_foreach_entry( ...@@ -1368,6 +1368,73 @@ int git_pack_foreach_entry(
return error; 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, int git_pack__lookup_sha1(const void *oid_lookup_table, size_t stride, unsigned lo,
unsigned hi, const unsigned char *oid_prefix) unsigned hi, const unsigned char *oid_prefix)
{ {
......
...@@ -20,6 +20,14 @@ ...@@ -20,6 +20,14 @@
#include "oidmap.h" #include "oidmap.h"
#include "zstream.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 GIT_PACK_FILE_MODE 0444
#define PACK_SIGNATURE 0x5041434b /* "PACK" */ #define PACK_SIGNATURE 0x5041434b /* "PACK" */
...@@ -176,5 +184,16 @@ int git_pack_foreach_entry( ...@@ -176,5 +184,16 @@ int git_pack_foreach_entry(
struct git_pack_file *p, struct git_pack_file *p,
git_odb_foreach_cb cb, git_odb_foreach_cb cb,
void *data); 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 #endif
...@@ -1093,8 +1093,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) ...@@ -1093,8 +1093,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(config, repo); GIT_REFCOUNT_OWN(config, repo);
config = git_atomic_compare_and_swap(&repo->_config, NULL, config); if (git_atomic_compare_and_swap(&repo->_config, NULL, config) != NULL) {
if (config != NULL) {
GIT_REFCOUNT_OWN(config, NULL); GIT_REFCOUNT_OWN(config, NULL);
git_config_free(config); git_config_free(config);
} }
...@@ -1164,8 +1163,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo) ...@@ -1164,8 +1163,7 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
return error; return error;
} }
odb = git_atomic_compare_and_swap(&repo->_odb, NULL, odb); if (git_atomic_compare_and_swap(&repo->_odb, NULL, odb) != NULL) {
if (odb != NULL) {
GIT_REFCOUNT_OWN(odb, NULL); GIT_REFCOUNT_OWN(odb, NULL);
git_odb_free(odb); git_odb_free(odb);
} }
...@@ -1209,8 +1207,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) ...@@ -1209,8 +1207,7 @@ int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(refdb, repo); GIT_REFCOUNT_OWN(refdb, repo);
refdb = git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb); if (git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb) != NULL) {
if (refdb != NULL) {
GIT_REFCOUNT_OWN(refdb, NULL); GIT_REFCOUNT_OWN(refdb, NULL);
git_refdb_free(refdb); git_refdb_free(refdb);
} }
...@@ -1257,8 +1254,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo) ...@@ -1257,8 +1254,7 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
if (!error) { if (!error) {
GIT_REFCOUNT_OWN(index, repo); GIT_REFCOUNT_OWN(index, repo);
index = git_atomic_compare_and_swap(&repo->_index, NULL, index); if (git_atomic_compare_and_swap(&repo->_index, NULL, index) != NULL) {
if (index != NULL) {
GIT_REFCOUNT_OWN(index, NULL); GIT_REFCOUNT_OWN(index, NULL);
git_index_free(index); git_index_free(index);
} }
......
...@@ -74,6 +74,9 @@ typedef git_atomic32 git_atomic_ssize; ...@@ -74,6 +74,9 @@ typedef git_atomic32 git_atomic_ssize;
# include "unix/pthread.h" # include "unix/pthread.h"
#endif #endif
/*
* Atomically sets the contents of *a to be val.
*/
GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val) GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -87,6 +90,10 @@ GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val) ...@@ -87,6 +90,10 @@ GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
#endif #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) GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -100,10 +107,14 @@ GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a) ...@@ -100,10 +107,14 @@ GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
#endif #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) GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
return InterlockedExchangeAdd(&a->val, addend); return InterlockedAdd(&a->val, addend);
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST); return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
...@@ -113,6 +124,10 @@ GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend) ...@@ -113,6 +124,10 @@ GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
#endif #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) GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -126,6 +141,10 @@ GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a) ...@@ -126,6 +141,10 @@ GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
#endif #endif
} }
/*
* Atomically gets the contents of *a.
* @return the contents of *a.
*/
GIT_INLINE(int) git_atomic32_get(git_atomic32 *a) GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -143,16 +162,13 @@ GIT_INLINE(void *) git_atomic__compare_and_swap( ...@@ -143,16 +162,13 @@ GIT_INLINE(void *) git_atomic__compare_and_swap(
void * volatile *ptr, void *oldval, void *newval) void * volatile *ptr, void *oldval, void *newval)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
volatile void *foundval; return InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
return (foundval == oldval) ? oldval : newval;
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
bool success = __atomic_compare_exchange(ptr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); void *foundval = oldval;
return success ? oldval : newval; __atomic_compare_exchange(ptr, &foundval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return foundval;
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
volatile void *foundval; return __sync_val_compare_and_swap(ptr, oldval, newval);
foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
return (foundval == oldval) ? oldval : newval;
#else #else
# error "Unsupported architecture for atomic operations" # error "Unsupported architecture for atomic operations"
#endif #endif
...@@ -164,11 +180,11 @@ GIT_INLINE(volatile void *) git_atomic__swap( ...@@ -164,11 +180,11 @@ GIT_INLINE(volatile void *) git_atomic__swap(
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
return InterlockedExchangePointer(ptr, newval); return InterlockedExchangePointer(ptr, newval);
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
void * volatile foundval; void * volatile foundval = NULL;
__atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST); __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST);
return foundval; return foundval;
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
return __sync_lock_test_and_set(ptr, newval); return (volatile void *)__sync_lock_test_and_set(ptr, newval);
#else #else
# error "Unsupported architecture for atomic operations" # error "Unsupported architecture for atomic operations"
#endif #endif
...@@ -178,9 +194,7 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr) ...@@ -178,9 +194,7 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
void *newval = NULL, *oldval = NULL; void *newval = NULL, *oldval = NULL;
volatile void *foundval = NULL; return (volatile void *)InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
return foundval;
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST); return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
...@@ -192,10 +206,14 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr) ...@@ -192,10 +206,14 @@ GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
#ifdef GIT_ARCH_64 #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) GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
return InterlockedExchangeAdd64(&a->val, addend); return InterlockedAdd64(&a->val, addend);
#elif defined(GIT_BUILTIN_ATOMIC) #elif defined(GIT_BUILTIN_ATOMIC)
return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST); return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
#elif defined(GIT_BUILTIN_SYNC) #elif defined(GIT_BUILTIN_SYNC)
...@@ -205,6 +223,9 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend) ...@@ -205,6 +223,9 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#endif #endif
} }
/*
* Atomically sets the contents of *a to be val.
*/
GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val) GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -218,6 +239,10 @@ GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val) ...@@ -218,6 +239,10 @@ GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
#endif #endif
} }
/*
* Atomically gets the contents of *a.
* @return the contents of *a.
*/
GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
{ {
#if defined(GIT_WIN32) #if defined(GIT_WIN32)
...@@ -297,11 +322,10 @@ GIT_INLINE(int) git_atomic32_get(git_atomic32 *a) ...@@ -297,11 +322,10 @@ GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
GIT_INLINE(void *) git_atomic__compare_and_swap( GIT_INLINE(void *) git_atomic__compare_and_swap(
void * volatile *ptr, void *oldval, void *newval) void * volatile *ptr, void *oldval, void *newval)
{ {
if (*ptr == oldval) void *foundval = *ptr;
if (foundval == oldval)
*ptr = newval; *ptr = newval;
else return foundval;
oldval = newval;
return oldval;
} }
GIT_INLINE(volatile void *) git_atomic__swap( GIT_INLINE(volatile void *) git_atomic__swap(
...@@ -339,17 +363,50 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a) ...@@ -339,17 +363,50 @@ GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
#endif #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) \ #define git_atomic_compare_and_swap(ptr, oldval, newval) \
git_atomic__compare_and_swap((void * volatile *)P, O, N) 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) #if defined(GIT_THREADS)
......
...@@ -18,7 +18,7 @@ static int basic_next_token( ...@@ -18,7 +18,7 @@ static int basic_next_token(
{ {
git_credential_userpass_plaintext *cred; git_credential_userpass_plaintext *cred;
git_buf raw = GIT_BUF_INIT; git_buf raw = GIT_BUF_INIT;
int error = -1; int error = GIT_EAUTH;
GIT_UNUSED(ctx); GIT_UNUSED(ctx);
......
...@@ -267,7 +267,7 @@ static int negotiate_init_context( ...@@ -267,7 +267,7 @@ static int negotiate_init_context(
if (!ctx->oid) { if (!ctx->oid) {
git_error_set(GIT_ERROR_NET, "negotiate authentication is not supported"); git_error_set(GIT_ERROR_NET, "negotiate authentication is not supported");
return -1; return GIT_EAUTH;
} }
git_buf_puts(&ctx->target, "HTTP@"); git_buf_puts(&ctx->target, "HTTP@");
......
...@@ -85,7 +85,7 @@ static int ntlm_next_token( ...@@ -85,7 +85,7 @@ static int ntlm_next_token(
git_buf input_buf = GIT_BUF_INIT; git_buf input_buf = GIT_BUF_INIT;
const unsigned char *msg; const unsigned char *msg;
size_t challenge_len, msg_len; size_t challenge_len, msg_len;
int error = -1; int error = GIT_EAUTH;
GIT_ASSERT_ARG(buf); GIT_ASSERT_ARG(buf);
GIT_ASSERT_ARG(ctx); GIT_ASSERT_ARG(ctx);
......
...@@ -162,7 +162,7 @@ static int handle_auth( ...@@ -162,7 +162,7 @@ static int handle_auth(
if (error > 0) { if (error > 0) {
git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type); git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type);
error = -1; error = GIT_EAUTH;
} }
if (!error) if (!error)
...@@ -179,7 +179,7 @@ GIT_INLINE(int) handle_remote_auth( ...@@ -179,7 +179,7 @@ GIT_INLINE(int) handle_remote_auth(
if (response->server_auth_credtypes == 0) { if (response->server_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support"); git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
return -1; return GIT_EAUTH;
} }
/* Otherwise, prompt for credentials. */ /* Otherwise, prompt for credentials. */
...@@ -201,7 +201,7 @@ GIT_INLINE(int) handle_proxy_auth( ...@@ -201,7 +201,7 @@ GIT_INLINE(int) handle_proxy_auth(
if (response->proxy_auth_credtypes == 0) { if (response->proxy_auth_credtypes == 0) {
git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support"); git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
return -1; return GIT_EAUTH;
} }
/* Otherwise, prompt for credentials. */ /* Otherwise, prompt for credentials. */
...@@ -259,7 +259,7 @@ static int handle_response( ...@@ -259,7 +259,7 @@ static int handle_response(
} else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED || } else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) { response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure"); git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
return -1; return GIT_EAUTH;
} }
if (response->status != GIT_HTTP_STATUS_OK) { if (response->status != GIT_HTTP_STATUS_OK) {
...@@ -416,7 +416,7 @@ static int http_stream_read( ...@@ -416,7 +416,7 @@ static int http_stream_read(
if (stream->state == HTTP_STATE_SENDING_REQUEST) { if (stream->state == HTTP_STATE_SENDING_REQUEST) {
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays"); 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; goto done;
} }
...@@ -554,7 +554,7 @@ static int http_stream_write( ...@@ -554,7 +554,7 @@ static int http_stream_write(
if (stream->state == HTTP_STATE_NONE) { if (stream->state == HTTP_STATE_NONE) {
git_error_set(GIT_ERROR_HTTP, git_error_set(GIT_ERROR_HTTP,
"too many redirects or authentication replays"); "too many redirects or authentication replays");
error = -1; error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
goto done; goto done;
} }
......
...@@ -597,6 +597,7 @@ static int apply_credentials( ...@@ -597,6 +597,7 @@ static int apply_credentials(
free_auth_context(server); free_auth_context(server);
} else if (!token.size) { } else if (!token.size) {
git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge"); git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge");
error = GIT_EAUTH;
error = -1; error = -1;
goto done; goto done;
} }
......
...@@ -461,13 +461,13 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char * ...@@ -461,13 +461,13 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char *
if (no_callback) { if (no_callback) {
git_error_set(GIT_ERROR_SSH, "authentication required but no callback set"); git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
return -1; return GIT_EAUTH;
} }
if (!(cred->credtype & auth_methods)) { if (!(cred->credtype & auth_methods)) {
cred->free(cred); cred->free(cred);
git_error_set(GIT_ERROR_SSH, "callback returned unsupported credentials type"); git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type");
return -1; return GIT_EAUTH;
} }
*out = cred; *out = cred;
...@@ -840,7 +840,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use ...@@ -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 */ /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
if (list == NULL && !libssh2_userauth_authenticated(session)) { if (list == NULL && !libssh2_userauth_authenticated(session)) {
ssh_error(session, "Failed to retrieve list of SSH authentication methods"); ssh_error(session, "Failed to retrieve list of SSH authentication methods");
return -1; return GIT_EAUTH;
} }
ptr = list; ptr = list;
......
...@@ -154,7 +154,7 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha ...@@ -154,7 +154,7 @@ static int apply_userpass_credentials(HINTERNET request, DWORD target, int mecha
native_scheme = WINHTTP_AUTH_SCHEME_BASIC; native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
} else { } else {
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme"); git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
error = -1; error = GIT_EAUTH;
goto done; goto done;
} }
...@@ -193,7 +193,7 @@ static int apply_default_credentials(HINTERNET request, DWORD target, int mechan ...@@ -193,7 +193,7 @@ static int apply_default_credentials(HINTERNET request, DWORD target, int mechan
native_scheme = WINHTTP_AUTH_SCHEME_NTLM; native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
} else { } else {
git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme"); git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
return -1; return GIT_EAUTH;
} }
/* /*
...@@ -616,7 +616,7 @@ static int parse_unauthorized_response( ...@@ -616,7 +616,7 @@ static int parse_unauthorized_response(
*/ */
if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) { if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
git_error_set(GIT_ERROR_OS, "failed to parse supported auth schemes"); git_error_set(GIT_ERROR_OS, "failed to parse supported auth schemes");
return -1; return GIT_EAUTH;
} }
if (WINHTTP_AUTH_SCHEME_NTLM & supported) { if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
...@@ -1040,7 +1040,7 @@ replay: ...@@ -1040,7 +1040,7 @@ replay:
/* Enforce a reasonable cap on the number of replays */ /* Enforce a reasonable cap on the number of replays */
if (replay_count++ >= GIT_HTTP_REPLAY_MAX) { if (replay_count++ >= GIT_HTTP_REPLAY_MAX) {
git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays"); 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 */ /* Connect if necessary */
......
...@@ -1636,3 +1636,49 @@ void test_checkout_tree__no_index_refresh(void) ...@@ -1636,3 +1636,49 @@ void test_checkout_tree__no_index_refresh(void)
modify_index_and_checkout_tree(&opts); modify_index_and_checkout_tree(&opts);
assert_status_entrycount(g_repo, 0); 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);
}
...@@ -172,7 +172,7 @@ void test_clone_nonetwork__can_checkout_given_branch(void) ...@@ -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_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_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); git_reference_free(remote_head);
} }
......
...@@ -964,7 +964,7 @@ void test_config_read__get_mapped(void) ...@@ -964,7 +964,7 @@ void test_config_read__get_mapped(void)
" key9 = off\n"); " key9 = off\n");
cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig")); 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_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map1, ARRAY_SIZE(_test_map1)));
cl_assert_equal_i(val, MAP_TRUE); cl_assert_equal_i(val, MAP_TRUE);
cl_git_pass(git_config_get_mapped(&val, cfg, "header.key2", _test_map1, ARRAY_SIZE(_test_map1))); 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) ...@@ -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))); 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_mapped(&val, cfg, "header.key1", _test_map2, ARRAY_SIZE(_test_map2)));
cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key1")); cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key1"));
cl_assert_equal_i(val, known_good); cl_assert_equal_i(val, known_good);
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include <git2.h> #include <git2.h>
#include <git2/sys/midx.h>
#include "futils.h"
#include "midx.h" #include "midx.h"
void test_pack_midx__parse(void) void test_pack_midx__parse(void)
...@@ -44,3 +46,65 @@ void test_pack_midx__lookup(void) ...@@ -44,3 +46,65 @@ void test_pack_midx__lookup(void)
git_commit_free(commit); git_commit_free(commit);
git_repository_free(repo); 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
...@@ -72,7 +72,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id, ...@@ -72,7 +72,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
.strings = &refspec_strs, .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_oid empty_tree_id;
git_tree *empty_tree; git_tree *empty_tree;
...@@ -92,7 +92,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id, ...@@ -92,7 +92,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_treebuilder_free(tb); git_treebuilder_free(tb);
} }
// fetch the reference via the remote /* fetch the reference via the remote */
{ {
git_remote *remote; git_remote *remote;
...@@ -103,7 +103,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id, ...@@ -103,7 +103,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_remote_free(remote); git_remote_free(remote);
} }
// assert that repo2 references the second commit /* assert that repo2 references the second commit */
{ {
const git_oid *target; const git_oid *target;
git_reference *ref; git_reference *ref;
...@@ -113,7 +113,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id, ...@@ -113,7 +113,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_reference_free(ref); 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 *ref;
git_reference *ref2; git_reference *ref2;
...@@ -124,7 +124,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id, ...@@ -124,7 +124,7 @@ void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
git_reference_free(ref2); git_reference_free(ref2);
} }
// fetch the reference again /* fetch the reference again */
{ {
git_remote *remote; git_remote *remote;
...@@ -144,7 +144,7 @@ void test_remote_fetch__dont_update_refs_if_not_descendant_and_not_force(void) { ...@@ -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); 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)); cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
target = git_reference_target(ref); target = git_reference_target(ref);
cl_assert_equal_b(git_oid_cmp(target, &commit2id), 0); 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) { ...@@ -160,7 +160,7 @@ void test_remote_fetch__do_update_refs_if_not_descendant_and_force(void) {
do_time_travelling_fetch(&commit1id, &commit2id, true); 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)); cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
target = git_reference_target(ref); target = git_reference_target(ref);
cl_assert_equal_b(git_oid_cmp(target, &commit1id), 0); cl_assert_equal_b(git_oid_cmp(target, &commit1id), 0);
......
#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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment