Merge remote-tracking branch 'source/development' into development

This commit is contained in:
Chris Young 2012-06-13 23:28:51 +01:00
commit a21bb1aa33
89 changed files with 3138 additions and 283 deletions

View File

@ -6,6 +6,7 @@ Alexei Sholik
Andreas Ericsson
Ankur Sethi
Ben Noordhuis
Ben Straub
Benjamin C Meyer
Brian Lopez
Carlos Martín Nieto

View File

@ -155,6 +155,12 @@ SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR})
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
IF (MSVC)
# Precompiled headers
SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
SET_SOURCE_FILES_PROPERTIES(src/win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
ENDIF ()
# Install
INSTALL(TARGETS git2
RUNTIME DESTINATION ${INSTALL_BIN}
@ -187,6 +193,12 @@ IF (BUILD_CLAR)
)
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT} ${SSL_LIBRARIES})
IF (MSVC)
# Precompiled headers
SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
ENDIF ()
IF (WIN32)
TARGET_LINK_LIBRARIES(libgit2_clar ws2_32)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")

View File

@ -185,9 +185,7 @@ int main(int argc, char *argv[])
/* open repo */
check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
"Could not discover repository");
check(git_repository_open(&repo, path),
check(git_repository_open_ext(&repo, dir, 0, NULL),
"Could not open repository");
if (treeish1)

View File

@ -25,6 +25,7 @@
#include "git2/merge.h"
#include "git2/refs.h"
#include "git2/reflog.h"
#include "git2/revparse.h"
#include "git2/object.h"
#include "git2/blob.h"
@ -43,5 +44,6 @@
#include "git2/indexer.h"
#include "git2/submodule.h"
#include "git2/notes.h"
#include "git2/reset.h"
#endif

View File

@ -29,6 +29,10 @@
*/
GIT_BEGIN_DECL
/**
* Flags for diff options. A combination of these flags can be passed
* in via the `flags` value in the `git_diff_options`.
*/
enum {
GIT_DIFF_NORMAL = 0,
GIT_DIFF_REVERSE = (1 << 0),
@ -96,9 +100,9 @@ typedef enum {
typedef struct {
git_oid oid;
char *path;
uint16_t mode;
git_off_t size;
unsigned int flags;
uint16_t mode;
} git_diff_file;
/**
@ -160,15 +164,16 @@ typedef int (*git_diff_hunk_fn)(
* the file or hunk headers.
*/
enum {
/* these values will be sent to `git_diff_data_fn` along with the line */
/* These values will be sent to `git_diff_data_fn` along with the line */
GIT_DIFF_LINE_CONTEXT = ' ',
GIT_DIFF_LINE_ADDITION = '+',
GIT_DIFF_LINE_DELETION = '-',
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< DEPRECATED */
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
/* these values will only be sent to a `git_diff_data_fn` when the content
* of a diff is being formatted (eg. through git_diff_print_patch() or
* git_diff_print_compact(), for instance).
/* The following values will only be sent to a `git_diff_data_fn` when
* the content of a diff is being formatted (eg. through
* git_diff_print_patch() or git_diff_print_compact(), for instance).
*/
GIT_DIFF_LINE_FILE_HDR = 'F',
GIT_DIFF_LINE_HUNK_HDR = 'H',
@ -206,6 +211,8 @@ GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
/**
* Compute a difference between two tree objects.
*
* This is equivalent to `git diff <treeish> <treeish>`
*
* @param repo The repository containing the trees.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param old_tree A git_tree object to diff from.
@ -222,6 +229,9 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
/**
* Compute a difference between a tree and the index.
*
* This is equivalent to `git diff --cached <treeish>` or if you pass
* the HEAD tree, then like `git diff --cached`.
*
* @param repo The repository containing the tree and index.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param old_tree A git_tree object to diff from.
@ -236,6 +246,11 @@ GIT_EXTERN(int) git_diff_index_to_tree(
/**
* Compute a difference between the working directory and the index.
*
* This matches the `git diff` command. See the note below on
* `git_diff_workdir_to_tree` for a discussion of the difference between
* `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
* using libgit2.
*
* @param repo The repository.
* @param opts Structure with options to influence diff or NULL for defaults.
* @param diff A pointer to a git_diff_list pointer that will be allocated.
@ -248,14 +263,24 @@ GIT_EXTERN(int) git_diff_workdir_to_index(
/**
* Compute a difference between the working directory and a tree.
*
* This returns strictly the differences between the tree and the
* files contained in the working directory, regardless of the state
* of files in the index. There is no direct equivalent in C git.
* This is *NOT* the same as `git diff <treeish>`. Running `git diff HEAD`
* or the like actually uses information from the index, along with the tree
* and workdir dir info.
*
* This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'. Those
* commands diff the tree, the index, and the workdir. To emulate those
* functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
* then call `git_diff_merge` on the results.
* This function returns strictly the differences between the tree and the
* files contained in the working directory, regardless of the state of
* files in the index. It may come as a surprise, but there is no direct
* equivalent in core git.
*
* To emulate `git diff <treeish>`, you should call both
* `git_diff_index_to_tree` and `git_diff_workdir_to_index`, then call
* `git_diff_merge` on the results. That will yield a `git_diff_list` that
* matches the git output.
*
* If this seems confusing, take the case of a file with a staged deletion
* where the file has then been put back into the working dir and modified.
* The tree-to-workdir diff for that file is 'modified', but core git would
* show status 'deleted' since there is a pending deletion in the index.
*
* @param repo The repository containing the tree.
* @param opts Structure with options to influence diff or NULL for defaults.
@ -298,10 +323,23 @@ GIT_EXTERN(int) git_diff_merge(
/**
* Iterate over a diff list issuing callbacks.
*
* If the hunk and/or line callbacks are not NULL, then this will calculate
* text diffs for all files it thinks are not binary. If those are both
* NULL, then this will not bother with the text diffs, so it can be
* efficient.
* This will iterate through all of the files described in a diff. You
* should provide a file callback to learn about each file.
*
* The "hunk" and "line" callbacks are optional, and the text diff of the
* files will only be calculated if they are not NULL. Of course, these
* callbacks will not be invoked for binary files on the diff list or for
* files whose only changed is a file mode change.
*
* @param diff A git_diff_list generated by one of the above functions.
* @param cb_data Reference pointer that will be passed to your callbacks.
* @param file_cb Callback function to make per file in the diff.
* @param hunk_cb Optional callback to make per hunk of text diff. This
* callback is called to describe a range of lines in the
* diff. It will not be issued for binary files.
* @param line_cb Optional callback to make per line of diff text. This
* same callback will be made for context lines, added, and
* removed lines, and even for a deleted trailing newline.
*/
GIT_EXTERN(int) git_diff_foreach(
git_diff_list *diff,
@ -322,6 +360,14 @@ GIT_EXTERN(int) git_diff_print_compact(
* Iterate over a diff generating text output like "git diff".
*
* This is a super easy way to generate a patch from a diff.
*
* @param diff A git_diff_list generated by one of the above functions.
* @param cb_data Reference pointer that will be passed to your callbacks.
* @param print_cb Callback function to output lines of the diff. This
* same function will be called for file headers, hunk
* headers, and diff lines. Fortunately, you can probably
* use various GIT_DIFF_LINE constants to determine what
* text you are given.
*/
GIT_EXTERN(int) git_diff_print_patch(
git_diff_list *diff,
@ -338,13 +384,14 @@ GIT_EXTERN(int) git_diff_print_patch(
/**
* Directly run a text diff on two blobs.
*
* Compared to a file, a blob lacks some contextual information. As such, the
* `git_diff_file` parameters of the callbacks will be filled accordingly to the following:
* `mode` will be set to 0, `path` will be set to NULL. When dealing with a NULL blob, `oid`
* will be set to 0.
* Compared to a file, a blob lacks some contextual information. As such,
* the `git_diff_file` parameters of the callbacks will be filled
* accordingly to the following: `mode` will be set to 0, `path` will be set
* to NULL. When dealing with a NULL blob, `oid` will be set to 0.
*
* When at least one of the blobs being dealt with is binary, the `git_diff_delta` binary
* attribute will be set to 1 and no call to the hunk_cb nor line_cb will be made.
* When at least one of the blobs being dealt with is binary, the
* `git_diff_delta` binary attribute will be set to 1 and no call to the
* hunk_cb nor line_cb will be made.
*/
GIT_EXTERN(int) git_diff_blobs(
git_blob *old_blob,

View File

@ -19,6 +19,14 @@
*/
GIT_BEGIN_DECL
/**
* Parse a refspec string and create a refspec object
*
* @param refspec pointer to the refspec structure to be used
* @param str the refspec as a string
*/
GIT_EXTERN(int) git_refspec_parse(git_refspec *refspec, const char *str);
/**
* Get the source specifier
*

44
include/git2/reset.h Normal file
View File

@ -0,0 +1,44 @@
/*
* 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_git_reset_h__
#define INCLUDE_git_reset_h__
/**
* @file git2/reset.h
* @brief Git reset management routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Sets the current head to the specified commit oid and optionally
* resets the index and working tree to match.
*
* When specifying a Soft kind of reset, the head will be moved to the commit.
*
* Specifying a Mixed kind of reset will trigger a Soft reset and the index will
* be replaced with the content of the commit tree.
*
* TODO: Implement remaining kinds of resets.
*
* @param repo Repository where to perform the reset operation.
*
* @param target Object to which the Head should be moved to. This object
* must belong to the given `repo` and can either be a git_commit or a
* git_tag. When a git_tag is being passed, it should be dereferencable
* to a git_commit which oid will be used as the target of the branch.
*
* @param reset_type Kind of reset operation to perform.
*
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_reset(git_repository *repo, const git_object *target, git_reset_type reset_type);
/** @} */
GIT_END_DECL
#endif

36
include/git2/revparse.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 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_git_revparse_h__
#define INCLUDE_git_revparse_h__
#include "common.h"
#include "types.h"
/**
* @file git2/revparse.h
* @brief Git revision parsing routines
* @defgroup git_revparse Git revision parsing routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Find an object, as specified by a revision string. See `man gitrevisions`, or the documentation
* for `git rev-parse` for information on the syntax accepted.
*
* @param out pointer to output object
* @param repo the repository to search in
* @param spec the textual specification for an object
* @return on success, GIT_ERROR otherwise (use git_error_last for information about the error)
*/
GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
/** @} */
GIT_END_DECL
#endif

View File

@ -102,7 +102,7 @@ enum {
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1 << 0),
GIT_STATUS_OPT_INCLUDE_IGNORED = (1 << 1),
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2),
GIT_STATUS_OPT_EXCLUDE_SUBMODULED = (1 << 3),
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1 << 3),
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4),
};

View File

@ -169,6 +169,12 @@ typedef enum {
GIT_BRANCH_REMOTE = 2,
} git_branch_t;
/** Kinds of reset operation. */
typedef enum {
GIT_RESET_SOFT = 1,
GIT_RESET_MIXED = 2,
} git_reset_type;
typedef struct git_refspec git_refspec;
typedef struct git_remote git_remote;

View File

@ -415,7 +415,7 @@ int git_attr_cache__push_file(
if (parse && (error = parse(repo, content, file)) < 0)
goto finish;
git_strmap_insert(cache->files, file->key, file, error);
git_strmap_insert(cache->files, file->key, file, error); //-V595
if (error > 0)
error = 0;

View File

@ -149,9 +149,11 @@ static int branch_list_cb(const char *branch_name, void *payload)
{
branch_filter_data *filter = (branch_filter_data *)payload;
if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
|| (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))
return git_vector_insert(filter->branchlist, git__strdup(branch_name));
if (filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0) {
return git_vector_insert(filter->branchlist, git__strdup(branch_name +strlen(GIT_REFS_HEADS_DIR)));
} else if (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0) {
return git_vector_insert(filter->branchlist, git__strdup(branch_name+strlen(GIT_REFS_DIR)));
}
return 0;
}

View File

@ -144,8 +144,9 @@ int git_buf_puts(git_buf *buf, const char *string)
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
int len;
const size_t expected_size = buf->size + (strlen(format) * 2);
ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));
ENSURE_SIZE(buf, expected_size);
while (1) {
va_list args;

View File

@ -69,7 +69,7 @@ void *git_cache_try_store(git_cache *cache, void *_entry)
git_cached_obj *entry = _entry;
uint32_t hash;
memcpy(&hash, &entry->oid, sizeof(hash));
memcpy(&hash, &entry->oid, sizeof(uint32_t));
/* increase the refcount on this object, because
* the cache now owns it */

View File

@ -81,66 +81,6 @@ int git_commit_create_v(
return res;
}
/* Update the reference named `ref_name` so it points to `oid` */
static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
{
git_reference *ref;
int res;
res = git_reference_lookup(&ref, repo, ref_name);
/* If we haven't found the reference at all, we assume we need to create
* a new reference and that's it */
if (res == GIT_ENOTFOUND) {
giterr_clear();
return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
}
if (res < 0)
return -1;
/* If we have found a reference, but it's symbolic, we need to update
* the direct reference it points to */
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
git_reference *aux;
const char *sym_target;
/* The target pointed at by this reference */
sym_target = git_reference_target(ref);
/* resolve the reference to the target it points to */
res = git_reference_resolve(&aux, ref);
/*
* if the symbolic reference pointed to an inexisting ref,
* this is means we're creating a new branch, for example.
* We need to create a new direct reference with that name
*/
if (res == GIT_ENOTFOUND) {
giterr_clear();
res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
git_reference_free(ref);
return res;
}
/* free the original symbolic reference now; not before because
* we're using the `sym_target` pointer */
git_reference_free(ref);
if (res < 0)
return -1;
/* store the newly found direct reference in its place */
ref = aux;
}
/* ref is made to point to `oid`: ref is either the original reference,
* or the target of the symbolic reference we've looked up */
res = git_reference_set_oid(ref, oid);
git_reference_free(ref);
return res;
}
int git_commit_create(
git_oid *oid,
git_repository *repo,
@ -192,7 +132,7 @@ int git_commit_create(
git_buf_free(&commit);
if (update_ref != NULL)
return update_reference(repo, oid, update_ref);
return git_reference__update(repo, oid, update_ref);
return 0;

876
src/date.c Normal file
View File

@ -0,0 +1,876 @@
/*
* GIT - The information manager from hell
*
* Copyright (C) Linus Torvalds, 2005
*/
#include "common.h"
#ifndef GIT_WIN32
#include <sys/time.h>
#endif
#include "util.h"
#include "cache.h"
#include "posix.h"
#include <ctype.h>
#include <time.h>
typedef enum {
DATE_NORMAL = 0,
DATE_RELATIVE,
DATE_SHORT,
DATE_LOCAL,
DATE_ISO8601,
DATE_RFC2822,
DATE_RAW
} date_mode;
/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
*/
static git_time_t tm_to_time_t(const struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
int year = tm->tm_year - 70;
int month = tm->tm_mon;
int day = tm->tm_mday;
if (year < 0 || year > 129) /* algo only works for 1970-2099 */
return -1;
if (month < 0 || month > 11) /* array bounds */
return -1;
if (month < 2 || (year + 2) % 4)
day--;
if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
return -1;
return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
}
static const char *month_names[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
static const char *weekday_names[] = {
"Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
};
/*
* Check these. And note how it doesn't do the summer-time conversion.
*
* In my world, it's always summer, and things are probably a bit off
* in other ways too.
*/
static const struct {
const char *name;
int offset;
int dst;
} timezone_names[] = {
{ "IDLW", -12, 0, }, /* International Date Line West */
{ "NT", -11, 0, }, /* Nome */
{ "CAT", -10, 0, }, /* Central Alaska */
{ "HST", -10, 0, }, /* Hawaii Standard */
{ "HDT", -10, 1, }, /* Hawaii Daylight */
{ "YST", -9, 0, }, /* Yukon Standard */
{ "YDT", -9, 1, }, /* Yukon Daylight */
{ "PST", -8, 0, }, /* Pacific Standard */
{ "PDT", -8, 1, }, /* Pacific Daylight */
{ "MST", -7, 0, }, /* Mountain Standard */
{ "MDT", -7, 1, }, /* Mountain Daylight */
{ "CST", -6, 0, }, /* Central Standard */
{ "CDT", -6, 1, }, /* Central Daylight */
{ "EST", -5, 0, }, /* Eastern Standard */
{ "EDT", -5, 1, }, /* Eastern Daylight */
{ "AST", -3, 0, }, /* Atlantic Standard */
{ "ADT", -3, 1, }, /* Atlantic Daylight */
{ "WAT", -1, 0, }, /* West Africa */
{ "GMT", 0, 0, }, /* Greenwich Mean */
{ "UTC", 0, 0, }, /* Universal (Coordinated) */
{ "Z", 0, 0, }, /* Zulu, alias for UTC */
{ "WET", 0, 0, }, /* Western European */
{ "BST", 0, 1, }, /* British Summer */
{ "CET", +1, 0, }, /* Central European */
{ "MET", +1, 0, }, /* Middle European */
{ "MEWT", +1, 0, }, /* Middle European Winter */
{ "MEST", +1, 1, }, /* Middle European Summer */
{ "CEST", +1, 1, }, /* Central European Summer */
{ "MESZ", +1, 1, }, /* Middle European Summer */
{ "FWT", +1, 0, }, /* French Winter */
{ "FST", +1, 1, }, /* French Summer */
{ "EET", +2, 0, }, /* Eastern Europe, USSR Zone 1 */
{ "EEST", +2, 1, }, /* Eastern European Daylight */
{ "WAST", +7, 0, }, /* West Australian Standard */
{ "WADT", +7, 1, }, /* West Australian Daylight */
{ "CCT", +8, 0, }, /* China Coast, USSR Zone 7 */
{ "JST", +9, 0, }, /* Japan Standard, USSR Zone 8 */
{ "EAST", +10, 0, }, /* Eastern Australian Standard */
{ "EADT", +10, 1, }, /* Eastern Australian Daylight */
{ "GST", +10, 0, }, /* Guam Standard, USSR Zone 9 */
{ "NZT", +12, 0, }, /* New Zealand */
{ "NZST", +12, 0, }, /* New Zealand Standard */
{ "NZDT", +12, 1, }, /* New Zealand Daylight */
{ "IDLE", +12, 0, }, /* International Date Line East */
};
static int match_string(const char *date, const char *str)
{
int i = 0;
for (i = 0; *date; date++, str++, i++) {
if (*date == *str)
continue;
if (toupper(*date) == toupper(*str))
continue;
if (!isalnum(*date))
break;
return 0;
}
return i;
}
static int skip_alpha(const char *date)
{
int i = 0;
do {
i++;
} while (isalpha(date[i]));
return i;
}
/*
* Parse month, weekday, or timezone name
*/
static int match_alpha(const char *date, struct tm *tm, int *offset)
{
unsigned int i;
for (i = 0; i < 12; i++) {
int match = match_string(date, month_names[i]);
if (match >= 3) {
tm->tm_mon = i;
return match;
}
}
for (i = 0; i < 7; i++) {
int match = match_string(date, weekday_names[i]);
if (match >= 3) {
tm->tm_wday = i;
return match;
}
}
for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
int match = match_string(date, timezone_names[i].name);
if (match >= 3 || match == (int)strlen(timezone_names[i].name)) {
int off = timezone_names[i].offset;
/* This is bogus, but we like summer */
off += timezone_names[i].dst;
/* Only use the tz name offset if we don't have anything better */
if (*offset == -1)
*offset = 60*off;
return match;
}
}
if (match_string(date, "PM") == 2) {
tm->tm_hour = (tm->tm_hour % 12) + 12;
return 2;
}
if (match_string(date, "AM") == 2) {
tm->tm_hour = (tm->tm_hour % 12) + 0;
return 2;
}
/* BAD CRAP */
return skip_alpha(date);
}
static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
{
if (month > 0 && month < 13 && day > 0 && day < 32) {
struct tm check = *tm;
struct tm *r = (now_tm ? &check : tm);
time_t specified;
r->tm_mon = month - 1;
r->tm_mday = day;
if (year == -1) {
if (!now_tm)
return 1;
r->tm_year = now_tm->tm_year;
}
else if (year >= 1970 && year < 2100)
r->tm_year = year - 1900;
else if (year > 70 && year < 100)
r->tm_year = year;
else if (year < 38)
r->tm_year = year + 100;
else
return 0;
if (!now_tm)
return 1;
specified = tm_to_time_t(r);
/* Be it commit time or author time, it does not make
* sense to specify timestamp way into the future. Make
* sure it is not later than ten days from now...
*/
if (now + 10*24*3600 < specified)
return 0;
tm->tm_mon = r->tm_mon;
tm->tm_mday = r->tm_mday;
if (year != -1)
tm->tm_year = r->tm_year;
return 1;
}
return 0;
}
static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
{
time_t now;
struct tm now_tm;
struct tm *refuse_future;
long num2, num3;
num2 = strtol(end+1, &end, 10);
num3 = -1;
if (*end == c && isdigit(end[1]))
num3 = strtol(end+1, &end, 10);
/* Time? Date? */
switch (c) {
case ':':
if (num3 < 0)
num3 = 0;
if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
tm->tm_hour = num;
tm->tm_min = num2;
tm->tm_sec = num3;
break;
}
return 0;
case '-':
case '/':
case '.':
now = time(NULL);
refuse_future = NULL;
if (p_gmtime_r(&now, &now_tm))
refuse_future = &now_tm;
if (num > 70) {
/* yyyy-mm-dd? */
if (is_date(num, num2, num3, refuse_future, now, tm))
break;
/* yyyy-dd-mm? */
if (is_date(num, num3, num2, refuse_future, now, tm))
break;
}
/* Our eastern European friends say dd.mm.yy[yy]
* is the norm there, so giving precedence to
* mm/dd/yy[yy] form only when separator is not '.'
*/
if (c != '.' &&
is_date(num3, num, num2, refuse_future, now, tm))
break;
/* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
if (is_date(num3, num2, num, refuse_future, now, tm))
break;
/* Funny European mm.dd.yy */
if (c == '.' &&
is_date(num3, num, num2, refuse_future, now, tm))
break;
return 0;
}
return end - date;
}
/*
* Have we filled in any part of the time/date yet?
* We just do a binary 'and' to see if the sign bit
* is set in all the values.
*/
static int nodate(struct tm *tm)
{
return (tm->tm_year &
tm->tm_mon &
tm->tm_mday &
tm->tm_hour &
tm->tm_min &
tm->tm_sec) < 0;
}
/*
* We've seen a digit. Time? Year? Date?
*/
static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
{
int n;
char *end;
unsigned long num;
num = strtoul(date, &end, 10);
/*
* Seconds since 1970? We trigger on that for any numbers with
* more than 8 digits. This is because we don't want to rule out
* numbers like 20070606 as a YYYYMMDD date.
*/
if (num >= 100000000 && nodate(tm)) {
time_t time = num;
if (p_gmtime_r(&time, tm)) {
*tm_gmt = 1;
return end - date;
}
}
/*
* Check for special formats: num[-.:/]num[same]num
*/
switch (*end) {
case ':':
case '.':
case '/':
case '-':
if (isdigit(end[1])) {
int match = match_multi_number(num, *end, date, end, tm);
if (match)
return match;
}
}
/*
* None of the special formats? Try to guess what
* the number meant. We use the number of digits
* to make a more educated guess..
*/
n = 0;
do {
n++;
} while (isdigit(date[n]));
/* Four-digit year or a timezone? */
if (n == 4) {
if (num <= 1400 && *offset == -1) {
unsigned int minutes = num % 100;
unsigned int hours = num / 100;
*offset = hours*60 + minutes;
} else if (num > 1900 && num < 2100)
tm->tm_year = num - 1900;
return n;
}
/*
* Ignore lots of numerals. We took care of 4-digit years above.
* Days or months must be one or two digits.
*/
if (n > 2)
return n;
/*
* NOTE! We will give precedence to day-of-month over month or
* year numbers in the 1-12 range. So 05 is always "mday 5",
* unless we already have a mday..
*
* IOW, 01 Apr 05 parses as "April 1st, 2005".
*/
if (num > 0 && num < 32 && tm->tm_mday < 0) {
tm->tm_mday = num;
return n;
}
/* Two-digit year? */
if (n == 2 && tm->tm_year < 0) {
if (num < 10 && tm->tm_mday >= 0) {
tm->tm_year = num + 100;
return n;
}
if (num >= 70) {
tm->tm_year = num;
return n;
}
}
if (num > 0 && num < 13 && tm->tm_mon < 0)
tm->tm_mon = num-1;
return n;
}
static int match_tz(const char *date, int *offp)
{
char *end;
int hour = strtoul(date + 1, &end, 10);
int n = end - (date + 1);
int min = 0;
if (n == 4) {
/* hhmm */
min = hour % 100;
hour = hour / 100;
} else if (n != 2) {
min = 99; /* random crap */
} else if (*end == ':') {
/* hh:mm? */
min = strtoul(end + 1, &end, 10);
if (end - (date + 1) != 5)
min = 99; /* random crap */
} /* otherwise we parsed "hh" */
/*
* Don't accept any random crap. Even though some places have
* offset larger than 12 hours (e.g. Pacific/Kiritimati is at
* UTC+14), there is something wrong if hour part is much
* larger than that. We might also want to check that the
* minutes are divisible by 15 or something too. (Offset of
* Kathmandu, Nepal is UTC+5:45)
*/
if (min < 60 && hour < 24) {
int offset = hour * 60 + min;
if (*date == '-')
offset = -offset;
*offp = offset;
}
return end - date;
}
/*
* Parse a string like "0 +0000" as ancient timestamp near epoch, but
* only when it appears not as part of any other string.
*/
static int match_object_header_date(const char *date, git_time_t *timestamp, int *offset)
{
char *end;
unsigned long stamp;
int ofs;
if (*date < '0' || '9' <= *date)
return -1;
stamp = strtoul(date, &end, 10);
if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
return -1;
date = end + 2;
ofs = strtol(date, &end, 10);
if ((*end != '\0' && (*end != '\n')) || end != date + 4)
return -1;
ofs = (ofs / 100) * 60 + (ofs % 100);
if (date[-1] == '-')
ofs = -ofs;
*timestamp = stamp;
*offset = ofs;
return 0;
}
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset)
{
struct tm tm;
int tm_gmt;
git_time_t dummy_timestamp;
int dummy_offset;
if (!timestamp)
timestamp = &dummy_timestamp;
if (!offset)
offset = &dummy_offset;
memset(&tm, 0, sizeof(tm));
tm.tm_year = -1;
tm.tm_mon = -1;
tm.tm_mday = -1;
tm.tm_isdst = -1;
tm.tm_hour = -1;
tm.tm_min = -1;
tm.tm_sec = -1;
*offset = -1;
tm_gmt = 0;
if (*date == '@' &&
!match_object_header_date(date + 1, timestamp, offset))
return 0; /* success */
for (;;) {
int match = 0;
unsigned char c = *date;
/* Stop at end of string or newline */
if (!c || c == '\n')
break;
if (isalpha(c))
match = match_alpha(date, &tm, offset);
else if (isdigit(c))
match = match_digit(date, &tm, offset, &tm_gmt);
else if ((c == '-' || c == '+') && isdigit(date[1]))
match = match_tz(date, offset);
if (!match) {
/* BAD CRAP */
match = 1;
}
date += match;
}
/* mktime uses local timezone */
*timestamp = tm_to_time_t(&tm);
if (*offset == -1)
*offset = (int)((time_t)*timestamp - mktime(&tm)) / 60;
if (*timestamp == (git_time_t)-1)
return -1;
if (!tm_gmt)
*timestamp -= *offset * 60;
return 0; /* success */
}
/*
* Relative time update (eg "2 days ago"). If we haven't set the time
* yet, we need to set it from current time.
*/
static git_time_t update_tm(struct tm *tm, struct tm *now, unsigned long sec)
{
time_t n;
if (tm->tm_mday < 0)
tm->tm_mday = now->tm_mday;
if (tm->tm_mon < 0)
tm->tm_mon = now->tm_mon;
if (tm->tm_year < 0) {
tm->tm_year = now->tm_year;
if (tm->tm_mon > now->tm_mon)
tm->tm_year--;
}
n = mktime(tm) - sec;
p_localtime_r(&n, tm);
return n;
}
static void date_now(struct tm *tm, struct tm *now, int *num)
{
GIT_UNUSED(num);
update_tm(tm, now, 0);
}
static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
GIT_UNUSED(num);
update_tm(tm, now, 24*60*60);
}
static void date_time(struct tm *tm, struct tm *now, int hour)
{
if (tm->tm_hour < hour)
date_yesterday(tm, now, NULL);
tm->tm_hour = hour;
tm->tm_min = 0;
tm->tm_sec = 0;
}
static void date_midnight(struct tm *tm, struct tm *now, int *num)
{
GIT_UNUSED(num);
date_time(tm, now, 0);
}
static void date_noon(struct tm *tm, struct tm *now, int *num)
{
GIT_UNUSED(num);
date_time(tm, now, 12);
}
static void date_tea(struct tm *tm, struct tm *now, int *num)
{
GIT_UNUSED(num);
date_time(tm, now, 17);
}
static void date_pm(struct tm *tm, struct tm *now, int *num)
{
int hour, n = *num;
*num = 0;
GIT_UNUSED(now);
hour = tm->tm_hour;
if (n) {
hour = n;
tm->tm_min = 0;
tm->tm_sec = 0;
}
tm->tm_hour = (hour % 12) + 12;
}
static void date_am(struct tm *tm, struct tm *now, int *num)
{
int hour, n = *num;
*num = 0;
GIT_UNUSED(now);
hour = tm->tm_hour;
if (n) {
hour = n;
tm->tm_min = 0;
tm->tm_sec = 0;
}
tm->tm_hour = (hour % 12);
}
static void date_never(struct tm *tm, struct tm *now, int *num)
{
time_t n = 0;
GIT_UNUSED(now);
GIT_UNUSED(num);
p_localtime_r(&n, tm);
}
static const struct special {
const char *name;
void (*fn)(struct tm *, struct tm *, int *);
} special[] = {
{ "yesterday", date_yesterday },
{ "noon", date_noon },
{ "midnight", date_midnight },
{ "tea", date_tea },
{ "PM", date_pm },
{ "AM", date_am },
{ "never", date_never },
{ "now", date_now },
{ NULL }
};
static const char *number_name[] = {
"zero", "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine", "ten",
};
static const struct typelen {
const char *type;
int length;
} typelen[] = {
{ "seconds", 1 },
{ "minutes", 60 },
{ "hours", 60*60 },
{ "days", 24*60*60 },
{ "weeks", 7*24*60*60 },
{ NULL }
};
static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
{
const struct typelen *tl;
const struct special *s;
const char *end = date;
int i;
while (isalpha(*++end));
;
for (i = 0; i < 12; i++) {
int match = match_string(date, month_names[i]);
if (match >= 3) {
tm->tm_mon = i;
*touched = 1;
return end;
}
}
for (s = special; s->name; s++) {
int len = strlen(s->name);
if (match_string(date, s->name) == len) {
s->fn(tm, now, num);
*touched = 1;
return end;
}
}
if (!*num) {
for (i = 1; i < 11; i++) {
int len = strlen(number_name[i]);
if (match_string(date, number_name[i]) == len) {
*num = i;
*touched = 1;
return end;
}
}
if (match_string(date, "last") == 4) {
*num = 1;
*touched = 1;
}
return end;
}
tl = typelen;
while (tl->type) {
int len = strlen(tl->type);
if (match_string(date, tl->type) >= len-1) {
update_tm(tm, now, tl->length * *num);
*num = 0;
*touched = 1;
return end;
}
tl++;
}
for (i = 0; i < 7; i++) {
int match = match_string(date, weekday_names[i]);
if (match >= 3) {
int diff, n = *num -1;
*num = 0;
diff = tm->tm_wday - i;
if (diff <= 0)
n++;
diff += 7*n;
update_tm(tm, now, diff * 24 * 60 * 60);
*touched = 1;
return end;
}
}
if (match_string(date, "months") >= 5) {
int n;
update_tm(tm, now, 0); /* fill in date fields if needed */
n = tm->tm_mon - *num;
*num = 0;
while (n < 0) {
n += 12;
tm->tm_year--;
}
tm->tm_mon = n;
*touched = 1;
return end;
}
if (match_string(date, "years") >= 4) {
update_tm(tm, now, 0); /* fill in date fields if needed */
tm->tm_year -= *num;
*num = 0;
*touched = 1;
return end;
}
return end;
}
static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
{
char *end;
unsigned long number = strtoul(date, &end, 10);
switch (*end) {
case ':':
case '.':
case '/':
case '-':
if (isdigit(end[1])) {
int match = match_multi_number(number, *end, date, end, tm);
if (match)
return date + match;
}
}
/* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
if (date[0] != '0' || end - date <= 2)
*num = number;
return end;
}
/*
* Do we have a pending number at the end, or when
* we see a new one? Let's assume it's a month day,
* as in "Dec 6, 1992"
*/
static void pending_number(struct tm *tm, int *num)
{
int number = *num;
if (number) {
*num = 0;
if (tm->tm_mday < 0 && number < 32)
tm->tm_mday = number;
else if (tm->tm_mon < 0 && number < 13)
tm->tm_mon = number-1;
else if (tm->tm_year < 0) {
if (number > 1969 && number < 2100)
tm->tm_year = number - 1900;
else if (number > 69 && number < 100)
tm->tm_year = number;
else if (number < 38)
tm->tm_year = 100 + number;
/* We screw up for number = 00 ? */
}
}
}
static git_time_t approxidate_str(const char *date,
const struct timeval *tv,
int *error_ret)
{
int number = 0;
int touched = 0;
struct tm tm = {0}, now;
time_t time_sec;
time_sec = tv->tv_sec;
p_localtime_r(&time_sec, &tm);
now = tm;
tm.tm_year = -1;
tm.tm_mon = -1;
tm.tm_mday = -1;
for (;;) {
unsigned char c = *date;
if (!c)
break;
date++;
if (isdigit(c)) {
pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
touched = 1;
continue;
}
if (isalpha(c))
date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
}
pending_number(&tm, &number);
if (!touched)
*error_ret = 1;
return update_tm(&tm, &now, 0);
}
int git__date_parse(git_time_t *out, const char *date)
{
struct timeval tv;
git_time_t timestamp;
int offset, error_ret=0;
if (!parse_date_basic(date, &timestamp, &offset)) {
*out = timestamp;
return 0;
}
p_gettimeofday(&tv, NULL);
*out = approxidate_str(date, &tv, &error_ret);
return error_ret;
}

View File

@ -130,37 +130,50 @@ fail:
static git_diff_delta *diff_delta__merge_like_cgit(
const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
{
git_diff_delta *dup = diff_delta__dup(a, pool);
if (!dup)
return NULL;
if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
return dup;
git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
dup->new_file.mode = b->new_file.mode;
dup->new_file.size = b->new_file.size;
dup->new_file.flags = b->new_file.flags;
git_diff_delta *dup;
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
*
* When C git does a diff between the work dir and a tree, it actually
* diffs with the index but uses the workdir contents. This emulates
* those choices so we can emulate the type of diff.
*
* We have three file descriptions here, let's call them:
* f1 = a->old_file
* f2 = a->new_file AND b->old_file
* f3 = b->new_file
*/
if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
if (dup->status == GIT_DELTA_DELETED)
/* preserve pending delete info */;
else if (b->status == GIT_DELTA_UNTRACKED ||
b->status == GIT_DELTA_IGNORED)
dup->status = b->status;
else
/* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
return diff_delta__dup(a, pool);
/* otherwise, base this diff on the 'b' diff */
if ((dup = diff_delta__dup(b, pool)) == NULL)
return NULL;
/* If 'a' status is uninteresting, then we're done */
if (a->status == GIT_DELTA_UNMODIFIED)
return dup;
assert(a->status != GIT_DELTA_UNMODIFIED);
assert(b->status != GIT_DELTA_UNMODIFIED);
/* A cgit exception is that the diff of a file that is only in the
* index (i.e. not in HEAD nor workdir) is given as empty.
*/
if (dup->status == GIT_DELTA_DELETED) {
if (a->status == GIT_DELTA_ADDED)
dup->status = GIT_DELTA_UNMODIFIED;
/* else don't overwrite DELETE status */
} else {
dup->status = a->status;
}
else if (dup->status == GIT_DELTA_UNMODIFIED ||
b->status == GIT_DELTA_DELETED)
dup->status = b->status;
git_oid_cpy(&dup->old_file.oid, &a->old_file.oid);
dup->old_file.mode = a->old_file.mode;
dup->old_file.size = a->old_file.size;
dup->old_file.flags = a->old_file.flags;
return dup;
}
@ -214,7 +227,9 @@ static int diff_delta__from_two(
git_diff_list *diff,
git_delta_t status,
const git_index_entry *old_entry,
uint32_t old_mode,
const git_index_entry *new_entry,
uint32_t new_mode,
git_oid *new_oid)
{
git_diff_delta *delta;
@ -224,19 +239,22 @@ static int diff_delta__from_two(
return 0;
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
const git_index_entry *temp = old_entry;
uint32_t temp_mode = old_mode;
const git_index_entry *temp_entry = old_entry;
old_entry = new_entry;
new_entry = temp;
new_entry = temp_entry;
old_mode = new_mode;
new_mode = temp_mode;
}
delta = diff_delta__alloc(diff, status, old_entry->path);
GITERR_CHECK_ALLOC(delta);
delta->old_file.mode = old_entry->mode;
delta->old_file.mode = old_mode;
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
delta->new_file.mode = new_entry->mode;
delta->new_file.mode = new_mode;
git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
if (new_oid || !git_oid_iszero(&new_entry->oid))
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
@ -300,7 +318,7 @@ static git_diff_list *git_diff_list_alloc(
if (config_bool(cfg, "core.ignorestat", 0))
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
if (config_bool(cfg, "core.filemode", 1))
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
if (config_bool(cfg, "core.trustctime", 1))
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
@ -419,7 +437,7 @@ static int oid_for_workdir_item(
return result;
}
#define EXEC_BIT_MASK 0000111
#define MODE_BITS_MASK 0000777
static int maybe_modified(
git_iterator *old_iter,
@ -443,13 +461,13 @@ static int maybe_modified(
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
/* on platforms with no execmode, clear exec bit from comparisons */
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
omode = omode & ~EXEC_BIT_MASK;
nmode = nmode & ~EXEC_BIT_MASK;
}
/* on platforms with no execmode, just preserve old mode */
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
(nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
new_iter->type == GIT_ITERATOR_WORKDIR)
nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
/* support "assume unchanged" (badly, b/c we still stat everything) */
/* support "assume unchanged" (poorly, b/c we still stat everything) */
if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
@ -471,8 +489,13 @@ static int maybe_modified(
omode == nmode)
status = GIT_DELTA_UNMODIFIED;
/* if we have a workdir item with an unknown oid, check deeper */
else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
/* if modes match and we have an unknown OID and a workdir iterator,
* then check deeper for matching
*/
else if (omode == nmode &&
git_oid_iszero(&nitem->oid) &&
new_iter->type == GIT_ITERATOR_WORKDIR)
{
/* TODO: add check against index file st_mtime to avoid racy-git */
/* if they files look exactly alike, then we'll assume the same */
@ -517,7 +540,8 @@ static int maybe_modified(
use_noid = &noid;
}
return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
return diff_delta__from_two(
diff, status, oitem, omode, nitem, nmode, use_noid);
}
static int diff_from_iterators(
@ -772,6 +796,12 @@ int git_diff_merge(
git_vector_swap(&onto->deltas, &onto_new);
git_pool_swap(&onto->pool, &onto_pool);
onto->new_src = from->new_src;
/* prefix strings also come from old pool, so recreate those.*/
onto->opts.old_prefix =
git_pool_strdup(&onto->pool, onto->opts.old_prefix);
onto->opts.new_prefix =
git_pool_strdup(&onto->pool, onto->opts.new_prefix);
}
git_vector_foreach(&onto_new, i, delta)

View File

@ -20,7 +20,7 @@
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
};
@ -36,5 +36,8 @@ struct git_diff_list {
uint32_t diffcaps;
};
extern void git_diff__cleanup_modes(
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
#endif

View File

@ -83,12 +83,13 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
return -1;
/* deal with adding and removing newline at EOF */
/* This should only happen if we are adding a line that does not
* have a newline at the end and the old code did. In that case,
* we have a ADD with a DEL_EOFNL as a pair.
*/
if (len == 3) {
if (origin == GIT_DIFF_LINE_ADDITION)
origin = GIT_DIFF_LINE_ADD_EOFNL;
else
origin = GIT_DIFF_LINE_DEL_EOFNL;
origin = (origin == GIT_DIFF_LINE_ADDITION) ?
GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL;
return info->line_cb(
info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);
@ -359,7 +360,7 @@ int git_diff_foreach(
/* map files */
if (delta->binary != 1 &&
(hunk_cb || line_cb) &&
(hunk_cb || line_cb || git_oid_iszero(&delta->old_file.oid)) &&
(delta->status == GIT_DELTA_DELETED ||
delta->status == GIT_DELTA_MODIFIED))
{
@ -397,7 +398,9 @@ int git_diff_foreach(
/* since we did not have the definitive oid, we may have
* incorrect status and need to skip this item.
*/
if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
if (delta->old_file.mode == delta->new_file.mode &&
!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
{
delta->status = GIT_DELTA_UNMODIFIED;
if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
goto cleanup;
@ -420,7 +423,8 @@ int git_diff_foreach(
*/
if (file_cb != NULL) {
error = file_cb(data, delta, (float)info.index / diff->deltas.length);
error = file_cb(
data, delta, (float)info.index / diff->deltas.length);
if (error < 0)
goto cleanup;
}
@ -433,6 +437,10 @@ int git_diff_foreach(
if (!old_data.len && !new_data.len)
goto cleanup;
/* nothing to do if only diff was a mode change */
if (!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
goto cleanup;
assert(hunk_cb || line_cb);
info.delta = delta;
@ -467,7 +475,7 @@ static char pick_suffix(int mode)
{
if (S_ISDIR(mode))
return '/';
else if (mode & 0100)
else if (mode & 0100) //-V536
/* in git, modes are very regular, so we must have 0100755 mode */
return '*';
else

View File

@ -28,6 +28,8 @@ static int parse_ignore_file(
GITERR_CHECK_ALLOC(match);
}
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
if (!(error = git_attr_fnmatch__parse(
match, ignores->pool, context, &scan)))
{

View File

@ -292,11 +292,13 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
{
int error;
struct git_pack_header hdr;
size_t processed = stats->processed;
size_t processed;
git_mwindow_file *mwf = &idx->pack->mwf;
assert(idx && data && stats);
processed = stats->processed;
if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1;

View File

@ -125,7 +125,7 @@ static int note_write(git_oid *out, git_repository *repo,
return error;
}
error = git_treebuilder_insert(&entry, tb, target + fanout, &oid, 0100644);
error = git_treebuilder_insert(&entry, tb, target + fanout, &oid, 0100644); //-V536
if (error < 0) {
/* libgit2 doesn't support object removal (gc) yet */
/* we leave an orphaned blob object behind - TODO */
@ -154,7 +154,7 @@ static int note_write(git_oid *out, git_repository *repo,
if (error < 0)
return error;
error = git_treebuilder_insert(NULL, tb, subtree, &oid, 0040000);
error = git_treebuilder_insert(NULL, tb, subtree, &oid, 0040000); //-V536
if (error < 0) {
git_treebuilder_free(tb);
return error;

View File

@ -17,6 +17,10 @@
#include <stdio.h>
#include <ctype.h>
#ifdef GIT_WIN32
#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':')
#endif
/*
* Based on the Android implementation, BSD licensed.
* Check http://android.git.kernel.org/
@ -105,7 +109,7 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
'C:/' here */
if (len == 2 && isalpha(path[0]) && path[1] == ':') {
if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) {
len = 3;
goto Exit;
}
@ -170,7 +174,7 @@ int git_path_root(const char *path)
#ifdef GIT_WIN32
/* Does the root of the path look like a windows drive ? */
if (isalpha(path[0]) && (path[1] == ':'))
if (LOOKS_LIKE_DRIVE_PREFIX(path))
offset += 2;
/* Are we dealing with a windows network path? */
@ -210,7 +214,7 @@ int git_path_prettify(git_buf *path_out, const char *path, const char *base)
giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
git_buf_clear(path_out);
return error;
}

View File

@ -59,9 +59,18 @@ extern int p_rename(const char *from, const char *to);
typedef int GIT_SOCKET;
#define INVALID_SOCKET -1
#define p_localtime_r localtime_r
#define p_gmtime_r gmtime_r
#define p_gettimeofday gettimeofday
#else
typedef SOCKET GIT_SOCKET;
struct timezone;
extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
extern int p_gettimeofday(struct timeval *tv, struct timezone *tz);
#endif

View File

@ -1705,3 +1705,62 @@ int git_reference_cmp(git_reference *ref1, git_reference *ref2)
return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
}
/* Update the reference named `ref_name` so it points to `oid` */
int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name)
{
git_reference *ref;
int res;
res = git_reference_lookup(&ref, repo, ref_name);
/* If we haven't found the reference at all, we assume we need to create
* a new reference and that's it */
if (res == GIT_ENOTFOUND) {
giterr_clear();
return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
}
if (res < 0)
return -1;
/* If we have found a reference, but it's symbolic, we need to update
* the direct reference it points to */
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
git_reference *aux;
const char *sym_target;
/* The target pointed at by this reference */
sym_target = git_reference_target(ref);
/* resolve the reference to the target it points to */
res = git_reference_resolve(&aux, ref);
/*
* if the symbolic reference pointed to an inexisting ref,
* this is means we're creating a new branch, for example.
* We need to create a new direct reference with that name
*/
if (res == GIT_ENOTFOUND) {
giterr_clear();
res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
git_reference_free(ref);
return res;
}
/* free the original symbolic reference now; not before because
* we're using the `sym_target` pointer */
git_reference_free(ref);
if (res < 0)
return -1;
/* store the newly found direct reference in its place */
ref = aux;
}
/* ref is made to point to `oid`: ref is either the original reference,
* or the target of the symbolic reference we've looked up */
res = git_reference_set_oid(ref, oid);
git_reference_free(ref);
return res;
}

View File

@ -54,6 +54,7 @@ void git_repository__refcache_free(git_refcache *refs);
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name);
/**
* Lookup a reference by name and try to resolve to an OID.

View File

@ -337,13 +337,16 @@ int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, co
unsigned int i = 0;
git_buf refname = GIT_BUF_INIT;
git_oid old;
git_vector *refs = &remote->refs;
git_vector *refs;
git_remote_head *head;
git_reference *ref;
struct git_refspec *spec = &remote->fetch;
struct git_refspec *spec;
assert(remote);
refs = &remote->refs;
spec = &remote->fetch;
if (refs->length == 0)
return 0;

View File

@ -718,6 +718,9 @@ static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
if (!is_bare)
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
if (!is_reinit && is_filesystem_case_insensitive(git_dir))
SET_REPO_CONFIG(bool, "core.ignorecase", true);
/* TODO: what other defaults? */

103
src/reset.c Normal file
View File

@ -0,0 +1,103 @@
/*
* 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.
*/
#include "common.h"
#include "commit.h"
#include "tag.h"
#include "git2/reset.h"
#define ERROR_MSG "Cannot perform reset"
static int reset_error_invalid(const char *msg)
{
giterr_set(GITERR_INVALID, "%s - %s", ERROR_MSG, msg);
return -1;
}
int git_reset(
git_repository *repo,
const git_object *target,
git_reset_type reset_type)
{
git_otype target_type = GIT_OBJ_BAD;
git_object *commit = NULL;
git_index *index = NULL;
git_tree *tree = NULL;
int error = -1;
assert(repo && target);
assert(reset_type == GIT_RESET_SOFT || reset_type == GIT_RESET_MIXED);
if (git_object_owner(target) != repo)
return reset_error_invalid("The given target does not belong to this repository.");
if (reset_type == GIT_RESET_MIXED && git_repository_is_bare(repo))
return reset_error_invalid("Mixed reset is not allowed in a bare repository.");
target_type = git_object_type(target);
switch (target_type)
{
case GIT_OBJ_TAG:
if (git_tag_peel(&commit, (git_tag *)target) < 0)
goto cleanup;
if (git_object_type(commit) != GIT_OBJ_COMMIT) {
reset_error_invalid("The given target does not resolve to a commit.");
goto cleanup;
}
break;
case GIT_OBJ_COMMIT:
commit = (git_object *)target;
break;
default:
return reset_error_invalid("Only git_tag and git_commit objects are valid targets.");
}
//TODO: Check for unmerged entries
if (git_reference__update(repo, git_object_id(commit), GIT_HEAD_FILE) < 0)
goto cleanup;
if (reset_type == GIT_RESET_SOFT) {
error = 0;
goto cleanup;
}
if (git_commit_tree(&tree, (git_commit *)commit) < 0) {
giterr_set(GITERR_OBJECT, "%s - Failed to retrieve the commit tree.", ERROR_MSG);
goto cleanup;
}
if (git_repository_index(&index, repo) < 0) {
giterr_set(GITERR_OBJECT, "%s - Failed to retrieve the index.", ERROR_MSG);
goto cleanup;
}
if (git_index_read_tree(index, tree) < 0) {
giterr_set(GITERR_INDEX, "%s - Failed to update the index.", ERROR_MSG);
goto cleanup;
}
if (git_index_write(index) < 0) {
giterr_set(GITERR_INDEX, "%s - Failed to write the index.", ERROR_MSG);
goto cleanup;
}
error = 0;
cleanup:
if (target_type == GIT_OBJ_TAG)
git_object_free(commit);
git_index_free(index);
git_tree_free(tree);
return error;
}

748
src/revparse.c Normal file
View File

@ -0,0 +1,748 @@
/*
* 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.
*/
#include <assert.h>
#include "common.h"
#include "buffer.h"
#include "tree.h"
#include "git2.h"
typedef enum {
REVPARSE_STATE_INIT,
REVPARSE_STATE_CARET,
REVPARSE_STATE_LINEAR,
REVPARSE_STATE_COLON,
REVPARSE_STATE_DONE,
} revparse_state;
static void set_invalid_syntax_err(const char *spec)
{
giterr_set(GITERR_INVALID, "Refspec '%s' is not valid.", spec);
}
static int revparse_lookup_fully_qualifed_ref(git_object **out, git_repository *repo, const char*spec)
{
git_oid resolved;
if (git_reference_name_to_oid(&resolved, repo, spec) < 0)
return GIT_ERROR;
return git_object_lookup(out, repo, &resolved, GIT_OBJ_ANY);
}
/* Returns non-zero if yes */
static int spec_looks_like_describe_output(const char *spec)
{
regex_t regex;
int regex_error, retcode;
regex_error = regcomp(&regex, ".+-[0-9]+-g[0-9a-fA-F]+", REG_EXTENDED);
if (regex_error != 0) {
giterr_set_regex(&regex, regex_error);
return 1; /* To be safe */
}
retcode = regexec(&regex, spec, 0, NULL, 0);
regfree(&regex);
return retcode == 0;
}
static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
{
size_t speclen = strlen(spec);
git_object *obj = NULL;
git_oid oid;
git_buf refnamebuf = GIT_BUF_INIT;
static const char* formatters[] = {
"refs/%s",
"refs/tags/%s",
"refs/heads/%s",
"refs/remotes/%s",
"refs/remotes/%s/HEAD",
NULL
};
unsigned int i;
const char *substr;
/* "git describe" output; snip everything before/including "-g" */
substr = strstr(spec, "-g");
if (substr &&
spec_looks_like_describe_output(spec) &&
!revparse_lookup_object(out, repo, substr+2)) {
return 0;
}
/* SHA or prefix */
if (!git_oid_fromstrn(&oid, spec, speclen)) {
if (!git_object_lookup_prefix(&obj, repo, &oid, speclen, GIT_OBJ_ANY)) {
*out = obj;
return 0;
}
}
/* Fully-named ref */
if (!revparse_lookup_fully_qualifed_ref(&obj, repo, spec)) {
*out = obj;
return 0;
}
/* Partially-named ref; match in this order: */
for (i=0; formatters[i]; i++) {
git_buf_clear(&refnamebuf);
if (git_buf_printf(&refnamebuf, formatters[i], spec) < 0) {
return GIT_ERROR;
}
if (!revparse_lookup_fully_qualifed_ref(&obj, repo, git_buf_cstr(&refnamebuf))) {
git_buf_free(&refnamebuf);
*out = obj;
return 0;
}
}
git_buf_free(&refnamebuf);
giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec);
return GIT_ERROR;
}
static int all_chars_are_digits(const char *str, size_t len)
{
size_t i=0;
for (i=0; i<len; i++) {
if (str[i] < '0' || str[i] > '9') return 0;
}
return 1;
}
static void normalize_maybe_empty_refname(git_buf *buf, git_repository *repo, const char *refspec, size_t refspeclen)
{
git_reference *ref;
if (!refspeclen) {
/* Empty refspec means current branch (target of HEAD) */
git_reference_lookup(&ref, repo, "HEAD");
git_buf_puts(buf, git_reference_target(ref));
git_reference_free(ref);
} else if (strstr(refspec, "HEAD")) {
/* Explicit head */
git_buf_puts(buf, refspec);
}else {
if (git__prefixcmp(refspec, "refs/heads/") != 0) {
git_buf_printf(buf, "refs/heads/%s", refspec);
} else {
git_buf_puts(buf, refspec);
}
}
}
static int walk_ref_history(git_object **out, git_repository *repo, const char *refspec, const char *reflogspec)
{
git_reference *ref;
git_reflog *reflog = NULL;
int n, retcode = GIT_ERROR;
int i, refloglen;
const git_reflog_entry *entry;
git_buf buf = GIT_BUF_INIT;
size_t refspeclen = strlen(refspec);
size_t reflogspeclen = strlen(reflogspec);
if (git__prefixcmp(reflogspec, "@{") != 0 ||
git__suffixcmp(reflogspec, "}") != 0) {
giterr_set(GITERR_INVALID, "Bad reflogspec '%s'", reflogspec);
return GIT_ERROR;
}
/* "@{-N}" form means walk back N checkouts. That means the HEAD log. */
if (refspeclen == 0 && !git__prefixcmp(reflogspec, "@{-")) {
regex_t regex;
int regex_error;
if (git__strtol32(&n, reflogspec+3, NULL, 0) < 0 ||
n < 1) {
giterr_set(GITERR_INVALID, "Invalid reflogspec %s", reflogspec);
return GIT_ERROR;
}
if (!git_reference_lookup(&ref, repo, "HEAD")) {
if (!git_reflog_read(&reflog, ref)) {
regex_error = regcomp(&regex, "checkout: moving from (.*) to .*", REG_EXTENDED);
if (regex_error != 0) {
giterr_set_regex(&regex, regex_error);
} else {
regmatch_t regexmatches[2];
refloglen = git_reflog_entrycount(reflog);
for (i=refloglen-1; i >= 0; i--) {
const char *msg;
entry = git_reflog_entry_byindex(reflog, i);
msg = git_reflog_entry_msg(entry);
if (!regexec(&regex, msg, 2, regexmatches, 0)) {
n--;
if (!n) {
git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so);
retcode = revparse_lookup_object(out, repo, git_buf_cstr(&buf));
break;
}
}
}
regfree(&regex);
}
}
git_reference_free(ref);
}
} else {
int date_error = 0;
git_time_t timestamp;
git_buf datebuf = GIT_BUF_INIT;
git_buf_put(&datebuf, reflogspec+2, reflogspeclen-3);
date_error = git__date_parse(&timestamp, git_buf_cstr(&datebuf));
/* @{u} or @{upstream} -> upstream branch, for a tracking branch. This is stored in the config. */
if (!strcmp(reflogspec, "@{u}") || !strcmp(reflogspec, "@{upstream}")) {
git_config *cfg;
if (!git_repository_config(&cfg, repo)) {
/* Is the ref a tracking branch? */
const char *remote;
git_buf_clear(&buf);
git_buf_printf(&buf, "branch.%s.remote", refspec);
if (!git_config_get_string(&remote, cfg, git_buf_cstr(&buf))) {
/* Yes. Find the first merge target name. */
const char *mergetarget;
git_buf_clear(&buf);
git_buf_printf(&buf, "branch.%s.merge", refspec);
if (!git_config_get_string(&mergetarget, cfg, git_buf_cstr(&buf)) &&
!git__prefixcmp(mergetarget, "refs/heads/")) {
/* Success. Look up the target and fetch the object. */
git_buf_clear(&buf);
git_buf_printf(&buf, "refs/remotes/%s/%s", remote, mergetarget+11);
retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf));
}
}
git_config_free(cfg);
}
}
/* @{N} -> Nth prior value for the ref (from reflog) */
else if (all_chars_are_digits(reflogspec+2, reflogspeclen-3) &&
!git__strtol32(&n, reflogspec+2, NULL, 0) &&
n <= 100000000) { /* Allow integer time */
normalize_maybe_empty_refname(&buf, repo, refspec, refspeclen);
if (n == 0) {
retcode = revparse_lookup_fully_qualifed_ref(out, repo, git_buf_cstr(&buf));
} else if (!git_reference_lookup(&ref, repo, git_buf_cstr(&buf))) {
if (!git_reflog_read(&reflog, ref)) {
int numentries = git_reflog_entrycount(reflog);
if (numentries < n) {
giterr_set(GITERR_REFERENCE, "Reflog for '%s' has only %d entries, asked for %d",
git_buf_cstr(&buf), numentries, n);
retcode = GIT_ERROR;
} else {
const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, n);
const git_oid *oid = git_reflog_entry_oidold(entry);
retcode = git_object_lookup(out, repo, oid, GIT_OBJ_ANY);
}
}
git_reference_free(ref);
}
}
else if (!date_error) {
/* Ref as it was on a certain date */
normalize_maybe_empty_refname(&buf, repo, refspec, refspeclen);
if (!git_reference_lookup(&ref, repo, git_buf_cstr(&buf))) {
git_reflog *reflog;
if (!git_reflog_read(&reflog, ref)) {
/* Keep walking until we find an entry older than the given date */
int numentries = git_reflog_entrycount(reflog);
int i;
/* TODO: clunky. Factor "now" into a utility */
git_signature *sig;
git_time as_of;
git_signature_now(&sig, "blah", "blah");
as_of = sig->when;
git_signature_free(sig);
as_of.time = (timestamp > 0)
? timestamp
: sig->when.time + timestamp;
for (i=numentries-1; i>0; i--) {
const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i);
git_time commit_time = git_reflog_entry_committer(entry)->when;
if (git__time_cmp(&commit_time, &as_of) <= 0 ) {
retcode = git_object_lookup(out, repo, git_reflog_entry_oidnew(entry), GIT_OBJ_ANY);
break;
}
}
if (!i) {
/* Didn't find a match. Use the oldest revision in the reflog. */
const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, 0);
retcode = git_object_lookup(out, repo, git_reflog_entry_oidnew(entry), GIT_OBJ_ANY);
}
git_reflog_free(reflog);
}
git_reference_free(ref);
}
}
git_buf_free(&datebuf);
}
if (reflog) git_reflog_free(reflog);
git_buf_free(&buf);
return retcode;
}
static git_object* dereference_object(git_object *obj)
{
git_otype type = git_object_type(obj);
switch (type) {
case GIT_OBJ_COMMIT:
{
git_tree *tree = NULL;
if (0 == git_commit_tree(&tree, (git_commit*)obj)) {
return (git_object*)tree;
}
}
break;
case GIT_OBJ_TAG:
{
git_object *newobj = NULL;
if (0 == git_tag_target(&newobj, (git_tag*)obj)) {
return newobj;
}
}
break;
default:
case GIT_OBJ_TREE:
case GIT_OBJ_BLOB:
case GIT_OBJ_OFS_DELTA:
case GIT_OBJ_REF_DELTA:
break;
}
/* Can't dereference some types */
return NULL;
}
static int dereference_to_type(git_object **out, git_object *obj, git_otype target_type)
{
int retcode = 1;
git_object *obj1 = obj, *obj2 = obj;
while (retcode > 0) {
git_otype this_type = git_object_type(obj1);
if (this_type == target_type) {
*out = obj1;
retcode = 0;
} else {
/* Dereference once, if possible. */
obj2 = dereference_object(obj1);
if (!obj2) {
giterr_set(GITERR_REFERENCE, "Can't dereference to type");
retcode = GIT_ERROR;
}
}
if (obj1 != obj && obj1 != obj2) {
git_object_free(obj1);
}
obj1 = obj2;
}
return retcode;
}
static git_otype parse_obj_type(const char *str)
{
if (!strcmp(str, "{commit}")) return GIT_OBJ_COMMIT;
if (!strcmp(str, "{tree}")) return GIT_OBJ_TREE;
if (!strcmp(str, "{blob}")) return GIT_OBJ_BLOB;
if (!strcmp(str, "{tag}")) return GIT_OBJ_TAG;
return GIT_OBJ_BAD;
}
static int handle_caret_syntax(git_object **out, git_repository *repo, git_object *obj, const char *movement)
{
git_commit *commit;
size_t movementlen = strlen(movement);
int n;
if (*movement == '{') {
if (movement[movementlen-1] != '}') {
set_invalid_syntax_err(movement);
return GIT_ERROR;
}
/* {} -> Dereference until we reach an object that isn't a tag. */
if (movementlen == 2) {
git_object *newobj = obj;
git_object *newobj2 = newobj;
while (git_object_type(newobj2) == GIT_OBJ_TAG) {
newobj2 = dereference_object(newobj);
if (newobj != obj) git_object_free(newobj);
if (!newobj2) {
giterr_set(GITERR_REFERENCE, "Couldn't find object of target type.");
return GIT_ERROR;
}
newobj = newobj2;
}
*out = newobj2;
return 0;
}
/* {/...} -> Walk all commits until we see a commit msg that matches the phrase. */
if (movement[1] == '/') {
int retcode = GIT_ERROR;
git_revwalk *walk;
if (!git_revwalk_new(&walk, repo)) {
git_oid oid;
regex_t preg;
int reg_error;
git_buf buf = GIT_BUF_INIT;
git_revwalk_sorting(walk, GIT_SORT_TIME);
git_revwalk_push(walk, git_object_id(obj));
/* Extract the regex from the movement string */
git_buf_put(&buf, movement+2, strlen(movement)-3);
reg_error = regcomp(&preg, git_buf_cstr(&buf), REG_EXTENDED);
if (reg_error != 0) {
giterr_set_regex(&preg, reg_error);
} else {
while(!git_revwalk_next(&oid, walk)) {
git_object *walkobj;
/* Fetch the commit object, and check for matches in the message */
if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) {
if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) {
/* Found it! */
retcode = 0;
*out = walkobj;
if (obj == walkobj) {
/* Avoid leaking an object */
git_object_free(walkobj);
}
break;
}
git_object_free(walkobj);
}
}
if (retcode < 0) {
giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", movement);
}
regfree(&preg);
}
git_buf_free(&buf);
git_revwalk_free(walk);
}
return retcode;
}
/* {...} -> Dereference until we reach an object of a certain type. */
if (dereference_to_type(out, obj, parse_obj_type(movement)) < 0) {
return GIT_ERROR;
}
return 0;
}
/* Dereference until we reach a commit. */
if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) {
/* Can't dereference to a commit; fail */
return GIT_ERROR;
}
/* "^" is the same as "^1" */
if (movementlen == 0) {
n = 1;
} else {
git__strtol32(&n, movement, NULL, 0);
}
commit = (git_commit*)obj;
/* "^0" just returns the input */
if (n == 0) {
*out = obj;
return 0;
}
if (git_commit_parent(&commit, commit, n-1) < 0) {
return GIT_ERROR;
}
*out = (git_object*)commit;
return 0;
}
static int handle_linear_syntax(git_object **out, git_object *obj, const char *movement)
{
git_commit *commit1, *commit2;
int i, n;
/* Dereference until we reach a commit. */
if (dereference_to_type(&obj, obj, GIT_OBJ_COMMIT) < 0) {
/* Can't dereference to a commit; fail */
return GIT_ERROR;
}
/* "~" is the same as "~1" */
if (*movement == '\0') {
n = 1;
} else {
git__strtol32(&n, movement, NULL, 0);
}
commit1 = (git_commit*)obj;
/* "~0" just returns the input */
if (n == 0) {
*out = obj;
return 0;
}
for (i=0; i<n; i++) {
if (git_commit_parent(&commit2, commit1, 0) < 0) {
return GIT_ERROR;
}
if (commit1 != (git_commit*)obj) {
git_commit_free(commit1);
}
commit1 = commit2;
}
*out = (git_object*)commit1;
return 0;
}
static int oid_for_tree_path(git_oid *out, git_tree *tree, git_repository *repo, const char *path)
{
char *str = git__strdup(path);
char *tok;
void *alloc = str;
git_tree *tree2 = tree;
const git_tree_entry *entry;
while ((tok = git__strtok(&str, "/\\")) != NULL) {
entry = git_tree_entry_byname(tree2, tok);
if (tree2 != tree) git_tree_free(tree2);
if (git_tree_entry__is_tree(entry)) {
if (git_tree_lookup(&tree2, repo, &entry->oid) < 0) {
git__free(alloc);
return GIT_ERROR;
}
}
}
git_oid_cpy(out, git_tree_entry_id(entry));
git__free(alloc);
return 0;
}
static int handle_colon_syntax(git_object **out,
git_repository *repo,
git_object *obj,
const char *path)
{
git_tree *tree;
git_oid oid;
/* Dereference until we reach a tree. */
if (dereference_to_type(&obj, obj, GIT_OBJ_TREE) < 0) {
return GIT_ERROR;
}
tree = (git_tree*)obj;
/* Find the blob at the given path. */
oid_for_tree_path(&oid, tree, repo, path);
git_tree_free(tree);
return git_object_lookup(out, repo, &oid, GIT_OBJ_ANY);
}
static int revparse_global_grep(git_object **out, git_repository *repo, const char *pattern)
{
git_revwalk *walk;
int retcode = GIT_ERROR;
if (!pattern[0]) {
giterr_set(GITERR_REGEX, "Empty pattern");
return GIT_ERROR;
}
if (!git_revwalk_new(&walk, repo)) {
regex_t preg;
int reg_error;
git_oid oid;
git_revwalk_sorting(walk, GIT_SORT_TIME);
git_revwalk_push_glob(walk, "refs/heads/*");
reg_error = regcomp(&preg, pattern, REG_EXTENDED);
if (reg_error != 0) {
giterr_set_regex(&preg, reg_error);
} else {
git_object *walkobj = NULL, *resultobj = NULL;
while(!git_revwalk_next(&oid, walk)) {
/* Fetch the commit object, and check for matches in the message */
if (walkobj != resultobj) git_object_free(walkobj);
if (!git_object_lookup(&walkobj, repo, &oid, GIT_OBJ_COMMIT)) {
if (!regexec(&preg, git_commit_message((git_commit*)walkobj), 0, NULL, 0)) {
/* Match! */
resultobj = walkobj;
retcode = 0;
break;
}
}
}
if (!resultobj) {
giterr_set(GITERR_REFERENCE, "Couldn't find a match for %s", pattern);
} else {
*out = resultobj;
}
regfree(&preg);
git_revwalk_free(walk);
}
}
return retcode;
}
int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
{
revparse_state current_state = REVPARSE_STATE_INIT, next_state = REVPARSE_STATE_INIT;
const char *spec_cur = spec;
git_object *cur_obj = NULL, *next_obj = NULL;
git_buf specbuffer = GIT_BUF_INIT, stepbuffer = GIT_BUF_INIT;
int retcode = 0;
assert(out && repo && spec);
if (spec[0] == ':') {
if (spec[1] == '/') {
return revparse_global_grep(out, repo, spec+2);
}
/* TODO: support merge-stage path lookup (":2:Makefile"). */
giterr_set(GITERR_INVALID, "Unimplemented");
return GIT_ERROR;
}
while (current_state != REVPARSE_STATE_DONE) {
switch (current_state) {
case REVPARSE_STATE_INIT:
if (!*spec_cur) {
/* No operators, just a name. Find it and return. */
retcode = revparse_lookup_object(out, repo, spec);
next_state = REVPARSE_STATE_DONE;
} else if (*spec_cur == '@') {
/* '@' syntax doesn't allow chaining */
git_buf_puts(&stepbuffer, spec_cur);
retcode = walk_ref_history(out, repo, git_buf_cstr(&specbuffer), git_buf_cstr(&stepbuffer));
next_state = REVPARSE_STATE_DONE;
} else if (*spec_cur == '^') {
next_state = REVPARSE_STATE_CARET;
} else if (*spec_cur == '~') {
next_state = REVPARSE_STATE_LINEAR;
} else if (*spec_cur == ':') {
next_state = REVPARSE_STATE_COLON;
} else {
git_buf_putc(&specbuffer, *spec_cur);
}
spec_cur++;
if (current_state != next_state && next_state != REVPARSE_STATE_DONE) {
/* Leaving INIT state, find the object specified, in case that state needs it */
if (revparse_lookup_object(&next_obj, repo, git_buf_cstr(&specbuffer)) < 0) {
retcode = GIT_ERROR;
next_state = REVPARSE_STATE_DONE;
}
}
break;
case REVPARSE_STATE_CARET:
/* Gather characters until NULL, '~', or '^' */
if (!*spec_cur) {
retcode = handle_caret_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer));
next_state = REVPARSE_STATE_DONE;
} else if (*spec_cur == '~') {
retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer));
git_buf_clear(&stepbuffer);
next_state = !retcode ? REVPARSE_STATE_LINEAR : REVPARSE_STATE_DONE;
} else if (*spec_cur == '^') {
retcode = handle_caret_syntax(&next_obj, repo, cur_obj, git_buf_cstr(&stepbuffer));
git_buf_clear(&stepbuffer);
if (retcode < 0) {
next_state = REVPARSE_STATE_DONE;
}
} else {
git_buf_putc(&stepbuffer, *spec_cur);
}
spec_cur++;
break;
case REVPARSE_STATE_LINEAR:
if (!*spec_cur) {
retcode = handle_linear_syntax(out, cur_obj, git_buf_cstr(&stepbuffer));
next_state = REVPARSE_STATE_DONE;
} else if (*spec_cur == '~') {
retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer));
git_buf_clear(&stepbuffer);
if (retcode < 0) {
next_state = REVPARSE_STATE_DONE;
}
} else if (*spec_cur == '^') {
retcode = handle_linear_syntax(&next_obj, cur_obj, git_buf_cstr(&stepbuffer));
git_buf_clear(&stepbuffer);
next_state = !retcode ? REVPARSE_STATE_CARET : REVPARSE_STATE_DONE;
} else {
git_buf_putc(&stepbuffer, *spec_cur);
}
spec_cur++;
break;
case REVPARSE_STATE_COLON:
if (*spec_cur) {
git_buf_putc(&stepbuffer, *spec_cur);
} else {
retcode = handle_colon_syntax(out, repo, cur_obj, git_buf_cstr(&stepbuffer));
next_state = REVPARSE_STATE_DONE;
}
spec_cur++;
break;
case REVPARSE_STATE_DONE:
if (cur_obj && *out != cur_obj) git_object_free(cur_obj);
if (next_obj && *out != next_obj) git_object_free(next_obj);
break;
}
current_state = next_state;
if (cur_obj != next_obj) {
if (cur_obj) git_object_free(cur_obj);
cur_obj = next_obj;
}
}
if (*out != cur_obj) git_object_free(cur_obj);
if (*out != next_obj && next_obj != cur_obj) git_object_free(next_obj);
git_buf_free(&specbuffer);
git_buf_free(&stepbuffer);
return retcode;
}

View File

@ -169,14 +169,23 @@ static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
return commit;
}
static int commit_error(commit_object *commit, const char *msg)
{
char commit_oid[GIT_OID_HEXSZ + 1];
git_oid_fmt(commit_oid, &commit->oid);
commit_oid[GIT_OID_HEXSZ] = '\0';
giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg);
return -1;
}
static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw)
{
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
unsigned char *buffer = raw->data;
unsigned char *buffer_end = buffer + raw->len;
unsigned char *parents_start;
int i, parents = 0;
int commit_time;
@ -207,21 +216,18 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
commit->out_degree = (unsigned short)parents;
if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) {
giterr_set(GITERR_ODB, "Failed to parse commit. Object is corrupted");
return -1;
}
if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
return commit_error(commit, "object is corrupted");
buffer = memchr(buffer, '>', buffer_end - buffer);
if (buffer == NULL) {
giterr_set(GITERR_ODB, "Failed to parse commit. Can't find author");
return -1;
}
if ((buffer = memchr(buffer, '<', buffer_end - buffer)) == NULL ||
(buffer = memchr(buffer, '>', buffer_end - buffer)) == NULL)
return commit_error(commit, "malformed author information");
if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < 0) {
giterr_set(GITERR_ODB, "Failed to parse commit. Can't parse commit time");
return -1;
}
while (*buffer == '>' || git__isspace(*buffer))
buffer++;
if (git__strtol32(&commit_time, (char *)buffer, NULL, 10) < 0)
return commit_error(commit, "cannot parse commit time");
commit->time = (time_t)commit_time;
commit->parsed = 1;

View File

@ -113,26 +113,14 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
time_t offset;
struct tm *utc_tm, *local_tm;
git_signature *sig;
#ifndef GIT_WIN32
struct tm _utc, _local;
#endif
*sig_out = NULL;
time(&now);
/**
* On Win32, `gmtime_r` doesn't exist but
* `gmtime` is threadsafe, so we can use that
*/
#ifdef GIT_WIN32
utc_tm = gmtime(&now);
local_tm = localtime(&now);
#else
utc_tm = gmtime_r(&now, &_utc);
local_tm = localtime_r(&now, &_local);
#endif
utc_tm = p_gmtime_r(&now, &_utc);
local_tm = p_localtime_r(&now, &_local);
offset = mktime(local_tm) - mktime(utc_tm);
offset /= 60;

View File

@ -69,7 +69,7 @@ const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char
return NULL;
}
if (end == NULL || end + 1 == '\0')
if (end == NULL || *end + 1 == '\0')
return tree;
ptr = end + 1;

View File

@ -22,7 +22,7 @@ static int valid_attributes(const int attributes)
static int valid_entry_name(const char *filename)
{
return strlen(filename) > 0 && strchr(filename, '/') == NULL;
return *filename != '\0' && strchr(filename, '/') == NULL;
}
static int entry_sort_cmp(const void *a, const void *b)

View File

@ -209,6 +209,13 @@ GIT_INLINE(bool) git__isspace(int c)
return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
}
GIT_INLINE(int) git__time_cmp(const git_time *a, const git_time *b)
{
/* Adjust for time zones. Times are in seconds, offsets are in minutes. */
git_time_t adjusted_a = a->time + ((b->offset - a->offset) * 60);
return (int)(adjusted_a - b->time);
}
GIT_INLINE(bool) git__iswildcard(int c)
{
return (c == '*' || c == '?' || c == '[');
@ -223,4 +230,14 @@ GIT_INLINE(bool) git__iswildcard(int c)
*/
extern int git__parse_bool(int *out, const char *value);
/*
* Parse a string into a value as a git_time_t.
*
* Sample valid input:
* - "yesterday"
* - "July 17, 2003"
* - "2003-7-17 08:23"
*/
int git__date_parse(git_time_t *out, const char *date);
#endif /* INCLUDE_util_h__ */

View File

@ -414,7 +414,7 @@ int p_mkstemp(char *tmp_path)
return -1;
#endif
return p_creat(tmp_path, 0744);
return p_creat(tmp_path, 0744); //-V536
}
int p_setenv(const char* name, const char* value, int overwrite)
@ -470,3 +470,79 @@ int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
return send(socket, buffer, (int)length, flags);
}
/**
* Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
* On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
*/
struct tm *
p_localtime_r (const time_t *timer, struct tm *result)
{
struct tm *local_result;
local_result = localtime (timer);
if (local_result == NULL || result == NULL)
return NULL;
memcpy (result, local_result, sizeof (struct tm));
return result;
}
struct tm *
p_gmtime_r (const time_t *timer, struct tm *result)
{
struct tm *local_result;
local_result = gmtime (timer);
if (local_result == NULL || result == NULL)
return NULL;
memcpy (result, local_result, sizeof (struct tm));
return result;
}
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
#else
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
#endif
struct timezone
{
int tz_minuteswest; /* minutes W of Greenwich */
int tz_dsttime; /* type of dst correction */
};
int p_gettimeofday(struct timeval *tv, struct timezone *tz)
{
FILETIME ft;
unsigned __int64 tmpres = 0;
static int tzflag;
if (NULL != tv)
{
GetSystemTimeAsFileTime(&ft);
tmpres |= ft.dwHighDateTime;
tmpres <<= 32;
tmpres |= ft.dwLowDateTime;
/*converting file time to unix epoch*/
tmpres /= 10; /*convert into microseconds*/
tmpres -= DELTA_EPOCH_IN_MICROSECS;
tv->tv_sec = (long)(tmpres / 1000000UL);
tv->tv_usec = (long)(tmpres % 1000000UL);
}
if (NULL != tz)
{
if (!tzflag)
{
_tzset();
tzflag++;
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}

1
src/win32/precompiled.c Normal file
View File

@ -0,0 +1 @@
#include "precompiled.h"

19
src/win32/precompiled.h Normal file
View File

@ -0,0 +1,19 @@
#include "git2.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <regex.h>
#include <io.h>
#include <direct.h>
#ifdef GIT_THREADS
#include "win32/pthread.h"
#endif

View File

@ -48,6 +48,62 @@ void cl_git_rewritefile(const char *filename, const char *new_content)
cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_TRUNC);
}
#ifdef GIT_WIN32
#include "win32/utf-conv.h"
char *cl_getenv(const char *name)
{
wchar_t *name_utf16 = gitwin_to_utf16(name);
DWORD value_len, alloc_len;
wchar_t *value_utf16;
char *value_utf8;
cl_assert(name_utf16);
alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0);
if (alloc_len <= 0)
return NULL;
cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t)));
value_len = GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
cl_assert_equal_i(value_len, alloc_len - 1);
cl_assert(value_utf8 = gitwin_from_utf16(value_utf16));
git__free(value_utf16);
return value_utf8;
}
int cl_setenv(const char *name, const char *value)
{
wchar_t *name_utf16 = gitwin_to_utf16(name);
wchar_t *value_utf16 = value ? gitwin_to_utf16(value) : NULL;
cl_assert(name_utf16);
cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16));
git__free(name_utf16);
git__free(value_utf16);
return 0;
}
#else
#include <stdlib.h>
char *cl_getenv(const char *name)
{
return getenv(name);
}
int cl_setenv(const char *name, const char *value)
{
return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
}
#endif
static const char *_cl_sandbox = NULL;
static git_repository *_cl_repo = NULL;
@ -98,3 +154,28 @@ void cl_git_sandbox_cleanup(void)
_cl_sandbox = NULL;
}
}
bool cl_toggle_filemode(const char *filename)
{
struct stat st1, st2;
cl_must_pass(p_stat(filename, &st1));
cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
cl_must_pass(p_stat(filename, &st2));
return (st1.st_mode != st2.st_mode);
}
bool cl_is_chmod_supported(void)
{
static int _is_supported = -1;
if (_is_supported < 0) {
cl_git_mkfile("filemode.t", "Test if filemode can be modified");
_is_supported = cl_toggle_filemode("filemode.t");
cl_must_pass(p_unlink("filemode.t"));
}
return _is_supported;
}

View File

@ -40,6 +40,13 @@ void cl_git_append2file(const char *filename, const char *new_content);
void cl_git_rewritefile(const char *filename, const char *new_content);
void cl_git_write2file(const char *filename, const char *new_content, int mode);
bool cl_toggle_filemode(const char *filename);
bool cl_is_chmod_supported(void);
/* Environment wrappers */
char *cl_getenv(const char *name);
int cl_setenv(const char *name, const char *value);
/* Git sandbox setup helpers */
git_repository *cl_git_sandbox_init(const char *sandbox);

View File

@ -2,59 +2,6 @@
#include "fileops.h"
#include "path.h"
#ifdef GIT_WIN32
#include "win32/utf-conv.h"
static char *cl_getenv(const char *name)
{
wchar_t *name_utf16 = gitwin_to_utf16(name);
DWORD value_len, alloc_len;
wchar_t *value_utf16;
char *value_utf8;
cl_assert(name_utf16);
alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0);
if (alloc_len <= 0)
return NULL;
cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t)));
value_len = GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
cl_assert_equal_i(value_len, alloc_len - 1);
cl_assert(value_utf8 = gitwin_from_utf16(value_utf16));
git__free(value_utf16);
return value_utf8;
}
static int cl_setenv(const char *name, const char *value)
{
wchar_t *name_utf16 = gitwin_to_utf16(name);
wchar_t *value_utf16 = value ? gitwin_to_utf16(value) : NULL;
cl_assert(name_utf16);
cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16));
git__free(name_utf16);
git__free(value_utf16);
return 0;
}
#else
#include <stdlib.h>
#define cl_getenv(n) getenv(n)
static int cl_setenv(const char *name, const char *value)
{
return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
}
#endif
#ifdef GIT_WIN32
static char *env_userprofile = NULL;
static char *env_programfiles = NULL;

View File

@ -8,7 +8,7 @@ void test_core_filebuf__0(void)
int fd;
char test[] = "test", testlock[] = "test.lock";
fd = p_creat(testlock, 0744);
fd = p_creat(testlock, 0744); //-V536
cl_must_pass(fd);
cl_must_pass(p_close(fd));
@ -27,7 +27,7 @@ void test_core_filebuf__1(void)
int fd;
char test[] = "test";
fd = p_creat(test, 0666);
fd = p_creat(test, 0666); //-V536
cl_must_pass(fd);
cl_must_pass(p_write(fd, "libgit2 rocks\n", 14));
cl_must_pass(p_close(fd));

15
tests-clar/date/date.c Normal file
View File

@ -0,0 +1,15 @@
#include "clar_libgit2.h"
#include "util.h"
void test_date_date__overflow(void)
{
#ifdef __LP64__
git_time_t d2038, d2039;
/* This is expected to fail on a 32-bit machine. */
cl_git_pass(git__date_parse(&d2038, "2038-1-1"));
cl_git_pass(git__date_parse(&d2039, "2039-1-1"));
cl_assert(d2038 < d2039);
#endif
}

View File

@ -85,11 +85,16 @@ int diff_line_fn(
e->line_ctxt++;
break;
case GIT_DIFF_LINE_ADDITION:
case GIT_DIFF_LINE_ADD_EOFNL:
e->line_adds++;
break;
case GIT_DIFF_LINE_ADD_EOFNL:
assert(0);
break;
case GIT_DIFF_LINE_DELETION:
e->line_dels++;
break;
case GIT_DIFF_LINE_DEL_EOFNL:
/* technically not a line delete, but we'll count it as such */
e->line_dels++;
break;
default:

View File

@ -116,7 +116,7 @@ void test_diff_tree__options(void)
{ 5, 3, 0, 2, 0, 0, 0, 4, 0, 0, 51, 2, 46, 3 },
{ 5, 3, 0, 2, 0, 0, 0, 4, 0, 0, 53, 4, 46, 3 },
{ 5, 0, 3, 2, 0, 0, 0, 4, 0, 0, 52, 3, 3, 46 },
{ 5, 3, 0, 2, 0, 0, 0, 5, 0, 0, 54, 3, 48, 3 },
{ 5, 3, 0, 2, 0, 0, 0, 5, 0, 0, 54, 3, 47, 4 },
/* c vs d tests */
{ 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 22, 9, 10, 3 },
{ 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 19, 12, 7, 0 },

View File

@ -5,7 +5,6 @@ static git_repository *g_repo = NULL;
void test_diff_workdir__initialize(void)
{
g_repo = cl_git_sandbox_init("status");
}
void test_diff_workdir__cleanup(void)
@ -19,6 +18,8 @@ void test_diff_workdir__to_index(void)
git_diff_list *diff = NULL;
diff_expects exp;
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;
@ -59,13 +60,17 @@ void test_diff_workdir__to_tree(void)
/* grabbed a couple of commit oids from the history of the attr repo */
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
const char *b_commit = "0017bd4ab1ec3"; /* the start */
git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
git_tree *a, *b;
git_diff_options opts = {0};
git_diff_list *diff = NULL;
git_diff_list *diff2 = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("status");
a = resolve_commit_oid_to_tree(g_repo, a_commit);
b = resolve_commit_oid_to_tree(g_repo, b_commit);
opts.context_lines = 3;
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
@ -87,12 +92,12 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 14);
cl_assert(exp.file_adds == 0);
cl_assert(exp.file_dels == 4);
cl_assert(exp.file_mods == 4);
cl_assert(exp.file_ignored == 1);
cl_assert(exp.file_untracked == 5);
cl_assert_equal_i(14, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(4, exp.file_dels);
cl_assert_equal_i(4, exp.file_mods);
cl_assert_equal_i(1, exp.file_ignored);
cl_assert_equal_i(5, exp.file_untracked);
/* Since there is no git diff equivalent, let's just assume that the
* text diffs produced by git_diff_foreach are accurate here. We will
@ -115,19 +120,19 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 15);
cl_assert(exp.file_adds == 2);
cl_assert(exp.file_dels == 5);
cl_assert(exp.file_mods == 4);
cl_assert(exp.file_ignored == 1);
cl_assert(exp.file_untracked == 3);
cl_assert_equal_i(15, exp.files);
cl_assert_equal_i(2, exp.file_adds);
cl_assert_equal_i(5, exp.file_dels);
cl_assert_equal_i(4, exp.file_mods);
cl_assert_equal_i(1, exp.file_ignored);
cl_assert_equal_i(3, exp.file_untracked);
cl_assert(exp.hunks == 11);
cl_assert_equal_i(11, exp.hunks);
cl_assert(exp.lines == 17);
cl_assert(exp.line_ctxt == 4);
cl_assert(exp.line_adds == 8);
cl_assert(exp.line_dels == 5);
cl_assert_equal_i(17, exp.lines);
cl_assert_equal_i(4, exp.line_ctxt);
cl_assert_equal_i(8, exp.line_adds);
cl_assert_equal_i(5, exp.line_dels);
git_diff_list_free(diff);
diff = NULL;
@ -144,19 +149,19 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 16);
cl_assert(exp.file_adds == 5);
cl_assert(exp.file_dels == 4);
cl_assert(exp.file_mods == 3);
cl_assert(exp.file_ignored == 1);
cl_assert(exp.file_untracked == 3);
cl_assert_equal_i(16, exp.files);
cl_assert_equal_i(5, exp.file_adds);
cl_assert_equal_i(4, exp.file_dels);
cl_assert_equal_i(3, exp.file_mods);
cl_assert_equal_i(1, exp.file_ignored);
cl_assert_equal_i(3, exp.file_untracked);
cl_assert(exp.hunks == 12);
cl_assert_equal_i(12, exp.hunks);
cl_assert(exp.lines == 19);
cl_assert(exp.line_ctxt == 3);
cl_assert(exp.line_adds == 12);
cl_assert(exp.line_dels == 4);
cl_assert_equal_i(19, exp.lines);
cl_assert_equal_i(3, exp.line_ctxt);
cl_assert_equal_i(12, exp.line_adds);
cl_assert_equal_i(4, exp.line_dels);
git_diff_list_free(diff);
@ -171,6 +176,8 @@ void test_diff_workdir__to_index_with_pathspec(void)
diff_expects exp;
char *pathspec = NULL;
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;
@ -237,6 +244,241 @@ void test_diff_workdir__to_index_with_pathspec(void)
git_diff_list_free(diff);
}
void test_diff_workdir__filemode_changes(void)
{
git_config *cfg;
git_diff_list *diff = NULL;
diff_expects exp;
if (!cl_is_chmod_supported())
return;
g_repo = cl_git_sandbox_init("issue_592");
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_bool(cfg, "core.filemode", true));
/* test once with no mods */
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.file_mods);
cl_assert_equal_i(0, exp.hunks);
git_diff_list_free(diff);
/* chmod file and test again */
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(1, exp.file_mods);
cl_assert_equal_i(0, exp.hunks);
git_diff_list_free(diff);
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
git_config_free(cfg);
}
void test_diff_workdir__filemode_changes_with_filemode_false(void)
{
git_config *cfg;
git_diff_list *diff = NULL;
diff_expects exp;
if (!cl_is_chmod_supported())
return;
g_repo = cl_git_sandbox_init("issue_592");
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_bool(cfg, "core.filemode", false));
/* test once with no mods */
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.file_mods);
cl_assert_equal_i(0, exp.hunks);
git_diff_list_free(diff);
/* chmod file and test again */
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.file_mods);
cl_assert_equal_i(0, exp.hunks);
git_diff_list_free(diff);
cl_assert(cl_toggle_filemode("issue_592/a.txt"));
git_config_free(cfg);
}
void test_diff_workdir__head_index_and_workdir_all_differ(void)
{
git_diff_options opts = {0};
git_diff_list *diff_i2t = NULL, *diff_w2i = NULL;
diff_expects exp;
char *pathspec = "staged_changes_modified_file";
git_tree *tree;
/* For this file,
* - head->index diff has 1 line of context, 1 line of diff
* - index->workdir diff has 2 lines of context, 1 line of diff
* but
* - head->workdir diff has 1 line of context, 2 lines of diff
* Let's make sure the right one is returned from each fn.
*/
g_repo = cl_git_sandbox_init("status");
tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, tree, &diff_i2t));
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff_w2i));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(0, exp.file_dels);
cl_assert_equal_i(1, exp.file_mods);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(2, exp.lines);
cl_assert_equal_i(1, exp.line_ctxt);
cl_assert_equal_i(1, exp.line_adds);
cl_assert_equal_i(0, exp.line_dels);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(0, exp.file_dels);
cl_assert_equal_i(1, exp.file_mods);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(3, exp.lines);
cl_assert_equal_i(2, exp.line_ctxt);
cl_assert_equal_i(1, exp.line_adds);
cl_assert_equal_i(0, exp.line_dels);
cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(0, exp.file_dels);
cl_assert_equal_i(1, exp.file_mods);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(3, exp.lines);
cl_assert_equal_i(1, exp.line_ctxt);
cl_assert_equal_i(2, exp.line_adds);
cl_assert_equal_i(0, exp.line_dels);
git_diff_list_free(diff_i2t);
git_diff_list_free(diff_w2i);
}
void test_diff_workdir__eof_newline_changes(void)
{
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects exp;
char *pathspec = "current_file";
g_repo = cl_git_sandbox_init("status");
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(0, exp.file_dels);
cl_assert_equal_i(0, exp.file_mods);
cl_assert_equal_i(0, exp.hunks);
cl_assert_equal_i(0, exp.lines);
cl_assert_equal_i(0, exp.line_ctxt);
cl_assert_equal_i(0, exp.line_adds);
cl_assert_equal_i(0, exp.line_dels);
git_diff_list_free(diff);
cl_git_append2file("status/current_file", "\n");
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(0, exp.file_dels);
cl_assert_equal_i(1, exp.file_mods);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(2, exp.lines);
cl_assert_equal_i(1, exp.line_ctxt);
cl_assert_equal_i(1, exp.line_adds);
cl_assert_equal_i(0, exp.line_dels);
git_diff_list_free(diff);
cl_git_rewritefile("status/current_file", "current_file");
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(0, exp.file_adds);
cl_assert_equal_i(0, exp.file_dels);
cl_assert_equal_i(1, exp.file_mods);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(3, exp.lines);
cl_assert_equal_i(0, exp.line_ctxt);
cl_assert_equal_i(1, exp.line_adds);
cl_assert_equal_i(2, exp.line_dels);
git_diff_list_free(diff);
}
/* PREPARATION OF TEST DATA
*
* Since there is no command line equivalent of git_diff_workdir_to_tree,

View File

@ -33,7 +33,7 @@ static void copy_file(const char *src, const char *dst)
cl_git_pass(git_futils_readbuffer(&source_buf, src));
dst_fd = git_futils_creat_withpath(dst, 0777, 0666);
dst_fd = git_futils_creat_withpath(dst, 0777, 0666); //-V536
if (dst_fd < 0)
goto cleanup;

View File

@ -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(how_many_refs == 14); /* 1 HEAD + 6 heads + 1 lightweight tag + 3 annotated tags + 3 peeled target */
cl_assert_equal_i(how_many_refs, 20);
}
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(how_many_refs == 14); /* 1 HEAD + 6 heads + 1 lightweight tag + 3 annotated tags + 3 peeled target */
cl_assert_equal_i(how_many_refs, 20);
git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
remote = NULL;

View File

@ -63,14 +63,14 @@ void test_object_tree_write__subtree(void)
//create subtree
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644));
cl_git_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644)); //-V536
cl_git_pass(git_treebuilder_write(&subtree_id, g_repo, builder));
git_treebuilder_free(builder);
// create parent tree
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
cl_git_pass(git_treebuilder_create(&builder, tree));
cl_git_pass(git_treebuilder_insert(NULL,builder,"new",&subtree_id,040000));
cl_git_pass(git_treebuilder_insert(NULL,builder,"new",&subtree_id,040000)); //-V536
cl_git_pass(git_treebuilder_write(&id_hiearar, g_repo, builder));
git_treebuilder_free(builder);
git_tree_free(tree);

View File

@ -30,22 +30,22 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count)
{
cl_git_pass(git_branch_list(&branch_list, repo, flags));
cl_assert_equal_i(expected_count, branch_list.count);
cl_assert_equal_i(branch_list.count, expected_count);
}
void test_refs_branches_listall__retrieve_all_branches(void)
{
assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 6 + 1);
assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 9);
}
void test_refs_branches_listall__retrieve_remote_branches(void)
{
assert_retrieval(GIT_BRANCH_REMOTE, 1);
assert_retrieval(GIT_BRANCH_REMOTE, 2);
}
void test_refs_branches_listall__retrieve_local_branches(void)
{
assert_retrieval(GIT_BRANCH_LOCAL, 6);
assert_retrieval(GIT_BRANCH_LOCAL, 7);
}
static void assert_branch_list_contains(git_strarray *branches, const char* expected_branch_name)
@ -72,7 +72,7 @@ void test_refs_branches_listall__retrieve_remote_symbolic_HEAD_when_present(void
cl_git_pass(git_branch_list(&branch_list, repo, GIT_BRANCH_REMOTE));
cl_assert_equal_i(2, branch_list.count);
assert_branch_list_contains(&branch_list, "refs/remotes/nulltoken/HEAD");
assert_branch_list_contains(&branch_list, "refs/remotes/nulltoken/master");
cl_assert_equal_i(3, branch_list.count);
assert_branch_list_contains(&branch_list, "remotes/nulltoken/HEAD");
assert_branch_list_contains(&branch_list, "remotes/nulltoken/master");
}

174
tests-clar/refs/revparse.c Normal file
View File

@ -0,0 +1,174 @@
#include "clar_libgit2.h"
#include "git2/revparse.h"
static git_repository *g_repo;
static git_object *g_obj;
static char g_orig_tz[16] = {0};
/* Helpers */
static void test_object(const char *spec, const char *expected_oid)
{
char objstr[64] = {0};
cl_git_pass(git_revparse_single(&g_obj, g_repo, spec));
git_oid_fmt(objstr, git_object_id(g_obj));
cl_assert_equal_s(objstr, expected_oid);
git_object_free(g_obj);
g_obj = NULL;
}
void test_refs_revparse__initialize(void)
{
char *tz = cl_getenv("TZ");
if (tz)
strcpy(g_orig_tz, tz);
cl_setenv("TZ", "UTC");
g_repo = cl_git_sandbox_init("testrepo.git");
}
void test_refs_revparse__cleanup(void)
{
cl_git_sandbox_cleanup();
g_obj = NULL;
cl_setenv("TZ", g_orig_tz);
}
void test_refs_revparse__nonexistant_object(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't exist"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't exist^1"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "this doesn't exist~2"));
}
void test_refs_revparse__shas(void)
{
test_object("c47800c7266a2be04c571c04d5a6614691ea99bd", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object("c47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
}
void test_refs_revparse__head(void)
{
test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
}
void test_refs_revparse__full_refs(void)
{
test_object("refs/heads/master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("refs/heads/test", "e90810b8df3e80c413d903f631643c716887138d");
test_object("refs/tags/test", "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
}
void test_refs_revparse__partial_refs(void)
{
test_object("point_to_blob", "1385f264afb75a56a5bec74243be9b367ba4ca08");
test_object("packed-test", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
test_object("br2", "a4a7dce85cf63874e984719f4fdd239f5145052f");
}
void test_refs_revparse__describe_output(void)
{
test_object("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object("not-good", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
}
void test_refs_revparse__nth_parent(void)
{
test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
}
void test_refs_revparse__not_tag(void)
{
test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
}
void test_refs_revparse__to_type(void)
{
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}"));
}
void test_refs_revparse__linear_history(void)
{
test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
}
void test_refs_revparse__chaining(void)
{
test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479");
}
void test_refs_revparse__reflog(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, "@{1000}"));
test_object("@{-2}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("@{-1}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
}
void test_refs_revparse__revwalk(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/not found in any commit}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/merge}"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, "master^{/((}"));
test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a");
}
void test_refs_revparse__date(void)
{
test_object("HEAD@{10 years ago}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master@{2012-4-30 10:23:20 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
test_object("master@{2012-4-30 18:24 -0800}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
test_object("master@{2012-4-30 23:24 -0300}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
/* Core git gives a65fedf, because they don't take time zones into account. */
test_object("master@{1335806640}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
}
void test_refs_revparse__colon(void)
{
cl_git_fail(git_revparse_single(&g_obj, g_repo, ":/"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, ":/not found in any commit"));
cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README"));
test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f");
test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b");
test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f");
test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd");
test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9");
}

View File

@ -166,14 +166,14 @@ void test_repo_init__additional_templates(void)
git_buf_free(&path);
}
static void assert_config_entry_on_init(const char *config_key, int expected_value)
static void assert_config_entry_on_init_bytype(const char *config_key, int expected_value, bool is_bare)
{
git_config *config;
int current_value;
cl_set_cleanup(&cleanup_repository, "config_entry");
cl_git_pass(git_repository_init(&_repo, "config_entry/test.git", 1));
cl_git_pass(git_repository_init(&_repo, "config_entry/test.git", is_bare));
git_repository_config(&config, _repo);
if (expected_value >= 0) {
@ -189,6 +189,14 @@ static void assert_config_entry_on_init(const char *config_key, int expected_val
git_config_free(config);
}
static void assert_config_entry_on_init(const char *config_key, int expected_value)
{
assert_config_entry_on_init_bytype(config_key, expected_value, true);
git_repository_free(_repo);
assert_config_entry_on_init_bytype(config_key, expected_value, false);
}
void test_repo_init__detect_filemode(void)
{
#ifdef GIT_WIN32
@ -233,3 +241,9 @@ void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
git_config_free(config);
}
void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
{
assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
}

47
tests-clar/reset/mixed.c Normal file
View File

@ -0,0 +1,47 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "reset_helpers.h"
#include "path.h"
static git_repository *repo;
static git_object *target;
void test_reset_mixed__initialize(void)
{
repo = cl_git_sandbox_init("attr");
target = NULL;
}
void test_reset_mixed__cleanup(void)
{
git_object_free(target);
cl_git_sandbox_cleanup();
}
void test_reset_mixed__cannot_reset_in_a_bare_repository(void)
{
git_repository *bare;
cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
cl_assert(git_repository_is_bare(bare) == true);
retrieve_target_from_oid(&target, bare, KNOWN_COMMIT_IN_BARE_REPO);
cl_git_fail(git_reset(bare, target, GIT_RESET_MIXED));
git_repository_free(bare);
}
void test_reset_mixed__resetting_refreshes_the_index_to_the_commit_tree(void)
{
unsigned int status;
cl_git_pass(git_status_file(&status, repo, "macro_bad"));
cl_assert(status == GIT_STATUS_CURRENT);
retrieve_target_from_oid(&target, repo, "605812ab7fe421fdd325a935d35cb06a9234a7d7");
cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
cl_git_pass(git_status_file(&status, repo, "macro_bad"));
cl_assert(status == GIT_STATUS_WT_NEW);
}

View File

@ -0,0 +1,10 @@
#include "clar_libgit2.h"
#include "reset_helpers.h"
void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha)
{
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, sha));
cl_git_pass(git_object_lookup(object_out, repo, &oid, GIT_OBJ_ANY));
}

View File

@ -0,0 +1,6 @@
#include "common.h"
#define KNOWN_COMMIT_IN_BARE_REPO "e90810b8df3e80c413d903f631643c716887138d"
#define KNOWN_COMMIT_IN_ATTR_REPO "217878ab49e1314388ea2e32dc6fdb58a1b969e0"
extern void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha);

102
tests-clar/reset/soft.c Normal file
View File

@ -0,0 +1,102 @@
#include "clar_libgit2.h"
#include "reset_helpers.h"
static git_repository *repo;
static git_object *target;
void test_reset_soft__initialize(void)
{
repo = cl_git_sandbox_init("testrepo.git");
}
void test_reset_soft__cleanup(void)
{
git_object_free(target);
cl_git_sandbox_cleanup();
}
static void assert_reset_soft(bool should_be_detached)
{
git_oid oid;
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
cl_git_fail(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
cl_assert(git_repository_head_detached(repo) == should_be_detached);
cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
cl_assert(git_repository_head_detached(repo) == should_be_detached);
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
}
void test_reset_soft__can_reset_the_non_detached_Head_to_the_specified_commit(void)
{
assert_reset_soft(false);
}
static void detach_head(void)
{
git_reference *head;
git_oid oid;
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", &oid, true));
git_reference_free(head);
}
void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void)
{
detach_head();
assert_reset_soft(true);
}
void test_reset_soft__resetting_to_the_commit_pointed_at_by_the_Head_does_not_change_the_target_of_the_Head(void)
{
git_oid oid;
char raw_head_oid[GIT_OID_HEXSZ + 1];
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
git_oid_fmt(raw_head_oid, &oid);
raw_head_oid[GIT_OID_HEXSZ] = '\0';
retrieve_target_from_oid(&target, repo, raw_head_oid);
cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
cl_git_pass(git_oid_streq(&oid, raw_head_oid));
}
void test_reset_soft__resetting_to_a_tag_sets_the_Head_to_the_peeled_commit(void)
{
git_oid oid;
/* b25fa35 is a tag, pointing to another tag which points to commit e90810b */
retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
cl_assert(git_repository_head_detached(repo) == false);
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
}
void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void)
{
/* 53fc32d is the tree of commit e90810b */
retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
git_object_free(target);
/* 521d87c is an annotated tag pointing to a blob */
retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
}

View File

@ -0,0 +1 @@
ref: refs/heads/master

View File

@ -0,0 +1,6 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true

View File

@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@ -0,0 +1,24 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data

View File

@ -0,0 +1 @@
9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -0,0 +1 @@
Howdy

View File

@ -6,3 +6,7 @@
[remote "test"]
url = git://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/test/*
[branch "master"]
remote = test
merge = refs/heads/master

View File

@ -0,0 +1,5 @@
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 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

View File

@ -0,0 +1,2 @@
0000000000000000000000000000000000000000 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0700 branch: Created from refs/remotes/origin/br2
a4a7dce85cf63874e984719f4fdd239f5145052f a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0700 commit: checking in

View File

@ -0,0 +1,2 @@
0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0800 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0800 commit: checking in

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1336761944 -0700 branch: Created from master

View File

@ -0,0 +1 @@
0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git

View File

@ -0,0 +1,2 @@
xŒM F]s鈆Ÿ41ÆxÝ(­IÁéÂÛKݽ/_ÞãP@¡ÚÕø¢<C3B8>!8)es
” ¥N&FGSÆ„¹hÑ{+C<>ç‰÷ÆZzvØF¡7ZàÎ-¬Îñó‡k™x\ã¡[PÆ8ï´ôGØK/¥<10> l<>Ê>.4

View File

@ -0,0 +1 @@
a65fedf39aefe402d3bb6e24df4d4f5fe4547750

View File

@ -0,0 +1 @@
be3563ae3f795b2b4353bcce3a527ad0a4f7f644

View File

@ -0,0 +1 @@
849a5e34a26815e821f865b8479f5815a47af0fe

View File

@ -0,0 +1 @@
849a5e34a26815e821f865b8479f5815a47af0fe

View File

@ -131,3 +131,17 @@ void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
cl_assert(ignored);
}
void test_status_ignore__ignore_pattern_contains_space(void)
{
unsigned int flags;
const mode_t mode = 0777;
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_rewritefile("empty_standard_repo/.gitignore", "foo bar.txt\n");
cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", NULL, mode));
cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!");
cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt"));
cl_assert(flags == GIT_STATUS_WT_NEW);
}

View File

@ -581,3 +581,69 @@ void test_status_worktree__space_in_filename(void)
git_index_free(index);
git_repository_free(repo);
}
static const char *filemode_paths[] = {
"exec_off",
"exec_off2on_staged",
"exec_off2on_workdir",
"exec_off_untracked",
"exec_on",
"exec_on2off_staged",
"exec_on2off_workdir",
"exec_on_untracked",
};
static unsigned int filemode_statuses[] = {
GIT_STATUS_CURRENT,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW,
GIT_STATUS_CURRENT,
GIT_STATUS_INDEX_MODIFIED,
GIT_STATUS_WT_MODIFIED,
GIT_STATUS_WT_NEW
};
static const size_t filemode_count = 8;
void test_status_worktree__filemode_changes(void)
{
git_repository *repo = cl_git_sandbox_init("filemodes");
status_entry_counts counts;
git_status_options opts;
git_config *cfg;
/* overwrite stored filemode with platform appropriate value */
cl_git_pass(git_repository_config(&cfg, repo));
if (cl_is_chmod_supported())
cl_git_pass(git_config_set_bool(cfg, "core.filemode", true));
else {
unsigned int i;
cl_git_pass(git_config_set_bool(cfg, "core.filemode", false));
/* won't trust filesystem mode diffs, so these will appear unchanged */
for (i = 0; i < filemode_count; ++i)
if (filemode_statuses[i] == GIT_STATUS_WT_MODIFIED)
filemode_statuses[i] = GIT_STATUS_CURRENT;
}
memset(&opts, 0, sizeof(opts));
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_INCLUDE_IGNORED |
GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
memset(&counts, 0, sizeof(counts));
counts.expected_entry_count = filemode_count;
counts.expected_paths = filemode_paths;
counts.expected_statuses = filemode_statuses;
cl_git_pass(
git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
);
cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
cl_assert_equal_i(0, counts.wrong_status_flags_count);
cl_assert_equal_i(0, counts.wrong_sorted_path);
git_config_free(cfg);
}