Commit a4827a5b by Ben Straub

Merge remote-tracking branch 'upstream/development' into test-merge

parents 15445f9e 31637cd5
......@@ -6,8 +6,10 @@ language: erlang
# Settings to try
env:
- OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
- OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON"
- CC=gcc OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
- CC=clang OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
- CC=gcc OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON"
- CC=clang OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON"
- CC=i586-mingw32msvc-gcc OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON"
# Make sure CMake is installed
......
......@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
struct dl_data {
git_remote *remote;
......@@ -39,7 +40,7 @@ exit:
pthread_exit(&data->ret);
}
int update_cb(const char *refname, const git_oid *a, const git_oid *b)
int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
const char *action;
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
......@@ -65,6 +66,7 @@ int fetch(git_repository *repo, int argc, char **argv)
git_indexer_stats stats;
pthread_t worker;
struct dl_data data;
git_remote_callbacks callbacks;
// Figure out whether it's a named remote or a URL
printf("Fetching %s\n", argv[1]);
......@@ -73,6 +75,11 @@ int fetch(git_repository *repo, int argc, char **argv)
return -1;
}
// Set up the callbacks (only update_tips for now)
memset(&callbacks, 0, sizeof(callbacks));
callbacks.update_tips = &update_cb;
git_remote_set_callbacks(remote, &callbacks);
// Set up the information for the background worker thread
data.remote = remote;
data.bytes = &bytes;
......@@ -101,7 +108,7 @@ int fetch(git_repository *repo, int argc, char **argv)
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
// changed but all the neede objects are available locally.
if (git_remote_update_tips(remote, update_cb) < 0)
if (git_remote_update_tips(remote) < 0)
return -1;
git_remote_free(remote);
......
......@@ -26,9 +26,9 @@ GIT_BEGIN_DECL
* this target commit. If `force` is true and a reference
* already exists with the given name, it'll be replaced.
*
* @param oid_out Pointer where to store the OID of the target commit.
* The returned reference must be freed by the user.
*
* @param repo Repository where to store the branch.
* @param branch_out Pointer where to store the underlying reference.
*
* @param branch_name Name for the branch; this name is
* validated for consistency. It should also not conflict with
......@@ -46,7 +46,7 @@ GIT_BEGIN_DECL
* pointing to the provided target commit.
*/
GIT_EXTERN(int) git_branch_create(
git_oid *oid_out,
git_reference **branch_out,
git_repository *repo,
const char *branch_name,
const git_object *target,
......@@ -97,27 +97,62 @@ GIT_EXTERN(int) git_branch_foreach(
);
/**
* Move/rename an existing branch reference.
* Move/rename an existing local branch reference.
*
* @param repo Repository where lives the branch.
*
* @param old_branch_name Current name of the branch to be moved;
* this name is validated for consistency.
* @param branch Current underlying reference of the branch.
*
* @param new_branch_name Target name of the branch once the move
* is performed; this name is validated for consistency.
*
* @param force Overwrite existing branch.
*
* @return 0 on success, GIT_ENOTFOUND if the branch
* doesn't exist or an error code.
* @return 0 on success, or an error code.
*/
GIT_EXTERN(int) git_branch_move(
git_repository *repo,
const char *old_branch_name,
git_reference *branch,
const char *new_branch_name,
int force);
/**
* Lookup a branch by its name in a repository.
*
* The generated reference must be freed by the user.
*
* @param branch_out pointer to the looked-up branch reference
*
* @param repo the repository to look up the branch
*
* @param branch_name Name of the branch to be looked-up;
* this name is validated for consistency.
*
* @param branch_type Type of the considered branch. This should
* be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
*
* @return 0 on success; GIT_ENOTFOUND when no matching branch
* exists, otherwise an error code.
*/
GIT_EXTERN(int) git_branch_lookup(
git_reference **branch_out,
git_repository *repo,
const char *branch_name,
git_branch_t branch_type);
/**
* Return the reference supporting the remote tracking branch,
* given a local branch reference.
*
* @param tracking_out Pointer where to store the retrieved
* reference.
*
* @param branch Current underlying reference of the branch.
*
* @return 0 on success; GIT_ENOTFOUND when no remote tracking
* reference exists, otherwise an error code.
*/
GIT_EXTERN(int) git_branch_tracking(
git_reference **tracking_out,
git_reference *branch);
/** @} */
GIT_END_DECL
#endif
......@@ -46,6 +46,7 @@ enum {
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
};
/**
......
......@@ -167,6 +167,23 @@ GIT_EXTERN(int) git_object_typeisloose(git_otype type);
*/
GIT_EXTERN(size_t) git_object__size(git_otype type);
/**
* Recursively peel an object until an object of the specified
* type is met
*
* The retrieved `peeled` object is owned by the repository
* and should be closed with the `git_object_free` method.
*
* @param peeled Pointer to the peeled git_object
* @param object The object to be processed
* @param target_type The type of the requested object
* @return 0 or an error code
*/
GIT_EXTERN(int) git_object_peel(
git_object **peeled,
git_object *object,
git_otype target_type);
/** @} */
GIT_END_DECL
......
......@@ -23,6 +23,10 @@ GIT_BEGIN_DECL
/**
* Read the reflog for the given reference
*
* If there is no reflog file for the given
* reference yet, an empty reflog object will
* be returned.
*
* The reflog must be freed manually by using
* git_reflog_free().
*
......@@ -33,22 +37,26 @@ GIT_BEGIN_DECL
GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref);
/**
* Write a new reflog for the given reference
*
* If there is no reflog file for the given
* reference yet, it will be created.
* Write an existing in-memory reflog object back to disk
* using an atomic file lock.
*
* `oid_old` may be NULL in case it's a new reference.
* @param reflog an existing reflog object
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
/**
* Add a new entry to the reflog.
*
* `msg` is optional and can be NULL.
*
* @param ref the changed reference
* @param oid_old the OID the reference was pointing to
* @param reflog an existing reflog object
* @param new_oid the OID the reference is now pointing to
* @param committer the signature of the committer
* @param msg the reflog message
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, const git_signature *committer, const char *msg);
GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg);
/**
* Rename the reflog for the given reference
......@@ -87,6 +95,26 @@ GIT_EXTERN(unsigned int) git_reflog_entrycount(git_reflog *reflog);
GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx);
/**
* Remove an entry from the reflog by its index
*
* To ensure there's no gap in the log history, set the `rewrite_previosu_entry` to 1.
* When deleting entry `n`, member old_oid of entry `n-1` (if any) will be updated with
* the value of memeber new_oid of entry `n+1`.
*
* @param reflog a previously loaded reflog.
*
* @param idx the position of the entry to remove.
*
* @param rewrite_previous_entry 1 to rewrite the history; 0 otherwise.
*
* @return 0 on success or an error code.
*/
GIT_EXTERN(int) git_reflog_drop(
git_reflog *reflog,
unsigned int idx,
int rewrite_previous_entry);
/**
* Get the old oid
*
* @param entry a reflog entry
......
......@@ -363,26 +363,15 @@ GIT_EXTERN(int) git_reference_foreach_glob(
*/
GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
/**
* Return the reference supporting the remote tracking branch,
* given a reference branch.
*
* The input reference has to be located in the `refs/heads`
* namespace.
*
* @param tracking_ref Pointer where to store the retrieved
* reference.
* Check if a reference is a local branch.
*
* @param branch_ref A git local branch reference.
* @param ref A git reference
*
* @return 0 on success; GIT_ENOTFOUND when no remote tracking
* reference exists, otherwise an error code.
* @return 1 when the reference lives in the refs/heads
* namespace; 0 otherwise.
*/
GIT_EXTERN(int) git_reference_remote_tracking_from_branch(
git_reference **tracking_ref,
git_reference *branch_ref
);
GIT_EXTERN(int) git_reference_is_branch(git_reference *ref);
/** @} */
GIT_END_DECL
......
......@@ -80,6 +80,36 @@ GIT_EXTERN(const char *) git_remote_name(git_remote *remote);
GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
/**
* Get the remote's url for pushing
*
* @param remote the remote
* @return a pointer to the url or NULL if no special url for pushing is set
*/
GIT_EXTERN(const char *) git_remote_pushurl(git_remote *remote);
/**
* Set the remote's url
*
* Existing connections will not be updated.
*
* @param remote the remote
* @param url the url to set
* @return 0 or an error value
*/
GIT_EXTERN(int) git_remote_set_url(git_remote *remote, const char* url);
/**
* Set the remote's url for pushing
*
* Existing connections will not be updated.
*
* @param remote the remote
* @param url the url to set or NULL to clear the pushurl
* @return 0 or an error value
*/
GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
/**
* Set the remote's fetch refspec
*
* @param remote the remote
......@@ -190,7 +220,7 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
* @param remote the remote to update
* @param cb callback to run on each ref update. 'a' is the old value, 'b' is then new value
*/
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b));
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
/**
* Return whether a string is a valid remote URL
......@@ -238,6 +268,39 @@ GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const cha
GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
/**
* Argument to the completion callback which tells it which operation
* finished.
*/
typedef enum git_remote_completion_type {
GIT_REMOTE_COMPLETION_DOWNLOAD,
GIT_REMOTE_COMPLETION_INDEXING,
GIT_REMOTE_COMPLETION_ERROR,
} git_remote_completion_type;
/**
* The callback settings structure
*
* Set the calbacks to be called by the remote.
*/
struct git_remote_callbacks {
int (*progress)(const char *str, void *data);
int (*completion)(git_remote_completion_type type, void *data);
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
void *data;
};
/**
* Set the callbacks for a remote
*
* Note that the remote keeps its own copy of the data and you need to
* call this function again if you want to change the callbacks.
*
* @param remote the remote to configure
* @param callbacks a pointer to the user's callback settings
*/
GIT_EXTERN(void) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
/** @} */
GIT_END_DECL
#endif
......@@ -96,6 +96,8 @@ typedef enum {
* the top-level directory will be included (with a trailing
* slash on the entry name). Given this flag, the directory
* itself will not be included, but all the files in it will.
* - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given
* path will be treated as a literal path, and not as a pathspec.
*/
enum {
......@@ -104,6 +106,7 @@ enum {
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2),
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1 << 3),
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4),
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1 << 5),
};
/**
......
......@@ -179,6 +179,7 @@ typedef struct git_refspec git_refspec;
typedef struct git_remote git_remote;
typedef struct git_remote_head git_remote_head;
typedef struct git_remote_callbacks git_remote_callbacks;
/** @} */
GIT_END_DECL
......
......@@ -426,17 +426,7 @@ int git_attr_fnmatch__parse(
return -1;
} else {
/* strip '\' that might have be used for internal whitespace */
char *to = spec->pattern;
for (scan = spec->pattern; *scan; to++, scan++) {
if (*scan == '\\')
scan++; /* skip '\' but include next char */
if (to != scan)
*to = *scan;
}
if (to != scan) {
*to = '\0';
spec->length = (to - spec->pattern);
}
spec->length = git__unescape(spec->pattern);
}
return 0;
......
......@@ -7,8 +7,11 @@
#include "common.h"
#include "commit.h"
#include "branch.h"
#include "tag.h"
#include "config.h"
#include "refspec.h"
#include "git2/branch.h"
static int retrieve_branch_reference(
git_reference **branch_reference_out,
......@@ -48,8 +51,8 @@ static int create_error_invalid(const char *msg)
}
int git_branch_create(
git_oid *oid_out,
git_repository *repo,
git_reference **ref_out,
git_repository *repository,
const char *branch_name,
const git_object *target,
int force)
......@@ -60,10 +63,8 @@ int git_branch_create(
git_buf canonical_branch_name = GIT_BUF_INIT;
int error = -1;
assert(repo && branch_name && target && oid_out);
if (git_object_owner(target) != repo)
return create_error_invalid("The given target does not belong to this repository");
assert(branch_name && target && ref_out);
assert(git_object_owner(target) == repository);
target_type = git_object_type(target);
......@@ -90,17 +91,17 @@ int git_branch_create(
if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
goto cleanup;
if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
if (git_reference_create_oid(&branch, repository,
git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
goto cleanup;
git_oid_cpy(oid_out, git_reference_oid(branch));
*ref_out = branch;
error = 0;
cleanup:
if (target_type == GIT_OBJ_TAG)
git_object_free(commit);
git_reference_free(branch);
git_buf_free(&canonical_branch_name);
return error;
}
......@@ -111,6 +112,7 @@ int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_
git_reference *head = NULL;
int error;
assert(repo && branch_name);
assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
......@@ -183,28 +185,110 @@ int git_branch_foreach(
return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter);
}
int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
static int not_a_local_branch(git_reference *ref)
{
giterr_set(GITERR_INVALID, "Reference '%s' is not a local branch.", git_reference_name(ref));
return -1;
}
int git_branch_move(
git_reference *branch,
const char *new_branch_name,
int force)
{
git_reference *reference = NULL;
git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
int error = 0;
git_buf new_reference_name = GIT_BUF_INIT;
int error;
if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
goto cleanup;
assert(branch && new_branch_name);
/* We need to be able to return GIT_ENOTFOUND */
if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)
goto cleanup;
if (!git_reference_is_branch(branch))
return not_a_local_branch(branch);
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
goto cleanup;
error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
error = git_reference_rename(branch, git_buf_cstr(&new_reference_name), force);
cleanup:
git_reference_free(reference);
git_buf_free(&old_reference_name);
git_buf_free(&new_reference_name);
return error;
}
int git_branch_lookup(
git_reference **ref_out,
git_repository *repo,
const char *branch_name,
git_branch_t branch_type)
{
assert(ref_out && repo && branch_name);
return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
}
static int retrieve_tracking_configuration(
const char **out, git_reference *branch, const char *format)
{
git_config *config;
git_buf buf = GIT_BUF_INIT;
int error;
if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
return -1;
if (git_buf_printf(&buf, format,
git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
return -1;
error = git_config_get_string(out, config, git_buf_cstr(&buf));
git_buf_free(&buf);
return error;
}
int git_branch_tracking(
git_reference **tracking_out,
git_reference *branch)
{
const char *remote_name, *merge_name;
git_buf buf = GIT_BUF_INIT;
int error = -1;
git_remote *remote = NULL;
const git_refspec *refspec;
assert(tracking_out && branch);
if (!git_reference_is_branch(branch))
return not_a_local_branch(branch);
if ((error = retrieve_tracking_configuration(&remote_name, branch, "branch.%s.remote")) < 0)
goto cleanup;
if ((error = retrieve_tracking_configuration(&merge_name, branch, "branch.%s.merge")) < 0)
goto cleanup;
if (strcmp(".", remote_name) != 0) {
if ((error = git_remote_load(&remote, git_reference_owner(branch), remote_name)) < 0)
goto cleanup;
refspec = git_remote_fetchspec(remote);
if (refspec == NULL) {
error = GIT_ENOTFOUND;
goto cleanup;
}
if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
goto cleanup;
} else
if (git_buf_sets(&buf, merge_name) < 0)
goto cleanup;
error = git_reference_lookup(
tracking_out,
git_reference_owner(branch),
git_buf_cstr(&buf));
cleanup:
git_remote_free(remote);
git_buf_free(&buf);
return error;
}
/*
* Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_branch_h__
#define INCLUDE_branch_h__
#include "git2/branch.h"
struct git_branch {
char *remote; /* TODO: Make this a git_remote */
char *merge;
};
#endif
......@@ -496,3 +496,7 @@ bool git_buf_is_binary(const git_buf *buf)
return ((printable >> 7) < nonprintable);
}
void git_buf_unescape(git_buf *buf)
{
buf->size = git__unescape(buf->ptr);
}
......@@ -151,4 +151,7 @@ int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
/* Check if buffer looks like it contains binary data */
bool git_buf_is_binary(const git_buf *buf);
/* Unescape all characters in a buffer */
void git_buf_unescape(git_buf *buf);
#endif
......@@ -49,15 +49,6 @@
#include <regex.h>
extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__throw(error, ...) \
(git___throw(__VA_ARGS__), error)
extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__rethrow(error, ...) \
(git___rethrow(__VA_ARGS__), error)
#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
void giterr_set_oom(void);
......@@ -68,5 +59,4 @@ void giterr_set_regex(const regex_t *regex, int error_code);
#include "util.h"
#endif /* INCLUDE_common_h__ */
......@@ -983,9 +983,12 @@ static int write_section(git_filebuf *file, const char *key)
if (dot == NULL) {
git_buf_puts(&buf, key);
} else {
char *escaped;
git_buf_put(&buf, key, dot - key);
/* TODO: escape */
git_buf_printf(&buf, " \"%s\"", dot + 1);
escaped = escape_value(dot + 1);
GITERR_CHECK_ALLOC(escaped);
git_buf_printf(&buf, " \"%s\"", escaped);
git__free(escaped);
}
git_buf_puts(&buf, "]\n");
......
......@@ -20,14 +20,21 @@ static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
return NULL;
/* diff prefix will only be leading non-wildcards */
for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
for (scan = prefix.ptr; *scan; ++scan) {
if (git__iswildcard(*scan) &&
(scan == prefix.ptr || (*(scan - 1) != '\\')))
break;
}
git_buf_truncate(&prefix, scan - prefix.ptr);
if (prefix.size > 0)
return git_buf_detach(&prefix);
if (prefix.size <= 0) {
git_buf_free(&prefix);
return NULL;
}
git_buf_free(&prefix);
return NULL;
git_buf_unescape(&prefix);
return git_buf_detach(&prefix);
}
static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
......@@ -54,7 +61,11 @@ static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
return true;
git_vector_foreach(&diff->pathspec, i, match) {
int result = p_fnmatch(match->pattern, path, 0);
int result = strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
if (((diff->opts.flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH) == 0) &&
result == FNM_NOMATCH)
result = p_fnmatch(match->pattern, path, 0);
/* if we didn't match, look for exact dirname prefix match */
if (result == FNM_NOMATCH &&
......@@ -814,9 +825,9 @@ int git_diff_merge(
/* prefix strings also come from old pool, so recreate those.*/
onto->opts.old_prefix =
git_pool_strdup(&onto->pool, onto->opts.old_prefix);
git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix);
onto->opts.new_prefix =
git_pool_strdup(&onto->pool, onto->opts.new_prefix);
git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix);
}
git_vector_foreach(&onto_new, i, delta)
......@@ -826,4 +837,3 @@ int git_diff_merge(
return error;
}
......@@ -10,10 +10,6 @@
#include "mwindow.h"
typedef struct {
struct {
char last[1024];
} error;
git_error *last_error;
git_error error_t;
......
......@@ -333,3 +333,72 @@ int git_object__resolve_to_type(git_object **obj, git_otype type)
*obj = scan;
return error;
}
static int dereference_object(git_object **dereferenced, git_object *obj)
{
git_otype type = git_object_type(obj);
switch (type) {
case GIT_OBJ_COMMIT:
return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
break;
case GIT_OBJ_TAG:
return git_tag_target(dereferenced, (git_tag*)obj);
break;
default:
return GIT_ENOTFOUND;
break;
}
}
static int peel_error(int error, const char* msg)
{
giterr_set(GITERR_INVALID, "The given object cannot be peeled - %s", msg);
return error;
}
int git_object_peel(
git_object **peeled,
git_object *object,
git_otype target_type)
{
git_object *source, *deref = NULL;
assert(object);
if (git_object_type(object) == target_type)
return git_object__dup(peeled, object);
if (target_type == GIT_OBJ_BLOB
|| target_type == GIT_OBJ_ANY)
return peel_error(GIT_EAMBIGUOUS, "Ambiguous target type");
if (git_object_type(object) == GIT_OBJ_BLOB)
return peel_error(GIT_ERROR, "A blob cannot be dereferenced");
source = object;
while (true) {
if (dereference_object(&deref, source) < 0)
goto cleanup;
if (source != object)
git_object_free(source);
if (git_object_type(deref) == target_type) {
*peeled = deref;
return 0;
}
source = deref;
deref = NULL;
}
cleanup:
if (source != object)
git_object_free(source);
git_object_free(deref);
return -1;
}
......@@ -206,6 +206,11 @@ char *git_pool_strdup(git_pool *pool, const char *str)
return git_pool_strndup(pool, str, strlen(str));
}
char *git_pool_strdup_safe(git_pool *pool, const char *str)
{
return str ? git_pool_strdup(pool, str) : NULL;
}
char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
{
void *ptr;
......
......@@ -90,6 +90,13 @@ extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
extern char *git_pool_strdup(git_pool *pool, const char *str);
/**
* Allocate space and duplicate a string into it, NULL is no error.
*
* This is allowed only for pools with item_size == sizeof(char)
*/
extern char *git_pool_strdup_safe(git_pool *pool, const char *str);
/**
* Allocate space for the concatenation of two strings.
*
* This is allowed only for pools with item_size == sizeof(char)
......
......@@ -17,6 +17,8 @@
#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17)
#define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000"
struct git_reflog_entry {
git_oid oid_old;
git_oid oid_cur;
......@@ -28,6 +30,7 @@ struct git_reflog_entry {
struct git_reflog {
char *ref_name;
git_repository *owner;
git_vector entries;
};
......
......@@ -11,7 +11,6 @@
#include "fileops.h"
#include "pack.h"
#include "reflog.h"
#include "config.h"
#include <git2/tag.h>
#include <git2/object.h>
......@@ -1816,75 +1815,9 @@ int git_reference_has_log(
return result;
}
//TODO: How about also taking care of local tracking branches?
//cf. http://alblue.bandlem.com/2011/07/git-tip-of-week-tracking-branches.html
int git_reference_remote_tracking_from_branch(
git_reference **tracking_ref,
git_reference *branch_ref)
int git_reference_is_branch(git_reference *ref)
{
git_config *config = NULL;
const char *name, *remote, *merge;
git_buf buf = GIT_BUF_INIT;
int error = -1;
assert(tracking_ref && branch_ref);
name = git_reference_name(branch_ref);
if (git__prefixcmp(name, GIT_REFS_HEADS_DIR)) {
giterr_set(
GITERR_INVALID,
"Failed to retrieve tracking reference - '%s' is not a branch.",
name);
return -1;
}
if (git_repository_config(&config, branch_ref->owner) < 0)
return -1;
if (git_buf_printf(
&buf,
"branch.%s.remote",
name + strlen(GIT_REFS_HEADS_DIR)) < 0)
goto cleanup;
if ((error = git_config_get_string(&remote, config, git_buf_cstr(&buf))) < 0)
goto cleanup;
error = -1;
git_buf_clear(&buf);
//TODO: Is it ok to fail when no merge target is found?
if (git_buf_printf(
&buf,
"branch.%s.merge",
name + strlen(GIT_REFS_HEADS_DIR)) < 0)
goto cleanup;
if (git_config_get_string(&merge, config, git_buf_cstr(&buf)) < 0)
goto cleanup;
//TODO: Should we test this?
if (git__prefixcmp(merge, GIT_REFS_HEADS_DIR))
goto cleanup;
git_buf_clear(&buf);
if (git_buf_printf(
&buf,
"refs/remotes/%s/%s",
remote,
merge + strlen(GIT_REFS_HEADS_DIR)) < 0)
goto cleanup;
error = git_reference_lookup(
tracking_ref,
branch_ref->owner,
git_buf_cstr(&buf));
assert(ref);
cleanup:
git_config_free(config);
git_buf_free(&buf);
return error;
return git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0;
}
......@@ -131,6 +131,26 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
GITERR_CHECK_ALLOC(remote->url);
git_buf_clear(&buf);
if (git_buf_printf(&buf, "remote.%s.pushurl", name) < 0) {
error = -1;
goto cleanup;
}
error = git_config_get_string(&val, config, git_buf_cstr(&buf));
if (error == GIT_ENOTFOUND)
error = 0;
if (error < 0) {
error = -1;
goto cleanup;
}
if (val) {
remote->pushurl = git__strdup(val);
GITERR_CHECK_ALLOC(remote->pushurl);
}
git_buf_clear(&buf);
if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) {
error = -1;
goto cleanup;
......@@ -179,7 +199,7 @@ int git_remote_save(const git_remote *remote)
if (git_repository_config__weakptr(&config, remote->repo) < 0)
return -1;
if (git_buf_printf(&buf, "remote.%s.%s", remote->name, "url") < 0)
if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0)
return -1;
if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
......@@ -187,6 +207,26 @@ int git_remote_save(const git_remote *remote)
return -1;
}
git_buf_clear(&buf);
if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0)
return -1;
if (remote->pushurl) {
if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) {
git_buf_free(&buf);
return -1;
}
} else {
int error = git_config_delete(config, git_buf_cstr(&buf));
if (error == GIT_ENOTFOUND) {
error = 0;
}
if (error < 0) {
git_buf_free(&buf);
return -1;
}
}
if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {
git_buf_clear(&buf);
git_buf_clear(&value);
......@@ -238,6 +278,38 @@ const char *git_remote_url(git_remote *remote)
return remote->url;
}
int git_remote_set_url(git_remote *remote, const char* url)
{
assert(remote);
assert(url);
git__free(remote->url);
remote->url = git__strdup(url);
GITERR_CHECK_ALLOC(remote->url);
return 0;
}
const char *git_remote_pushurl(git_remote *remote)
{
assert(remote);
return remote->pushurl;
}
int git_remote_set_pushurl(git_remote *remote, const char* url)
{
assert(remote);
git__free(remote->pushurl);
if (url) {
remote->pushurl = git__strdup(url);
GITERR_CHECK_ALLOC(remote->pushurl);
} else {
remote->pushurl = NULL;
}
return 0;
}
int git_remote_set_fetchspec(git_remote *remote, const char *spec)
{
git_refspec refspec;
......@@ -284,13 +356,33 @@ const git_refspec *git_remote_pushspec(git_remote *remote)
return &remote->push;
}
const char* git_remote__urlfordirection(git_remote *remote, int direction)
{
assert(remote);
if (direction == GIT_DIR_FETCH) {
return remote->url;
}
if (direction == GIT_DIR_PUSH) {
return remote->pushurl ? remote->pushurl : remote->url;
}
return NULL;
}
int git_remote_connect(git_remote *remote, int direction)
{
git_transport *t;
const char *url;
assert(remote);
if (git_transport_new(&t, remote->url) < 0)
url = git_remote__urlfordirection(remote, direction);
if (url == NULL )
return -1;
if (git_transport_new(&t, url) < 0)
return -1;
t->check_cert = remote->check_cert;
......@@ -331,7 +423,7 @@ int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats
return git_fetch_download_pack(remote, bytes, stats);
}
int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b))
int git_remote_update_tips(git_remote *remote)
{
int error = 0;
unsigned int i = 0;
......@@ -377,12 +469,12 @@ int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, co
continue;
if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)
break;
goto on_error;
git_reference_free(ref);
if (cb != NULL) {
if (cb(refname.ptr, &old, &head->oid) < 0)
if (remote->callbacks.update_tips != NULL) {
if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.data) < 0)
goto on_error;
}
}
......@@ -429,6 +521,7 @@ void git_remote_free(git_remote *remote)
git__free(remote->push.src);
git__free(remote->push.dst);
git__free(remote->url);
git__free(remote->pushurl);
git__free(remote->name);
git__free(remote);
}
......@@ -525,3 +618,10 @@ void git_remote_check_cert(git_remote *remote, int check)
remote->check_cert = check;
}
void git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
{
assert(remote && callbacks);
memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
}
......@@ -14,13 +14,17 @@
struct git_remote {
char *name;
char *url;
char *pushurl;
git_vector refs;
struct git_refspec fetch;
struct git_refspec push;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
unsigned int need_pack:1,
check_cert;
};
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
#endif
......@@ -98,6 +98,13 @@ struct git_repository {
* export */
void git_object__free(void *object);
GIT_INLINE(int) git_object__dup(git_object **dest, git_object *source)
{
git_cached_obj_incref(source);
*dest = source;
return 0;
}
int git_object__resolve_to_type(git_object **obj, git_otype type);
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
......
......@@ -99,6 +99,8 @@ int git_status_foreach_ext(
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
if ((opts->flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
/* TODO: support EXCLUDE_SUBMODULES flag */
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
......@@ -176,10 +178,12 @@ static int get_one_status(const char *path, unsigned int status, void *data)
sfi->count++;
sfi->status = status;
if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {
if (sfi->count > 1 ||
(strcmp(sfi->expected, path) != 0 &&
p_fnmatch(sfi->expected, path, 0) != 0)) {
giterr_set(GITERR_INVALID,
"Ambiguous path '%s' given to git_status_file", sfi->expected);
return -1;
return GIT_EAMBIGUOUS;
}
return 0;
......
......@@ -759,11 +759,12 @@ int git_tree_entry_bypath(
return error;
}
static int tree_walk_post(
static int tree_walk(
git_tree *tree,
git_treewalk_cb callback,
git_buf *path,
void *payload)
void *payload,
bool preorder)
{
int error = 0;
unsigned int i;
......@@ -771,8 +772,8 @@ static int tree_walk_post(
for (i = 0; i < tree->entries.length; ++i) {
git_tree_entry *entry = tree->entries.contents[i];
if (callback(path->ptr, entry, payload) < 0)
continue;
if (preorder && callback(path->ptr, entry, payload) < 0)
return -1;
if (git_tree_entry__is_tree(entry)) {
git_tree *subtree;
......@@ -789,12 +790,15 @@ static int tree_walk_post(
if (git_buf_oom(path))
return -1;
if (tree_walk_post(subtree, callback, path, payload) < 0)
if (tree_walk(subtree, callback, path, payload, preorder) < 0)
return -1;
git_buf_truncate(path, path_len);
git_tree_free(subtree);
}
if (!preorder && callback(path->ptr, entry, payload) < 0)
return -1;
}
return 0;
......@@ -807,12 +811,12 @@ int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payl
switch (mode) {
case GIT_TREEWALK_POST:
error = tree_walk_post(tree, callback, &root_path, payload);
error = tree_walk(tree, callback, &root_path, payload, false);
break;
case GIT_TREEWALK_PRE:
tree_error("Preorder tree walking is still not implemented");
return -1;
error = tree_walk(tree, callback, &root_path, payload, true);
break;
default:
giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
......
......@@ -435,3 +435,21 @@ int git__parse_bool(int *out, const char *value)
return -1;
}
size_t git__unescape(char *str)
{
char *scan, *pos = str;
for (scan = str; *scan; pos++, scan++) {
if (*scan == '\\' && *(scan + 1) != '\0')
scan++; /* skip '\' but include next char */
if (pos != scan)
*pos = *scan;
}
if (pos != scan) {
*pos = '\0';
}
return (pos - str);
}
......@@ -238,4 +238,13 @@ extern int git__parse_bool(int *out, const char *value);
*/
int git__date_parse(git_time_t *out, const char *date);
/*
* Unescapes a string in-place.
*
* Edge cases behavior:
* - "jackie\" -> "jacky\"
* - "chan\\" -> "chan\"
*/
extern size_t git__unescape(char *str);
#endif /* INCLUDE_util_h__ */
......@@ -59,3 +59,26 @@ void test_config_stress__comments(void)
git_config_free(config);
}
void test_config_stress__escape_subsection_names(void)
{
struct git_config_file *file;
git_config *config;
const char *str;
cl_assert(git_path_exists("git-test-config"));
cl_git_pass(git_config_file__ondisk(&file, "git-test-config"));
cl_git_pass(git_config_new(&config));
cl_git_pass(git_config_add_file(config, file, 0));
cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo"));
git_config_free(config);
cl_git_pass(git_config_file__ondisk(&file, "git-test-config"));
cl_git_pass(git_config_new(&config));
cl_git_pass(git_config_add_file(config, file, 0));
cl_git_pass(git_config_get_string(&str, config, "some.sec\\tion.other"));
cl_assert(!strcmp("foo", str));
git_config_free(config);
}
......@@ -658,3 +658,23 @@ void test_core_buffer__puts_escaped(void)
git_buf_free(&a);
}
static void assert_unescape(char *expected, char *to_unescape) {
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_sets(&buf, to_unescape));
git_buf_unescape(&buf);
cl_assert_equal_s(expected, buf.ptr);
cl_assert_equal_i(strlen(expected), buf.size);
git_buf_free(&buf);
}
void test_core_buffer__unescape(void)
{
assert_unescape("Escaped\\", "Es\\ca\\ped\\");
assert_unescape("Es\\caped\\", "Es\\\\ca\\ped\\\\");
assert_unescape("\\", "\\");
assert_unescape("\\", "\\\\");
assert_unescape("", "");
}
......@@ -208,3 +208,51 @@ void test_diff_tree__bare(void)
git_tree_free(a);
git_tree_free(b);
}
void test_diff_tree__merge(void)
{
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "605812a";
const char *b_commit = "370fe9ec22";
const char *c_commit = "f5b0af1fb4f5c";
git_tree *a, *b, *c;
git_diff_list *diff1 = NULL, *diff2 = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("attr");
cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
cl_git_pass(git_diff_tree_to_tree(g_repo, NULL, a, b, &diff1));
cl_git_pass(git_diff_tree_to_tree(g_repo, NULL, c, b, &diff2));
git_tree_free(a);
git_tree_free(b);
git_tree_free(c);
cl_git_pass(git_diff_merge(diff1, diff2));
git_diff_list_free(diff2);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff1, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 6);
cl_assert(exp.file_adds == 2);
cl_assert(exp.file_dels == 1);
cl_assert(exp.file_mods == 3);
cl_assert(exp.hunks == 6);
cl_assert(exp.lines == 59);
cl_assert(exp.line_ctxt == 1);
cl_assert(exp.line_adds == 36);
cl_assert(exp.line_dels == 22);
git_diff_list_free(diff1);
}
......@@ -107,7 +107,7 @@ void test_network_remotelocal__retrieve_advertised_references(void)
cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
cl_assert_equal_i(how_many_refs, 22);
cl_assert_equal_i(how_many_refs, 23);
}
void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void)
......@@ -121,7 +121,7 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi
cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
cl_assert_equal_i(how_many_refs, 22);
cl_assert_equal_i(how_many_refs, 23);
git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
remote = NULL;
......
......@@ -2,6 +2,7 @@
#include "buffer.h"
#include "refspec.h"
#include "transport.h"
#include "remote.h"
static git_remote *_remote;
static git_repository *_repo;
......@@ -27,8 +28,37 @@ void test_network_remotes__cleanup(void)
void test_network_remotes__parsing(void)
{
git_remote *_remote2 = NULL;
cl_assert_equal_s(git_remote_name(_remote), "test");
cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
cl_assert(git_remote_pushurl(_remote) == NULL);
cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIR_FETCH),
"git://github.com/libgit2/libgit2");
cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIR_PUSH),
"git://github.com/libgit2/libgit2");
cl_git_pass(git_remote_load(&_remote2, _repo, "test_with_pushurl"));
cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl");
cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2");
cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2");
cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIR_FETCH),
"git://github.com/libgit2/fetchlibgit2");
cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIR_PUSH),
"git://github.com/libgit2/pushlibgit2");
git_remote_free(_remote2);
}
void test_network_remotes__pushurl(void)
{
cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/notlibgit2"));
cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/notlibgit2");
cl_git_pass(git_remote_set_pushurl(_remote, NULL));
cl_assert(git_remote_pushurl(_remote) == NULL);
}
void test_network_remotes__parsing_ssh_remote(void)
......@@ -81,6 +111,7 @@ void test_network_remotes__save(void)
cl_git_pass(git_remote_new(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2", NULL));
cl_git_pass(git_remote_set_fetchspec(_remote, "refs/heads/*:refs/remotes/upstream/*"));
cl_git_pass(git_remote_set_pushspec(_remote, "refs/heads/*:refs/heads/*"));
cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
cl_git_pass(git_remote_save(_remote));
git_remote_free(_remote);
_remote = NULL;
......@@ -98,6 +129,18 @@ void test_network_remotes__save(void)
cl_assert(_refspec != NULL);
cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
cl_assert_equal_s(git_refspec_dst(_refspec), "refs/heads/*");
cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
/* remove the pushurl again and see if we can save that too */
cl_git_pass(git_remote_set_pushurl(_remote, NULL));
cl_git_pass(git_remote_save(_remote));
git_remote_free(_remote);
_remote = NULL;
cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
cl_assert(git_remote_pushurl(_remote) == NULL);
}
void test_network_remotes__fnmatch(void)
......@@ -143,13 +186,13 @@ void test_network_remotes__list(void)
git_config *cfg;
cl_git_pass(git_remote_list(&list, _repo));
cl_assert(list.count == 1);
cl_assert(list.count == 2);
git_strarray_free(&list);
cl_git_pass(git_repository_config(&cfg, _repo));
cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
cl_git_pass(git_remote_list(&list, _repo));
cl_assert(list.count == 2);
cl_assert(list.count == 3);
git_strarray_free(&list);
git_config_free(cfg);
......@@ -180,4 +223,5 @@ void test_network_remotes__add(void)
cl_assert(!strcmp(git_refspec_src(_refspec), "refs/heads/*"));
cl_assert(git_refspec_force(_refspec) == 1);
cl_assert(!strcmp(git_refspec_dst(_refspec), "refs/remotes/addtest/*"));
cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
}
#include "clar_libgit2.h"
static git_repository *g_repo;
void test_object_peel__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
}
void test_object_peel__cleanup(void)
{
git_repository_free(g_repo);
}
static void assert_peel(const char* expected_sha, const char *sha, git_otype requested_type)
{
git_oid oid, expected_oid;
git_object *obj;
git_object *peeled;
cl_git_pass(git_oid_fromstr(&oid, sha));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_git_pass(git_object_peel(&peeled, obj, requested_type));
cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
git_object_free(peeled);
git_object_free(obj);
}
static void assert_peel_error(int error, const char *sha, git_otype requested_type)
{
git_oid oid;
git_object *obj;
git_object *peeled;
cl_git_pass(git_oid_fromstr(&oid, sha));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_assert_equal_i(error, git_object_peel(&peeled, obj, requested_type));
git_object_free(obj);
}
void test_object_peel__peeling_an_object_into_its_own_type_returns_another_instance_of_it(void)
{
assert_peel("e90810b8df3e80c413d903f631643c716887138d", "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TAG);
assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB);
}
void test_object_peel__can_peel_a_tag(void)
{
assert_peel("e90810b8df3e80c413d903f631643c716887138d", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_COMMIT);
assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TREE);
}
void test_object_peel__can_peel_a_commit(void)
{
assert_peel("53fc32d17276939fc79ed05badaef2db09990016", "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_TREE);
}
void test_object_peel__cannot_peel_a_tree(void)
{
assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB);
}
void test_object_peel__cannot_peel_a_blob(void)
{
assert_peel_error(GIT_ERROR, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT);
}
void test_object_peel__cannot_target_any_object(void)
{
assert_peel_error(GIT_EAMBIGUOUS, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY);
}
......@@ -29,8 +29,18 @@ static int foreach_cb(git_oid *oid, void *data)
return 0;
}
/*
* $ git --git-dir tests-clar/resources/testrepo.git count-objects --verbose
* count: 43
* size: 3
* in-pack: 1640
* packs: 3
* size-pack: 425
* prune-packable: 0
* garbage: 0
*/
void test_odb_foreach__foreach(void)
{
cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL));
cl_assert(nobj == 1683);
cl_assert_equal_i(43 + 1640, nobj); /* count + in-pack */
}
#include "clar_libgit2.h"
#include "refs.h"
#include "branch.h"
static git_repository *repo;
static git_oid branch_target_oid;
static git_object *target;
static git_reference *branch;
void test_refs_branches_create__initialize(void)
{
cl_fixture_sandbox("testrepo.git");
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
branch = NULL;
}
void test_refs_branches_create__cleanup(void)
{
git_reference_free(branch);
git_object_free(target);
git_repository_free(repo);
......@@ -39,54 +42,24 @@ void test_refs_branches_create__can_create_a_local_branch(void)
{
retrieve_known_commit(&target, repo);
cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
cl_git_pass(git_oid_cmp(&branch_target_oid, git_object_id(target)));
}
void test_refs_branches_create__creating_a_local_branch_triggers_the_creation_of_a_new_direct_reference(void)
{
git_reference *branch;
retrieve_known_commit(&target, repo);
cl_git_fail(git_reference_lookup(&branch, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
cl_git_pass(git_reference_lookup(&branch, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
cl_assert(git_reference_type(branch) == GIT_REF_OID);
git_reference_free(branch);
cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
cl_git_pass(git_oid_cmp(git_reference_oid(branch), git_object_id(target)));
}
void test_refs_branches_create__can_not_create_a_branch_if_its_name_collide_with_an_existing_one(void)
{
retrieve_known_commit(&target, repo);
cl_git_fail(git_branch_create(&branch_target_oid, repo, "br2", target, 0));
cl_git_fail(git_branch_create(&branch, repo, "br2", target, 0));
}
void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
{
retrieve_known_commit(&target, repo);
cl_git_pass(git_branch_create(&branch_target_oid, repo, "br2", target, 1));
cl_git_pass(git_oid_cmp(&branch_target_oid, git_object_id(target)));
}
void test_refs_branches_create__can_not_create_a_branch_pointing_at_an_object_unknown_from_the_repository(void)
{
git_repository *repo2;
/* Open another instance of the same repository */
cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git")));
/* Retrieve a commit object from this different repository */
retrieve_known_commit(&target, repo2);
cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
git_repository_free(repo2);
cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1));
cl_git_pass(git_oid_cmp(git_reference_oid(branch), git_object_id(target)));
cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
}
void test_refs_branches_create__creating_a_branch_targeting_a_tag_dereferences_it_to_its_commit(void)
......@@ -94,8 +67,8 @@ void test_refs_branches_create__creating_a_branch_targeting_a_tag_dereferences_i
/* b25fa35 is a tag, pointing to another tag which points to a commit */
retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
cl_git_pass(git_oid_streq(&branch_target_oid, "e90810b8df3e80c413d903f631643c716887138d"));
cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
cl_git_pass(git_oid_streq(git_reference_oid(branch), "e90810b8df3e80c413d903f631643c716887138d"));
}
void test_refs_branches_create__can_not_create_a_branch_pointing_to_a_non_commit_object(void)
......@@ -103,11 +76,11 @@ void test_refs_branches_create__can_not_create_a_branch_pointing_to_a_non_commit
/* 53fc32d is the tree of commit e90810b */
retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
cl_git_fail(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
git_object_free(target);
/* 521d87c is an annotated tag pointing to a blob */
retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
cl_git_fail(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
}
#include "clar_libgit2.h"
#include "refs.h"
#include "branch.h"
static git_repository *repo;
static git_reference *fake_remote;
......
#include "clar_libgit2.h"
#include "refs.h"
#include "branch.h"
static git_repository *repo;
static git_reference *fake_remote;
......@@ -48,7 +47,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count)
void test_refs_branches_foreach__retrieve_all_branches(void)
{
assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 10);
assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 11);
}
void test_refs_branches_foreach__retrieve_remote_branches(void)
......@@ -58,7 +57,7 @@ void test_refs_branches_foreach__retrieve_remote_branches(void)
void test_refs_branches_foreach__retrieve_local_branches(void)
{
assert_retrieval(GIT_BRANCH_LOCAL, 8);
assert_retrieval(GIT_BRANCH_LOCAL, 9);
}
struct expectations {
......
#include "clar_libgit2.h"
#include "refs.h"
static git_repository *repo;
static git_reference *branch;
void test_refs_branches_lookup__initialize(void)
{
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
branch = NULL;
}
void test_refs_branches_lookup__cleanup(void)
{
git_reference_free(branch);
git_repository_free(repo);
}
void test_refs_branches_lookup__can_retrieve_a_local_branch(void)
{
cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
}
void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch(void)
{
cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_REMOTE));
}
void test_refs_branches_lookup__trying_to_retrieve_an_unknown_branch_returns_ENOTFOUND(void)
{
cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "where/are/you", GIT_BRANCH_LOCAL));
cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "over/here", GIT_BRANCH_REMOTE));
}
#include "clar_libgit2.h"
#include "branch.h"
#include "refs.h"
static git_repository *repo;
static git_reference *ref;
void test_refs_branches_move__initialize(void)
{
cl_fixture_sandbox("testrepo.git");
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
repo = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/br2"));
}
void test_refs_branches_move__cleanup(void)
{
git_repository_free(repo);
cl_fixture_cleanup("testrepo.git");
git_reference_free(ref);
cl_git_sandbox_cleanup();
}
#define NEW_BRANCH_NAME "new-branch-on-the-block"
void test_refs_branches_move__can_move_a_local_branch(void)
{
cl_git_pass(git_branch_move(repo, "br2", NEW_BRANCH_NAME, 0));
cl_git_pass(git_branch_move(ref, NEW_BRANCH_NAME, 0));
cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(ref));
}
void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void)
{
/* Downward */
cl_git_pass(git_branch_move(repo, "br2", "somewhere/" NEW_BRANCH_NAME, 0));
cl_git_pass(git_branch_move(ref, "somewhere/" NEW_BRANCH_NAME, 0));
/* Upward */
cl_git_pass(git_branch_move(repo, "somewhere/" NEW_BRANCH_NAME, "br2", 0));
cl_git_pass(git_branch_move(ref, "br2", 0));
}
void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void)
{
/* Downward */
cl_git_pass(git_branch_move(repo, "br2", "br2/" NEW_BRANCH_NAME, 0));
cl_git_pass(git_branch_move(ref, "br2/" NEW_BRANCH_NAME, 0));
/* Upward */
cl_git_pass(git_branch_move(repo, "br2/" NEW_BRANCH_NAME, "br2", 0));
cl_git_pass(git_branch_move(ref, "br2", 0));
}
void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void)
{
cl_git_fail(git_branch_move(repo, "br2", "master", 0));
cl_git_fail(git_branch_move(ref, "master", 0));
}
void test_refs_branches_move__can_not_move_a_non_existing_branch(void)
void test_refs_branches_move__can_not_move_a_non_branch(void)
{
cl_git_fail(git_branch_move(repo, "i-am-no-branch", NEW_BRANCH_NAME, 0));
}
git_reference *tag;
void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
{
cl_git_pass(git_branch_move(repo, "br2", "master", 1));
}
cl_git_pass(git_reference_lookup(&tag, repo, "refs/tags/e90810b"));
cl_git_fail(git_branch_move(tag, NEW_BRANCH_NAME, 0));
void test_refs_branches_move__can_not_move_a_branch_through_its_canonical_name(void)
{
cl_git_fail(git_branch_move(repo, "refs/heads/br2", NEW_BRANCH_NAME, 1));
git_reference_free(tag);
}
void test_refs_branches_move__moving_a_non_exisiting_branch_returns_ENOTFOUND(void)
void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
{
int error;
error = git_branch_move(repo, "where/am/I", NEW_BRANCH_NAME, 0);
cl_git_fail(error);
cl_assert_equal_i(GIT_ENOTFOUND, error);
cl_git_pass(git_branch_move(ref, "master", 1));
}
#include "clar_libgit2.h"
#include "refs.h"
static git_repository *repo;
static git_reference *branch;
void test_refs_branches_tracking__initialize(void)
{
cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
branch = NULL;
}
void test_refs_branches_tracking__cleanup(void)
{
git_reference_free(branch);
git_repository_free(repo);
}
void test_refs_branches_tracking__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
cl_git_pass(git_branch_tracking(&tracking, branch));
cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking));
git_reference_free(branch);
git_reference_free(tracking);
}
void test_refs_branches_tracking__can_retrieve_the_local_tracking_reference_of_a_local_branch(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local"));
cl_git_pass(git_branch_tracking(&tracking, branch));
cl_assert_equal_s("refs/heads/master", git_reference_name(tracking));
git_reference_free(branch);
git_reference_free(tracking);
}
void test_refs_branches_tracking__cannot_retrieve_a_remote_tracking_reference_from_a_non_branch(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
cl_git_fail(git_branch_tracking(&tracking, branch));
git_reference_free(branch);
}
void test_refs_branches_tracking__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees"));
cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch));
git_reference_free(branch);
}
......@@ -46,7 +46,7 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_
void test_refs_foreachglob__retrieve_all_refs(void)
{
/* 7 heads (including one packed head) + 1 note + 2 remotes + 6 tags */
assert_retrieval("*", GIT_REF_LISTALL, 17);
assert_retrieval("*", GIT_REF_LISTALL, 18);
}
void test_refs_foreachglob__retrieve_remote_branches(void)
......@@ -56,7 +56,7 @@ void test_refs_foreachglob__retrieve_remote_branches(void)
void test_refs_foreachglob__retrieve_local_branches(void)
{
assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 8);
assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 9);
}
void test_refs_foreachglob__retrieve_partially_named_references(void)
......
......@@ -202,3 +202,19 @@ void test_refs_read__unfound_return_ENOTFOUND(void)
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&reference, g_repo, "refs/tags/test/master"));
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master"));
}
static void assert_is_branch(const char *name, bool expected_branchness)
{
git_reference *reference;
cl_git_pass(git_reference_lookup(&reference, g_repo, name));
cl_assert_equal_i(expected_branchness, git_reference_is_branch(reference));
git_reference_free(reference);
}
void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void)
{
assert_is_branch("refs/heads/master", true);
assert_is_branch("refs/heads/packed", true);
assert_is_branch("refs/remotes/test/master", false);
assert_is_branch("refs/tags/e90810b", false);
}
#include "clar_libgit2.h"
#include "reflog.h"
static git_repository *g_repo;
static git_reflog *g_reflog;
static unsigned int entrycount;
void test_refs_reflog_drop__initialize(void)
{
git_reference *ref;
g_repo = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
git_reflog_read(&g_reflog, ref);
entrycount = git_reflog_entrycount(g_reflog);
git_reference_free(ref);
}
void test_refs_reflog_drop__cleanup(void)
{
git_reflog_free(g_reflog);
cl_git_sandbox_cleanup();
}
void test_refs_reflog_drop__dropping_a_non_exisiting_entry_from_the_log_returns_ENOTFOUND(void)
{
cl_assert_equal_i(GIT_ENOTFOUND, git_reflog_drop(g_reflog, entrycount, 0));
cl_assert_equal_i(entrycount, git_reflog_entrycount(g_reflog));
}
void test_refs_reflog_drop__can_drop_an_entry(void)
{
cl_assert(entrycount > 4);
cl_git_pass(git_reflog_drop(g_reflog, 2, 0));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
}
void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void)
{
const git_reflog_entry *before_previous, *before_next;
const git_reflog_entry *after_next;
git_oid before_next_old_oid;
cl_assert(entrycount > 4);
before_previous = git_reflog_entry_byindex(g_reflog, 3);
before_next = git_reflog_entry_byindex(g_reflog, 1);
git_oid_cpy(&before_next_old_oid, &before_next->oid_old);
cl_git_pass(git_reflog_drop(g_reflog, 2, 1));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
after_next = git_reflog_entry_byindex(g_reflog, 1);
cl_assert_equal_i(0, git_oid_cmp(&before_next->oid_cur, &after_next->oid_cur));
cl_assert(git_oid_cmp(&before_next_old_oid, &after_next->oid_old) != 0);
cl_assert_equal_i(0, git_oid_cmp(&before_previous->oid_cur, &after_next->oid_old));
}
void test_refs_reflog_drop__can_drop_the_first_entry(void)
{
cl_assert(entrycount > 2);
cl_git_pass(git_reflog_drop(g_reflog, 0, 0));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
}
void test_refs_reflog_drop__can_drop_the_last_entry(void)
{
const git_reflog_entry *entry;
cl_assert(entrycount > 2);
cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) != 0);
}
void test_refs_reflog_drop__can_drop_the_last_entry_and_rewrite_the_log_history(void)
{
const git_reflog_entry *entry;
cl_assert(entrycount > 2);
cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
}
void test_refs_reflog_drop__can_drop_all_the_entries(void)
{
cl_assert(--entrycount > 0);
do {
cl_git_pass(git_reflog_drop(g_reflog, --entrycount, 1));
} while (entrycount > 0);
cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
cl_assert_equal_i(0, git_reflog_entrycount(g_reflog));
}
void test_refs_reflog_drop__can_persist_deletion_on_disk(void)
{
git_reference *ref;
cl_assert(entrycount > 2);
cl_git_pass(git_reference_lookup(&ref, g_repo, g_reflog->ref_name));
cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
cl_git_pass(git_reflog_write(g_reflog));
git_reflog_free(g_reflog);
git_reflog_read(&g_reflog, ref);
git_reference_free(ref);
cl_assert_equal_i(entrycount - 1, git_reflog_entrycount(g_reflog));
}
......@@ -7,7 +7,7 @@
static const char *new_ref = "refs/heads/test-reflog";
static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
static const char *commit_msg = "commit: bla bla";
#define commit_msg "commit: bla bla"
static git_repository *g_repo;
......@@ -24,66 +24,60 @@ static void assert_signature(git_signature *expected, git_signature *actual)
// Fixture setup and teardown
void test_refs_reflog__initialize(void)
void test_refs_reflog_reflog__initialize(void)
{
g_repo = cl_git_sandbox_init("testrepo.git");
}
void test_refs_reflog__cleanup(void)
void test_refs_reflog_reflog__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_refs_reflog__write_then_read(void)
void test_refs_reflog_reflog__append_then_read(void)
{
// write a reflog for a given reference and ensure it can be read back
git_repository *repo2;
git_repository *repo2;
git_reference *ref, *lookedup_ref;
git_oid oid;
git_signature *committer;
git_reflog *reflog;
git_reflog_entry *entry;
char oid_str[GIT_OID_HEXSZ+1];
const git_reflog_entry *entry;
/* Create a new branch pointing at the HEAD */
git_oid_fromstr(&oid, current_master_tip);
cl_git_pass(git_reference_create_oid(&ref, g_repo, new_ref, &oid, 0));
git_reference_free(ref);
cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
cl_git_pass(git_reflog_write(ref, NULL, committer, NULL));
cl_git_fail(git_reflog_write(ref, NULL, committer, "no ancestor NULL for an existing reflog"));
cl_git_fail(git_reflog_write(ref, NULL, committer, "no\nnewline"));
cl_git_pass(git_reflog_write(ref, &oid, committer, commit_msg));
cl_git_pass(git_reflog_read(&reflog, ref));
cl_git_fail(git_reflog_append(reflog, &oid, committer, "no inner\nnewline"));
cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
cl_git_pass(git_reflog_write(reflog));
git_reflog_free(reflog);
/* Reopen a new instance of the repository */
cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
/* Lookup the preivously created branch */
/* Lookup the previously created branch */
cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
/* Read and parse the reflog for this branch */
cl_git_pass(git_reflog_read(&reflog, lookedup_ref));
cl_assert(reflog->entries.length == 2);
cl_assert_equal_i(2, git_reflog_entrycount(reflog));
entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 0);
entry = git_reflog_entry_byindex(reflog, 0);
assert_signature(committer, entry->committer);
git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old);
cl_assert_equal_s("0000000000000000000000000000000000000000", oid_str);
git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur);
cl_assert_equal_s(current_master_tip, oid_str);
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
cl_assert(git_oid_cmp(&oid, &entry->oid_cur) == 0);
cl_assert(entry->msg == NULL);
entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 1);
entry = git_reflog_entry_byindex(reflog, 1);
assert_signature(committer, entry->committer);
git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old);
cl_assert_equal_s(current_master_tip, oid_str);
git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur);
cl_assert_equal_s(current_master_tip, oid_str);
cl_assert(git_oid_cmp(&oid, &entry->oid_old) == 0);
cl_assert(git_oid_cmp(&oid, &entry->oid_cur) == 0);
cl_assert_equal_s(commit_msg, entry->msg);
git_signature_free(committer);
......@@ -94,35 +88,7 @@ void test_refs_reflog__write_then_read(void)
git_reference_free(lookedup_ref);
}
void test_refs_reflog__dont_write_bad(void)
{
// avoid writing an obviously wrong reflog
git_reference *ref;
git_oid oid;
git_signature *committer;
/* Create a new branch pointing at the HEAD */
git_oid_fromstr(&oid, current_master_tip);
cl_git_pass(git_reference_create_oid(&ref, g_repo, new_ref, &oid, 0));
git_reference_free(ref);
cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
/* Write the reflog for the new branch */
cl_git_pass(git_reflog_write(ref, NULL, committer, NULL));
/* Try to update the reflog with wrong information:
* It's no new reference, so the ancestor OID cannot
* be NULL. */
cl_git_fail(git_reflog_write(ref, NULL, committer, NULL));
git_signature_free(committer);
git_reference_free(ref);
}
void test_refs_reflog__renaming_the_reference_moves_the_reflog(void)
void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
{
git_reference *master;
git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
......@@ -145,6 +111,7 @@ void test_refs_reflog__renaming_the_reference_moves_the_reflog(void)
git_buf_free(&moved_log_path);
git_buf_free(&master_log_path);
}
static void assert_has_reflog(bool expected_result, const char *name)
{
git_reference *ref;
......@@ -156,9 +123,50 @@ static void assert_has_reflog(bool expected_result, const char *name)
git_reference_free(ref);
}
void test_refs_reflog__reference_has_reflog(void)
void test_refs_reflog_reflog__reference_has_reflog(void)
{
assert_has_reflog(true, "HEAD");
assert_has_reflog(true, "refs/heads/master");
assert_has_reflog(false, "refs/heads/subtrees");
}
void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
{
git_reference *subtrees;
git_reflog *reflog;
git_buf subtrees_log_path = GIT_BUF_INIT;
cl_git_pass(git_reference_lookup(&subtrees, g_repo, "refs/heads/subtrees"));
git_buf_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, git_reference_name(subtrees));
cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&subtrees_log_path)));
cl_git_pass(git_reflog_read(&reflog, subtrees));
cl_assert_equal_i(0, git_reflog_entrycount(reflog));
git_reflog_free(reflog);
git_reference_free(subtrees);
git_buf_free(&subtrees_log_path);
}
void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
{
git_reference *master;
git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
git_reflog *reflog;
cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
cl_git_pass(git_reflog_read(&reflog, master));
cl_git_pass(git_reflog_write(reflog));
cl_git_pass(git_reference_rename(master, "refs/moved", 0));
cl_git_fail(git_reflog_write(reflog));
git_reflog_free(reflog);
git_reference_free(master);
git_buf_free(&moved_log_path);
git_buf_free(&master_log_path);
}
#include "clar_libgit2.h"
static git_repository *g_repo;
void test_refs_remotetracking__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
}
void test_refs_remotetracking__cleanup(void)
{
git_repository_free(g_repo);
}
void test_refs_remotetracking__unfound_returns_GIT_ENOTFOUND(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/subtrees"));
cl_assert_equal_i(GIT_ENOTFOUND, git_reference_remote_tracking_from_branch(&tracking, branch));
git_reference_free(branch);
}
void test_refs_remotetracking__retrieving_from_a_non_head_fails(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/tags/e90810b"));
cl_git_fail(git_reference_remote_tracking_from_branch(&tracking, branch));
git_reference_free(branch);
}
void test_refs_remotetracking__can_retrieve_a_remote_tracking_branch_reference(void)
{
git_reference *branch, *tracking;
cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master"));
cl_git_pass(git_reference_remote_tracking_from_branch(&tracking, branch));
cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking));
git_reference_free(branch);
git_reference_free(tracking);
}
......@@ -64,6 +64,8 @@ void test_refs_revparse__invalid_reference_name(void)
cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't make sense"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't make sense^1"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't make sense~2"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, ""));
}
void test_refs_revparse__shas(void)
......@@ -75,6 +77,9 @@ void test_refs_revparse__shas(void)
void test_refs_revparse__head(void)
{
test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("HEAD^0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("HEAD~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
}
void test_refs_revparse__full_refs(void)
......@@ -99,12 +104,18 @@ void test_refs_revparse__describe_output(void)
void test_refs_revparse__nth_parent(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "be3563a^-1"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "^"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "be3563a^{tree}^"));
test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
test_object("be3563a^^", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("be3563a^{commit}^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("be3563a^42", NULL);
}
......@@ -113,34 +124,56 @@ void test_refs_revparse__not_tag(void)
{
test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master^{tree}^{}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
test_object("e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
test_object("tags/e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
test_object("e908^{}", "e90810b8df3e80c413d903f631643c716887138d");
}
void test_refs_revparse__to_type(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{trip}"));
test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
test_object("point_to_blob^{blob}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
cl_git_fail(git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}"));
test_object("master^{commit}^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
}
void test_refs_revparse__linear_history(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "~"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "foo~bar"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master~bar"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master~-1"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master~0bar"));
test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("master~~", "9fd738e8f7967c078dceed8190330fc8648ee56a");
}
void test_refs_revparse__chaining(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{0}@{0}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{u}@{-1}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-1}@{-1}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-3}@{0}"));
test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("@{-1}@{0}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
test_object("@{-4}@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
test_object("master^^2^", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479");
test_object("master^^1^2^1", NULL);
}
void test_refs_revparse__upstream(void)
......@@ -158,6 +191,10 @@ void test_refs_revparse__upstream(void)
void test_refs_revparse__ordinal(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{-2}"));
/* TODO: make the test below actually fail
* cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}"));
*/
test_object("nope@{0}", NULL);
test_object("master@{31415}", NULL);
......@@ -177,6 +214,7 @@ void test_refs_revparse__previous_head(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-xyz}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-0}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "@{-1b}"));
test_object("@{-42}", NULL);
......@@ -353,6 +391,7 @@ void test_refs_revparse__colon(void)
test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9");
test_object("test/master^2:branch_file.txt", "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
test_object("test/master@{1}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
}
void test_refs_revparse__disambiguation(void)
......
......@@ -7,6 +7,14 @@
url = git://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/test/*
[remote "test_with_pushurl"]
url = git://github.com/libgit2/fetchlibgit2
pushurl = git://github.com/libgit2/pushlibgit2
fetch = +refs/heads/*:refs/remotes/test_with_pushurl/*
[branch "master"]
remote = test
merge = refs/heads/master
[branch "track-local"]
remote = .
merge = refs/heads/master
0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0900 commit:
a65fedf39aefe402d3bb6e24df4d4f5fe4547750 5b5b025afb0b4c913b4c338a42934a3863bf3644 Ben Straub <bstraub@github.com> 1335806604 -0900 checkout: moving from master to 5b5b025
5b5b025afb0b4c913b4c338a42934a3863bf3644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806605 -0900 checkout: moving from 5b5b025 to master
a65fedf39aefe402d3bb6e24df4d4f5fe4547750 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0900 checkout: moving from master to br2
c47800c7266a2be04c571c04d5a6614691ea99bd a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0900 commit: checking in
a4a7dce85cf63874e984719f4fdd239f5145052f a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806621 -0900 checkout: moving from br2 to master
0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806565 -0800 update by push
a65fedf39aefe402d3bb6e24df4d4f5fe4547750 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806688 -0800 update by push
9fd738e8f7967c078dceed8190330fc8648ee56a
......@@ -517,6 +517,85 @@ void test_status_worktree__status_file_with_clean_index_and_empty_workdir(void)
cl_git_pass(p_unlink("my-index"));
}
void test_status_worktree__bracket_in_filename(void)
{
git_repository *repo;
git_index *index;
status_entry_single result;
unsigned int status_flags;
int error;
#define FILE_WITH_BRACKET "LICENSE[1].md"
#define FILE_WITHOUT_BRACKET "LICENSE1.md"
cl_git_pass(git_repository_init(&repo, "with_bracket", 0));
cl_git_mkfile("with_bracket/" FILE_WITH_BRACKET, "I have a bracket in my name\n");
/* file is new to working directory */
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(1, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
/* ignore the file */
cl_git_rewritefile("with_bracket/.gitignore", "*.md\n.gitignore\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_IGNORED);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_IGNORED);
/* don't ignore the file */
cl_git_rewritefile("with_bracket/.gitignore", ".gitignore\n");
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
/* add the file to the index */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_add(index, FILE_WITH_BRACKET, 0));
cl_git_pass(git_index_write(index));
memset(&result, 0, sizeof(result));
cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
cl_assert_equal_i(2, result.count);
cl_assert(result.status == GIT_STATUS_INDEX_NEW);
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
/* Create file without bracket */
cl_git_mkfile("with_bracket/" FILE_WITHOUT_BRACKET, "I have no bracket in my name!\n");
cl_git_pass(git_status_file(&status_flags, repo, FILE_WITHOUT_BRACKET));
cl_assert(status_flags == GIT_STATUS_WT_NEW);
cl_git_pass(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md"));
cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
error = git_status_file(&status_flags, repo, FILE_WITH_BRACKET);
cl_git_fail(error);
cl_assert(error == GIT_EAMBIGUOUS);
git_index_free(index);
git_repository_free(repo);
}
void test_status_worktree__space_in_filename(void)
{
......@@ -647,3 +726,49 @@ void test_status_worktree__filemode_changes(void)
git_config_free(cfg);
}
static int cb_status__expected_path(const char *p, unsigned int s, void *payload)
{
const char *expected_path = (const char *)payload;
GIT_UNUSED(s);
if (payload == NULL)
cl_fail("Unexpected path");
cl_assert_equal_s(expected_path, p);
return 0;
}
void test_status_worktree__disable_pathspec_match(void)
{
git_repository *repo;
git_status_options opts;
char *file_with_bracket = "LICENSE[1].md",
*imaginary_file_with_bracket = "LICENSE[1-2].md";
cl_git_pass(git_repository_init(&repo, "pathspec", 0));
cl_git_mkfile("pathspec/LICENSE[1].md", "screaming bracket\n");
cl_git_mkfile("pathspec/LICENSE1.md", "no bracket\n");
memset(&opts, 0, sizeof(opts));
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
opts.pathspec.count = 1;
opts.pathspec.strings = &file_with_bracket;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__expected_path,
file_with_bracket)
);
/* Test passing a pathspec matching files in the workdir. */
/* Must not match because pathspecs are disabled. */
opts.pathspec.strings = &imaginary_file_with_bracket;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__expected_path, NULL)
);
git_repository_free(repo);
}
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