Merge branch 'main' into ethomson/worktree-config

This commit is contained in:
Edward Thomson
2024-03-11 22:41:25 +00:00
committed by GitHub
22 changed files with 483 additions and 190 deletions

View File

@@ -40,6 +40,7 @@ runs:
-e PKG_CONFIG_PATH \
-e SKIP_NEGOTIATE_TESTS \
-e SKIP_SSH_TESTS \
-e SKIP_PUSHOPTIONS_TESTS \
-e TSAN_OPTIONS \
-e UBSAN_OPTIONS \
${{ inputs.container-version }} \

View File

@@ -178,6 +178,7 @@ jobs:
CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (CentOS 7, dynamically-loaded OpenSSL)"
id: centos7-dynamicopenssl
os: ubuntu-latest
@@ -187,6 +188,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
SKIP_NEGOTIATE_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (CentOS 8, OpenSSL)"
id: centos8-openssl
os: ubuntu-latest
@@ -218,6 +220,7 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (x86, Bionic, Clang, OpenSSL)"
container:
@@ -229,6 +232,7 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (x86, Bionic, GCC, OpenSSL)"
container:
@@ -239,6 +243,7 @@ jobs:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
- name: "Linux (arm32, Bionic, GCC, OpenSSL)"
container:
@@ -251,6 +256,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
GITTEST_FLAKY_STAT: true
os: ubuntu-latest
- name: "Linux (arm64, Bionic, GCC, OpenSSL)"
@@ -264,6 +270,7 @@ jobs:
CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
RUN_INVASIVE_TESTS: true
SKIP_PROXY_TESTS: true
SKIP_PUSHOPTIONS_TESTS: true
os: ubuntu-latest
# Nightly builds: ensure we fallback when missing core functionality
@@ -276,6 +283,7 @@ jobs:
CC: gcc
CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
CMAKE_GENERATOR: Ninja
SKIP_PUSHOPTIONS_TESTS: true
- name: "Linux (no mmap)"
id: noble-nommap
os: ubuntu-latest

View File

@@ -113,7 +113,7 @@ Getting Help
**Getting Help**
If you have questions about the library, please be sure to check out the
[API documentation](http://libgit2.github.com/libgit2/). If you still have
[API documentation](https://libgit2.org/libgit2/). If you still have
questions, reach out to us on Slack or post a question on
[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).

View File

@@ -18,6 +18,11 @@ if [[ "$(uname -s)" == MINGW* ]]; then
SKIP_NTLM_TESTS=1
fi
# older versions of git don't support push options
if [ -z "$SKIP_PUSHOPTIONS_TESTS" ]; then
export GITTEST_PUSH_OPTIONS=true
fi
SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
BUILD_DIR=$(pwd)
BUILD_PATH=${BUILD_PATH:=$PATH}
@@ -207,7 +212,6 @@ if should_run "SSH_TESTS"; then
echo "Starting SSH server..."
SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
cp -R "${SOURCE_DIR}/tests/resources/pushoptions.git" "${SSHD_DIR}/test.git"
ls -FlasR "${SSHD_DIR}"
cat >"${SSHD_DIR}/sshd_config" <<-EOF
Port 2222
@@ -325,10 +329,8 @@ if should_run "GITDAEMON_TESTS"; then
echo ""
export GITTEST_REMOTE_URL="git://localhost/test.git"
export GITTEST_PUSH_OPTIONS=true
run_test gitdaemon
unset GITTEST_REMOTE_URL
unset GITTEST_PUSH_OPTIONS
echo ""
echo "Running gitdaemon (namespace) tests"
@@ -383,12 +385,10 @@ if should_run "NTLM_TESTS"; then
export GITTEST_REMOTE_URL="http://localhost:9000/ntlm/test.git"
export GITTEST_REMOTE_USER="foo"
export GITTEST_REMOTE_PASS="baz"
export GITTEST_PUSH_OPTIONS=true
run_test auth_clone_and_push
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_PASS
unset GITTEST_PUSH_OPTIONS
echo ""
echo "Running NTLM tests (Apache emulation)"
@@ -397,12 +397,10 @@ if should_run "NTLM_TESTS"; then
export GITTEST_REMOTE_URL="http://localhost:9000/broken-ntlm/test.git"
export GITTEST_REMOTE_USER="foo"
export GITTEST_REMOTE_PASS="baz"
export GITTEST_PUSH_OPTIONS=true
run_test auth_clone_and_push
unset GITTEST_REMOTE_URL
unset GITTEST_REMOTE_USER
unset GITTEST_REMOTE_PASS
unset GITTEST_PUSH_OPTIONS
fi
if should_run "NEGOTIATE_TESTS" && -n "$GITTEST_NEGOTIATE_PASSWORD" ; then
@@ -452,20 +450,16 @@ if should_run "SSH_TESTS"; then
echo ""
export GITTEST_REMOTE_URL="ssh://localhost:2222/$SSHD_DIR/test.git"
export GITTEST_PUSH_OPTIONS=true
run_test ssh
unset GITTEST_REMOTE_URL
unset GITTEST_PUSH_OPTIONS
echo ""
echo "Running ssh tests (scp-style paths)"
echo ""
export GITTEST_REMOTE_URL="[localhost:2222]:$SSHD_DIR/test.git"
export GITTEST_PUSH_OPTIONS=true
run_test ssh
unset GITTEST_REMOTE_URL
unset GITTEST_PUSH_OPTIONS
unset GITTEST_SSH_CMD

View File

@@ -15,8 +15,8 @@ so there are no restrictions on their use.
For annotated HTML versions, see the "Examples" section of:
http://libgit2.github.com/libgit2
https://libgit2.org/libgit2
such as:
http://libgit2.github.com/libgit2/ex/HEAD/general.html
https://libgit2.org/libgit2/ex/HEAD/general.html

View File

@@ -31,8 +31,8 @@
* Git Internals that you will need to know to work with Git at this level,
* check out [Chapter 10][pg] of the Pro Git book.
*
* [lg]: http://libgit2.github.com
* [ap]: http://libgit2.github.com/libgit2
* [lg]: https://libgit2.org
* [ap]: https://libgit2.org/libgit2
* [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
*/
@@ -97,7 +97,7 @@ int lg2_general(git_repository *repo, int argc, char** argv)
*
* (Try running this program against tests/resources/testrepo.git.)
*
* [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
* [me]: https://libgit2.org/libgit2/#HEAD/group/repository
*/
repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git";
@@ -173,7 +173,7 @@ static void oid_parsing(git_oid *oid)
* working with raw objects, we'll need to get this structure from the
* repository.
*
* [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb
* [odb]: https://libgit2.org/libgit2/#HEAD/group/odb
*/
static void object_database(git_repository *repo, git_oid *oid)
{
@@ -262,7 +262,7 @@ static void object_database(git_repository *repo, git_oid *oid)
* of them here. You can read about the other ones in the [commit API
* docs][cd].
*
* [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit
* [cd]: https://libgit2.org/libgit2/#HEAD/group/commit
*/
static void commit_writing(git_repository *repo)
{
@@ -347,7 +347,7 @@ static void commit_writing(git_repository *repo)
* data in the commit - the author (name, email, datetime), committer
* (same), tree, message, encoding and parent(s).
*
* [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit
* [pco]: https://libgit2.org/libgit2/#HEAD/group/commit
*/
static void commit_parsing(git_repository *repo)
{
@@ -418,7 +418,7 @@ static void commit_parsing(git_repository *repo)
* functions very similarly to the commit lookup, parsing and creation
* methods, since the objects themselves are very similar.
*
* [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag
* [tm]: https://libgit2.org/libgit2/#HEAD/group/tag
*/
static void tag_parsing(git_repository *repo)
{
@@ -472,7 +472,7 @@ static void tag_parsing(git_repository *repo)
* object type in Git, but a useful structure for parsing and traversing
* tree entries.
*
* [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree
* [tp]: https://libgit2.org/libgit2/#HEAD/group/tree
*/
static void tree_parsing(git_repository *repo)
{
@@ -536,7 +536,7 @@ static void tree_parsing(git_repository *repo)
* from disk and writing it to the db and getting the oid back so you
* don't have to do all those steps yourself.
*
* [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob
* [ba]: https://libgit2.org/libgit2/#HEAD/group/blob
*/
static void blob_parsing(git_repository *repo)
{
@@ -578,7 +578,7 @@ static void blob_parsing(git_repository *repo)
* that were ancestors of (reachable from) a given starting point. This
* can allow you to create `git log` type functionality.
*
* [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk
* [rw]: https://libgit2.org/libgit2/#HEAD/group/revwalk
*/
static void revwalking(git_repository *repo)
{
@@ -643,7 +643,7 @@ static void revwalking(git_repository *repo)
* The [index file API][gi] allows you to read, traverse, update and write
* the Git index file (sometimes thought of as the staging area).
*
* [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index
* [gi]: https://libgit2.org/libgit2/#HEAD/group/index
*/
static void index_walking(git_repository *repo)
{
@@ -687,7 +687,7 @@ static void index_walking(git_repository *repo)
* references such as branches, tags and remote references (everything in
* the .git/refs directory).
*
* [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference
* [ref]: https://libgit2.org/libgit2/#HEAD/group/reference
*/
static void reference_listing(git_repository *repo)
{
@@ -740,7 +740,7 @@ static void reference_listing(git_repository *repo)
* The [config API][config] allows you to list and update config values
* in any of the accessible config file locations (system, global, local).
*
* [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
* [config]: https://libgit2.org/libgit2/#HEAD/group/config
*/
static void config_files(const char *repo_path, git_repository* repo)
{

View File

@@ -50,6 +50,7 @@ static int add_revision(struct log_state *s, const char *revstr);
/** log_options holds other command line options that affect log output */
struct log_options {
int show_diff;
int show_oneline;
int show_log_size;
int skip, limit;
int min_parents, max_parents;
@@ -81,9 +82,11 @@ int lg2_log(git_repository *repo, int argc, char *argv[])
git_commit *commit = NULL;
git_pathspec *ps = NULL;
memset(&s, 0, sizeof(s));
/** Parse arguments and set up revwalker. */
last_arg = parse_options(&s, &opt, argc, argv);
s.repo = repo;
last_arg = parse_options(&s, &opt, argc, argv);
diffopts.pathspec.strings = &argv[last_arg];
diffopts.pathspec.count = argc - last_arg;
@@ -335,34 +338,45 @@ static void print_commit(git_commit *commit, struct log_options *opts)
const char *scan, *eol;
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
printf("commit %s\n", buf);
if (opts->show_log_size) {
printf("log size %d\n", (int)strlen(git_commit_message(commit)));
}
if (opts->show_oneline) {
printf("%s ", buf);
} else {
printf("commit %s\n", buf);
if ((count = (int)git_commit_parentcount(commit)) > 1) {
printf("Merge:");
for (i = 0; i < count; ++i) {
git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
printf(" %s", buf);
if (opts->show_log_size) {
printf("log size %d\n", (int)strlen(git_commit_message(commit)));
}
if ((count = (int)git_commit_parentcount(commit)) > 1) {
printf("Merge:");
for (i = 0; i < count; ++i) {
git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
printf(" %s", buf);
}
printf("\n");
}
if ((sig = git_commit_author(commit)) != NULL) {
printf("Author: %s <%s>\n", sig->name, sig->email);
print_time(&sig->when, "Date: ");
}
printf("\n");
}
if ((sig = git_commit_author(commit)) != NULL) {
printf("Author: %s <%s>\n", sig->name, sig->email);
print_time(&sig->when, "Date: ");
}
printf("\n");
for (scan = git_commit_message(commit); scan && *scan; ) {
for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
printf(" %.*s\n", (int)(eol - scan), scan);
if (opts->show_oneline)
printf("%.*s\n", (int)(eol - scan), scan);
else
printf(" %.*s\n", (int)(eol - scan), scan);
scan = *eol ? eol + 1 : NULL;
if (opts->show_oneline)
break;
}
printf("\n");
if (!opts->show_oneline)
printf("\n");
}
/** Helper to find how many files in a commit changed from its nth parent. */
@@ -407,8 +421,6 @@ static int parse_options(
struct log_state *s, struct log_options *opt, int argc, char **argv)
{
struct args_info args = ARGS_INFO_INIT;
memset(s, 0, sizeof(*s));
s->sorting = GIT_SORT_TIME;
memset(opt, 0, sizeof(*opt));
@@ -424,7 +436,7 @@ static int parse_options(
else
/** Try failed revision parse as filename. */
break;
} else if (!match_arg_separator(&args)) {
} else if (match_arg_separator(&args)) {
break;
}
else if (!strcmp(a, "--date-order"))
@@ -474,6 +486,8 @@ static int parse_options(
opt->show_diff = 1;
else if (!strcmp(a, "--log-size"))
opt->show_log_size = 1;
else if (!strcmp(a, "--oneline"))
opt->show_oneline = 1;
else
usage("Unsupported argument", a);
}

View File

@@ -19,20 +19,20 @@ GIT_BEGIN_DECL
/** Generic return codes */
typedef enum {
GIT_OK = 0, /**< No error */
GIT_OK = 0, /**< No error */
GIT_ERROR = -1, /**< Generic error */
GIT_ENOTFOUND = -3, /**< Requested object could not be found */
GIT_EEXISTS = -4, /**< Object exists preventing operation */
GIT_EAMBIGUOUS = -5, /**< More than one object matches */
GIT_EBUFS = -6, /**< Output buffer too short to hold data */
GIT_ERROR = -1, /**< Generic error */
GIT_ENOTFOUND = -3, /**< Requested object could not be found */
GIT_EEXISTS = -4, /**< Object exists preventing operation */
GIT_EAMBIGUOUS = -5, /**< More than one object matches */
GIT_EBUFS = -6, /**< Output buffer too short to hold data */
/**
* GIT_EUSER is a special error that is never generated by libgit2
* code. You can return it from a callback (e.g to stop an iteration)
* to know that it was generated by the callback and not by libgit2.
*/
GIT_EUSER = -7,
GIT_EUSER = -7,
GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
@@ -61,7 +61,8 @@ typedef enum {
GIT_EOWNER = -36, /**< The object is not owned by the current user */
GIT_TIMEOUT = -37, /**< The operation timed out */
GIT_EUNCHANGED = -38, /**< There were no changes */
GIT_EREADONLY = -39 /**< The subject is read-only */
GIT_ENOTSUPPORTED = -39, /**< An option is not supported */
GIT_EREADONLY = -40 /**< The subject is read-only */
} git_error_code;
/**

View File

@@ -85,8 +85,9 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
typedef struct git_worktree_add_options {
unsigned int version;
int lock; /**< lock newly created worktree */
git_reference *ref; /**< reference to use for the new worktree HEAD */
int lock; /**< lock newly created worktree */
int checkout_existing; /**< allow checkout of existing branch matching worktree name */
git_reference *ref; /**< reference to use for the new worktree HEAD */
/**
* Options for the checkout.
@@ -95,7 +96,8 @@ typedef struct git_worktree_add_options {
} git_worktree_add_options;
#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL,GIT_CHECKOUT_OPTIONS_INIT}
#define GIT_WORKTREE_ADD_OPTIONS_INIT { GIT_WORKTREE_ADD_OPTIONS_VERSION, \
0, 0, NULL, GIT_CHECKOUT_OPTIONS_INIT }
/**
* Initialize git_worktree_add_options structure

View File

@@ -29,6 +29,7 @@ typedef struct {
const char *new_prefix;
uint32_t flags;
int id_strlen;
unsigned int sent_file_header;
git_oid_t oid_type;
int (*strcomp)(const char *, const char *);
@@ -579,6 +580,30 @@ static int diff_print_patch_file_binary(
return error;
}
GIT_INLINE(int) should_force_header(const git_diff_delta *delta)
{
if (delta->old_file.mode != delta->new_file.mode)
return 1;
if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
return 1;
return 0;
}
GIT_INLINE(int) flush_file_header(const git_diff_delta *delta, diff_print_info *pi)
{
if (pi->sent_file_header)
return 0;
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
pi->line.content = git_str_cstr(pi->buf);
pi->line.content_len = git_str_len(pi->buf);
pi->sent_file_header = 1;
return pi->print_cb(delta, NULL, &pi->line, pi->payload);
}
static int diff_print_patch_file(
const git_diff_delta *delta, float progress, void *data)
{
@@ -609,15 +634,22 @@ static int diff_print_patch_file(
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
pi->sent_file_header = 0;
if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
id_strlen, print_index)) < 0)
return error;
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
pi->line.content = git_str_cstr(pi->buf);
pi->line.content_len = git_str_len(pi->buf);
/*
* pi->buf now contains the file header data. Go ahead and send it
* if there's useful data in there, like similarity. Otherwise, we
* should queue it to send when we see the first hunk. This prevents
* us from sending a header when all hunks were ignored.
*/
if (should_force_header(delta) && (error = flush_file_header(delta, pi)) < 0)
return error;
return pi->print_cb(delta, NULL, &pi->line, pi->payload);
return 0;
}
static int diff_print_patch_binary(
@@ -632,6 +664,9 @@ static int diff_print_patch_binary(
pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
int error;
if ((error = flush_file_header(delta, pi)) < 0)
return error;
git_str_clear(pi->buf);
if ((error = diff_print_patch_file_binary(
@@ -651,10 +686,14 @@ static int diff_print_patch_hunk(
void *data)
{
diff_print_info *pi = data;
int error;
if (S_ISDIR(d->new_file.mode))
return 0;
if ((error = flush_file_header(d, pi)) < 0)
return error;
pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
pi->line.content = h->header;
pi->line.content_len = h->header_len;
@@ -669,10 +708,14 @@ static int diff_print_patch_line(
void *data)
{
diff_print_info *pi = data;
int error;
if (S_ISDIR(delta->new_file.mode))
return 0;
if ((error = flush_file_header(delta, pi)) < 0)
return error;
return pi->print_cb(delta, hunk, line, pi->payload);
}

View File

@@ -10,7 +10,7 @@
#endif
#ifndef LIBGIT2_COMMENTS
# define LIBGIT2_COMMENTS "For more information visit http://libgit2.github.com/"
# define LIBGIT2_COMMENTS "For more information visit https://libgit2.org/"
#endif
#ifdef __GNUC__

View File

@@ -410,7 +410,9 @@ static const char *loose_parse_symbolic(git_str *file_content)
static bool is_per_worktree_ref(const char *ref_name)
{
return git__prefixcmp(ref_name, "refs/") != 0 ||
git__prefixcmp(ref_name, "refs/bisect/") == 0;
git__prefixcmp(ref_name, "refs/bisect/") == 0 ||
git__prefixcmp(ref_name, "refs/worktree/") == 0 ||
git__prefixcmp(ref_name, "refs/rewritten/") == 0;
}
static int loose_lookup(
@@ -805,83 +807,149 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
git__free(iter);
}
struct iter_load_context {
refdb_fs_backend *backend;
refdb_fs_iter *iter;
/*
* If we have a glob with a prefix (eg `refs/heads/ *`) then we can
* optimize our prefix to avoid walking refs that we know won't
* match. This is that prefix.
*/
const char *ref_prefix;
size_t ref_prefix_len;
/* Temporary variables to avoid unnecessary allocations */
git_str ref_name;
git_str path;
};
static void iter_load_optimize_prefix(struct iter_load_context *ctx)
{
const char *pos, *last_sep = NULL;
if (!ctx->iter->glob)
return;
for (pos = ctx->iter->glob; *pos; pos++) {
switch (*pos) {
case '?':
case '*':
case '[':
case '\\':
break;
case '/':
last_sep = pos;
/* FALLTHROUGH */
default:
continue;
}
break;
}
if (last_sep) {
ctx->ref_prefix = ctx->iter->glob;
ctx->ref_prefix_len = (last_sep - ctx->ref_prefix) + 1;
}
}
static int iter_load_paths(
struct iter_load_context *ctx,
const char *root_path,
bool worktree)
{
git_iterator *fsit = NULL;
git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
const git_index_entry *entry;
int error = 0;
fsit_opts.flags = ctx->backend->iterator_flags;
git_str_clear(&ctx->path);
git_str_puts(&ctx->path, root_path);
git_str_put(&ctx->path, ctx->ref_prefix, ctx->ref_prefix_len);
fsit_opts.flags = ctx->backend->iterator_flags;
fsit_opts.oid_type = ctx->backend->oid_type;
if ((error = git_iterator_for_filesystem(&fsit, ctx->path.ptr, &fsit_opts)) < 0) {
/*
* Subdirectories - either glob provided or per-worktree refs - need
* not exist.
*/
if ((worktree || ctx->iter->glob) && error == GIT_ENOTFOUND)
error = 0;
goto done;
}
git_str_clear(&ctx->ref_name);
git_str_put(&ctx->ref_name, ctx->ref_prefix, ctx->ref_prefix_len);
while (git_iterator_advance(&entry, fsit) == 0) {
char *ref_dup;
git_str_truncate(&ctx->ref_name, ctx->ref_prefix_len);
git_str_puts(&ctx->ref_name, entry->path);
if (worktree) {
if (!is_per_worktree_ref(ctx->ref_name.ptr))
continue;
} else {
if (git_repository_is_worktree(ctx->backend->repo) &&
is_per_worktree_ref(ctx->ref_name.ptr))
continue;
}
if (git__suffixcmp(ctx->ref_name.ptr, ".lock") == 0)
continue;
if (ctx->iter->glob && wildmatch(ctx->iter->glob, ctx->ref_name.ptr, 0))
continue;
ref_dup = git_pool_strdup(&ctx->iter->pool, ctx->ref_name.ptr);
GIT_ERROR_CHECK_ALLOC(ref_dup);
if ((error = git_vector_insert(&ctx->iter->loose, ref_dup)) < 0)
goto done;
}
done:
git_iterator_free(fsit);
return error;
}
#define iter_load_context_init(b, i) { b, i, GIT_REFS_DIR, CONST_STRLEN(GIT_REFS_DIR) }
#define iter_load_context_dispose(ctx) do { \
git_str_dispose(&((ctx)->path)); \
git_str_dispose(&((ctx)->ref_name)); \
} while(0)
static int iter_load_loose_paths(
refdb_fs_backend *backend,
refdb_fs_iter *iter)
{
int error = 0;
git_str path = GIT_STR_INIT;
git_iterator *fsit = NULL;
git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
const git_index_entry *entry = NULL;
const char *ref_prefix = GIT_REFS_DIR;
size_t ref_prefix_len = strlen(ref_prefix);
struct iter_load_context ctx = iter_load_context_init(backend, iter);
if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
int error = 0;
if (!backend->commonpath)
return 0;
fsit_opts.flags = backend->iterator_flags;
fsit_opts.oid_type = backend->oid_type;
iter_load_optimize_prefix(&ctx);
if (iter->glob) {
const char *last_sep = NULL;
const char *pos;
for (pos = iter->glob; *pos; ++pos) {
switch (*pos) {
case '?':
case '*':
case '[':
case '\\':
break;
case '/':
last_sep = pos;
/* FALLTHROUGH */
default:
continue;
}
break;
}
if (last_sep) {
ref_prefix = iter->glob;
ref_prefix_len = (last_sep - ref_prefix) + 1;
}
if ((error = iter_load_paths(&ctx,
backend->commonpath, false)) < 0)
goto done;
if (git_repository_is_worktree(backend->repo)) {
if ((error = iter_load_paths(&ctx,
backend->gitpath, true)) < 0)
goto done;
}
if ((error = git_str_puts(&path, backend->commonpath)) < 0 ||
(error = git_str_put(&path, ref_prefix, ref_prefix_len)) < 0) {
git_str_dispose(&path);
return error;
}
if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
git_str_dispose(&path);
return (iter->glob && error == GIT_ENOTFOUND)? 0 : error;
}
error = git_str_sets(&path, ref_prefix);
while (!error && !git_iterator_advance(&entry, fsit)) {
const char *ref_name;
char *ref_dup;
git_str_truncate(&path, ref_prefix_len);
git_str_puts(&path, entry->path);
ref_name = git_str_cstr(&path);
if (git__suffixcmp(ref_name, ".lock") == 0 ||
(iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
continue;
ref_dup = git_pool_strdup(&iter->pool, ref_name);
if (!ref_dup)
error = -1;
else
error = git_vector_insert(&iter->loose, ref_dup);
}
git_iterator_free(fsit);
git_str_dispose(&path);
done:
iter_load_context_dispose(&ctx);
return error;
}

View File

@@ -3281,14 +3281,18 @@ int git_repository_set_workdir(
if (git_fs_path_prettify_dir(&path, workdir, NULL) < 0)
return -1;
if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) {
git_str_dispose(&path);
return 0;
}
if (update_gitlink) {
git_config *config;
if (git_repository_config__weakptr(&config, repo) < 0)
if (git_repository_config__weakptr(&config, repo) < 0) {
git_str_dispose(&path);
return -1;
}
error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
@@ -3310,6 +3314,7 @@ int git_repository_set_workdir(
git__free(old_workdir);
}
git_str_dispose(&path);
return error;
}

View File

@@ -196,7 +196,7 @@ static void free_submodule_names(git_strmap *names)
*/
static int load_submodule_names(git_strmap **out, git_repository *repo, git_config *cfg)
{
const char *key = "submodule\\..*\\.path";
const char *key = "^submodule\\..*\\.path$";
git_config_iterator *iter = NULL;
git_config_entry *entry;
git_str buf = GIT_STR_INIT;
@@ -332,7 +332,7 @@ int git_submodule__lookup_with_cache(
/* If it's not configured or we're looking by path */
if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
git_config_backend *mods;
const char *pattern = "submodule\\..*\\.path";
const char *pattern = "^submodule\\..*\\.path$";
git_str path = GIT_STR_INIT;
fbp_data data = { NULL, NULL };

View File

@@ -303,6 +303,11 @@ static int local_negotiate_fetch(
GIT_UNUSED(wants);
if (wants->depth) {
git_error_set(GIT_ERROR_NET, "shallow fetch is not supported by the local transport");
return GIT_ENOTSUPPORTED;
}
/* Fill in the loids */
git_vector_foreach(&t->refs, i, rhead) {
git_object *obj;

View File

@@ -335,11 +335,21 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
goto out;
}
if (git_branch_is_checked_out(wtopts.ref)) {
git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out");
err = -1;
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
goto out;
}
} else if (wtopts.checkout_existing && git_branch_lookup(&ref, repo, name, GIT_BRANCH_LOCAL) == 0) {
/* Do nothing */
} else if ((err = git_repository_head(&head, repo)) < 0 ||
(err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0 ||
(err = git_branch_create(&ref, repo, name, commit, false)) < 0) {
goto out;
}
if (git_branch_is_checked_out(ref)) {
git_error_set(GIT_ERROR_WORKTREE, "reference %s is already checked out",
git_reference_name(ref));
err = -1;
goto out;
}
/* Create gitdir directory ".git/worktrees/<name>" */
@@ -392,19 +402,6 @@ int git_worktree_add(git_worktree **out, git_repository *repo,
|| (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
goto out;
/* Set up worktree reference */
if (wtopts.ref) {
if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
goto out;
} else {
if ((err = git_repository_head(&head, repo)) < 0)
goto out;
if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
goto out;
if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
goto out;
}
/* Set worktree's HEAD */
if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
goto out;

View File

@@ -210,3 +210,14 @@ void test_clone_local__git_style_unc_paths(void)
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
#endif
}
void test_clone_local__shallow_fails(void)
{
git_repository *repo;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
opts.fetch_opts.depth = 4;
cl_git_fail_with(GIT_ENOTSUPPORTED, git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts));
git_repository_free(repo);
}

View File

@@ -2286,42 +2286,81 @@ void test_diff_workdir__to_index_reversed_content_loads(void)
diff_expects exp;
int use_iterator;
char *pathspec = "new_file";
g_repo = cl_git_sandbox_init("status");
opts.context_lines = 3;
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_SHOW_UNTRACKED_CONTENT | GIT_DIFF_REVERSE;
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
memset(&exp, 0, sizeof(exp));
if (use_iterator)
cl_git_pass(diff_foreach_via_iterator(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
else
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(1, exp.lines);
cl_assert_equal_i(0, exp.line_ctxt);
cl_assert_equal_i(0, exp.line_adds);
cl_assert_equal_i(1, exp.line_dels);
}
git_diff_free(diff);
}
void test_diff_workdir__completely_ignored_shows_empty_diff(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff *diff;
git_patch *patch;
git_buf buf = GIT_BUF_INIT;
char *pathspec = "subdir.txt";
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
g_repo = cl_git_sandbox_init("status");
cl_git_rewritefile("status/subdir.txt", "Is it a bird?\n\nIs it a plane?\n");
/* Perform the diff normally */
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s("diff --git a/subdir.txt b/subdir.txt\nindex e8ee89e..53c8db5 100644\n--- a/subdir.txt\n+++ b/subdir.txt\n@@ -1,2 +1,3 @@\n Is it a bird?\n+\n Is it a plane?\n", buf.ptr);
git_buf_dispose(&buf);
git_patch_free(patch);
git_diff_free(diff);
/* Perform the diff ignoring blank lines */
opts.flags |= GIT_DIFF_IGNORE_BLANK_LINES;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
cl_git_pass(git_patch_from_diff(&patch, diff, 0));
cl_git_pass(git_patch_to_buf(&buf, patch));
cl_assert_equal_s("", buf.ptr);
git_buf_dispose(&buf);
git_patch_free(patch);
git_diff_free(diff);
}

View File

@@ -401,6 +401,24 @@ void test_submodule_lookup__prefix_name(void)
git_submodule_free(sm);
}
/* ".path" in name of submodule */
void test_submodule_lookup__dotpath_in_name(void)
{
sm_lookup_data data;
cl_git_rewritefile(
"submod2/.gitmodules", "[submodule \"kwb.pathdict\"]\n"
" path = kwb.pathdict\n"
" url = ../Test_App\n"
"[submodule \"fakin.path.app\"]\n"
" path = fakin.path.app\n"
" url = ../Test_App\n");
memset(&data, 0, sizeof(data));
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
cl_assert_equal_i(9, data.count);
}
void test_submodule_lookup__renamed(void)
{
const char *newpath = "sm_actually_changed";

View File

@@ -32,16 +32,11 @@ void test_trace_trace__cleanup(void)
void test_trace_trace__sets(void)
{
#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
#else
cl_skip();
#endif
}
void test_trace_trace__can_reset(void)
{
#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
cl_git_pass(git_trace_set(GIT_TRACE_ERROR, trace_callback));
@@ -51,14 +46,10 @@ void test_trace_trace__can_reset(void)
git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
cl_assert(written == 1);
#else
cl_skip();
#endif
}
void test_trace_trace__can_unset(void)
{
#ifdef GIT_TRACE
cl_assert(git_trace_level() == GIT_TRACE_INFO);
cl_git_pass(git_trace_set(GIT_TRACE_NONE, NULL));
@@ -67,40 +58,25 @@ void test_trace_trace__can_unset(void)
cl_assert(written == 0);
git_trace(GIT_TRACE_FATAL, "Hello %s!", "world");
cl_assert(written == 0);
#else
cl_skip();
#endif
}
void test_trace_trace__skips_higher_level(void)
{
#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_DEBUG, "Hello %s!", "world");
cl_assert(written == 0);
#else
cl_skip();
#endif
}
void test_trace_trace__writes(void)
{
#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
cl_assert(written == 1);
#else
cl_skip();
#endif
}
void test_trace_trace__writes_lower_level(void)
{
#ifdef GIT_TRACE
cl_assert(written == 0);
git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
cl_assert(written == 1);
#else
cl_skip();
#endif
}

View File

@@ -20,7 +20,7 @@ void test_worktree_refs__cleanup(void)
cleanup_fixture_worktree(&fixture);
}
void test_worktree_refs__list(void)
void test_worktree_refs__list_no_difference_in_worktree(void)
{
git_strarray refs, wtrefs;
unsigned i, j;
@@ -61,6 +61,66 @@ exit:
cl_git_pass(error);
}
void test_worktree_refs__list_worktree_specific(void)
{
git_strarray refs, wtrefs;
git_reference *ref, *new_branch;
int error = 0;
git_oid oid;
cl_git_pass(git_reference_name_to_id(&oid, fixture.repo, "refs/heads/dir"));
cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
cl_git_pass(git_reference_create(
&new_branch, fixture.worktree, "refs/bisect/a-bisect-ref", &oid,
0, "test"));
cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
cl_git_pass(git_reference_lookup(&ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
cl_git_pass(git_reference_list(&refs, fixture.repo));
cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
cl_assert_equal_sz(wtrefs.count, refs.count + 1);
git_reference_free(ref);
git_reference_free(new_branch);
git_strarray_dispose(&refs);
git_strarray_dispose(&wtrefs);
cl_git_pass(error);
}
void test_worktree_refs__list_worktree_specific_hidden_in_main_repo(void)
{
git_strarray refs, wtrefs;
git_reference *ref, *new_branch;
int error = 0;
git_oid oid;
cl_git_pass(
git_reference_name_to_id(&oid, fixture.repo, "refs/heads/dir"));
cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(
&ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
cl_git_pass(git_reference_create(
&new_branch, fixture.repo, "refs/bisect/a-bisect-ref", &oid,
0, "test"));
cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(
&ref, fixture.worktree, "refs/bisect/a-bisect-ref"));
cl_git_pass(git_reference_lookup(
&ref, fixture.repo, "refs/bisect/a-bisect-ref"));
cl_git_pass(git_reference_list(&refs, fixture.repo));
cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
cl_assert_equal_sz(refs.count, wtrefs.count + 1);
git_reference_free(ref);
git_reference_free(new_branch);
git_strarray_dispose(&refs);
git_strarray_dispose(&wtrefs);
cl_git_pass(error);
}
void test_worktree_refs__read_head(void)
{
git_reference *head;

View File

@@ -217,6 +217,50 @@ void test_worktree_worktree__init(void)
git_repository_free(repo);
}
void test_worktree_worktree__add_remove_add(void)
{
git_worktree_add_options add_opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
git_str path = GIT_BUF_INIT;
git_reference *branch;
git_repository *repo;
git_worktree *wt;
/* Add the worktree */
cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-add-remove-add"));
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, NULL));
/* Open and verify created repo */
cl_git_pass(git_repository_open(&repo, path.ptr));
cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
git_reference_free(branch);
git_repository_free(repo);
/* Prune the worktree */
opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
cl_git_pass(git_worktree_prune(wt, &opts));
cl_assert(!git_fs_path_exists(wt->gitdir_path));
cl_assert(!git_fs_path_exists(wt->gitlink_path));
git_worktree_free(wt);
/* Add the worktree back with default options should fail. */
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
/* If allowing checkout of existing branches, it should succeed. */
add_opts.checkout_existing = 1;
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-add-remove-add", path.ptr, &add_opts));
/* Open and verify created repo */
cl_git_pass(git_repository_open(&repo, path.ptr));
cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-add-remove-add/") == 0);
cl_git_pass(git_branch_lookup(&branch, repo, "worktree-add-remove-add", GIT_BRANCH_LOCAL));
git_reference_free(branch);
git_repository_free(repo);
git_str_dispose(&path);
git_worktree_free(wt);
}
void test_worktree_worktree__add_locked(void)
{
git_worktree *wt;
@@ -244,6 +288,7 @@ void test_worktree_worktree__add_locked(void)
void test_worktree_worktree__init_existing_branch(void)
{
git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
git_reference *head, *branch;
git_commit *commit;
git_worktree *wt;
@@ -251,12 +296,18 @@ void test_worktree_worktree__init_existing_branch(void)
cl_git_pass(git_repository_head(&head, fixture.repo));
cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new-exist", commit, false));
cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new-exist"));
/* Add the worktree back with default options should fail. */
cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, NULL));
/* If allowing checkout of existing branches, it should succeed. */
opts.checkout_existing = 1;
cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new-exist", path.ptr, &opts));
git_str_dispose(&path);
git_worktree_free(wt);
git_commit_free(commit);
git_reference_free(head);
git_reference_free(branch);