Merge remote-tracking branch 'origin/development' into fix-git-status-list-new-unreadable-folder

Conflicts:
	include/git2/diff.h
This commit is contained in:
Alan Rogers 2014-06-04 15:36:28 -07:00
commit dc49e1b5b3
115 changed files with 2549 additions and 851 deletions

View File

@ -3,6 +3,10 @@
language: c language: c
os:
- linux
- osx
compiler: compiler:
- gcc - gcc
- clang - clang
@ -17,17 +21,21 @@ env:
matrix: matrix:
fast_finish: true fast_finish: true
exclude:
- os: osx
compiler: gcc
include: include:
- compiler: i586-mingw32msvc-gcc - compiler: i586-mingw32msvc-gcc
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF" env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF"
os: linux
- compiler: gcc - compiler: gcc
env: COVERITY=1 env: COVERITY=1
os: linux
allow_failures: allow_failures:
- env: COVERITY=1 - env: COVERITY=1
install: install:
- sudo apt-get -qq update - ./script/install-deps-${TRAVIS_OS_NAME}.sh
- sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
# Run the Build script and tests # Run the Build script and tests
script: script:
@ -35,8 +43,8 @@ script:
# Run Tests # Run Tests
after_success: after_success:
- sudo apt-get -qq install valgrind - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install valgrind; fi
- valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi
# Only watch the development branch # Only watch the development branch
branches: branches:

View File

@ -39,12 +39,6 @@ These are good small projects to get started with libgit2.
the data is available, you would just need to add the code into the the data is available, you would just need to add the code into the
`print_commit()` routine (along with a way of passing the option `print_commit()` routine (along with a way of passing the option
into that function). into that function).
* For `examples/log.c`, implement any one of `--author=<...>`,
`--committer=<...>`, or `--grep=<...>` but just use simple string
match with `strstr()` instead of full regular expression
matching. (I.e. I'm suggesting implementing this as if
`--fixed-strings` was always turned on, because it will be a simpler
project.)
* As an extension to the matching idea for `examples/log.c`, add the * As an extension to the matching idea for `examples/log.c`, add the
`-i` option to use `strcasestr()` for matches. `-i` option to use `strcasestr()` for matches.
* For `examples/log.c`, implement the `--first-parent` option now that * For `examples/log.c`, implement the `--first-parent` option now that

View File

@ -189,6 +189,8 @@ Here are the bindings to libgit2 that are currently available:
* GitPowerShell <https://github.com/ethomson/gitpowershell> * GitPowerShell <https://github.com/ethomson/gitpowershell>
* Python * Python
* pygit2 <https://github.com/libgit2/pygit2> * pygit2 <https://github.com/libgit2/pygit2>
* R
* git2r <https://github.com/ropensci/git2r>
* Ruby * Ruby
* Rugged <https://github.com/libgit2/rugged> * Rugged <https://github.com/libgit2/rugged>
* Vala * Vala

View File

@ -54,8 +54,9 @@ struct log_options {
int min_parents, max_parents; int min_parents, max_parents;
git_time_t before; git_time_t before;
git_time_t after; git_time_t after;
char *author; const char *author;
char *committer; const char *committer;
const char *grep;
}; };
/** utility functions that parse options and help with log output */ /** utility functions that parse options and help with log output */
@ -65,6 +66,9 @@ static void print_time(const git_time *intime, const char *prefix);
static void print_commit(git_commit *commit); static void print_commit(git_commit *commit);
static int match_with_parent(git_commit *commit, int i, git_diff_options *); static int match_with_parent(git_commit *commit, int i, git_diff_options *);
/** utility functions for filtering */
static int signature_matches(const git_signature *sig, const char *filter);
static int log_message_matches(const git_commit *commit, const char *filter);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -128,6 +132,15 @@ int main(int argc, char *argv[])
continue; continue;
} }
if (!signature_matches(git_commit_author(commit), opt.author))
continue;
if (!signature_matches(git_commit_committer(commit), opt.committer))
continue;
if (!log_message_matches(commit, opt.grep))
continue;
if (count++ < opt.skip) if (count++ < opt.skip)
continue; continue;
if (opt.limit != -1 && printed++ >= opt.limit) { if (opt.limit != -1 && printed++ >= opt.limit) {
@ -172,6 +185,32 @@ int main(int argc, char *argv[])
return 0; return 0;
} }
/** Determine if the given git_signature does not contain the filter text. */
static int signature_matches(const git_signature *sig, const char *filter) {
if (filter == NULL)
return 1;
if (sig != NULL &&
(strstr(sig->name, filter) != NULL ||
strstr(sig->email, filter) != NULL))
return 1;
return 0;
}
static int log_message_matches(const git_commit *commit, const char *filter) {
const char *message = NULL;
if (filter == NULL)
return 1;
if ((message = git_commit_message(commit)) != NULL &&
strstr(message, filter) != NULL)
return 1;
return 0;
}
/** Push object (for hide or show) onto revwalker. */ /** Push object (for hide or show) onto revwalker. */
static void push_rev(struct log_state *s, git_object *obj, int hide) static void push_rev(struct log_state *s, git_object *obj, int hide)
{ {
@ -401,6 +440,12 @@ static int parse_options(
set_sorting(s, GIT_SORT_TOPOLOGICAL); set_sorting(s, GIT_SORT_TOPOLOGICAL);
else if (!strcmp(a, "--reverse")) else if (!strcmp(a, "--reverse"))
set_sorting(s, GIT_SORT_REVERSE); set_sorting(s, GIT_SORT_REVERSE);
else if (match_str_arg(&opt->author, &args, "--author"))
/** Found valid --author */;
else if (match_str_arg(&opt->committer, &args, "--committer"))
/** Found valid --committer */;
else if (match_str_arg(&opt->grep, &args, "--grep"))
/** Found valid --grep */;
else if (match_str_arg(&s->repodir, &args, "--git-dir")) else if (match_str_arg(&s->repodir, &args, "--git-dir"))
/** Found git-dir. */; /** Found git-dir. */;
else if (match_int_arg(&opt->skip, &args, "--skip", 0)) else if (match_int_arg(&opt->skip, &args, "--skip", 0))

View File

@ -14,9 +14,10 @@
#include "common.h" #include "common.h"
#ifdef _WIN32 #ifdef _WIN32
#define sleep(a) Sleep(a * 1000) # include <Windows.h>
# define sleep(a) Sleep(a * 1000)
#else #else
#include <unistd.h> # include <unistd.h>
#endif #endif
/** /**

View File

@ -210,7 +210,7 @@ GIT_EXTERN(int) git_blob_create_frombuffer(
* *
* The heuristic used to guess if a file is binary is taken from core git: * The heuristic used to guess if a file is binary is taken from core git:
* Searching for NUL bytes and looking for a reasonable ratio of printable * Searching for NUL bytes and looking for a reasonable ratio of printable
* to non-printable characters among the first 4000 bytes. * to non-printable characters among the first 8000 bytes.
* *
* @param blob The blob which content should be analyzed * @param blob The blob which content should be analyzed
* @return 1 if the content of the blob is detected * @return 1 if the content of the blob is detected

View File

@ -43,17 +43,17 @@ GIT_BEGIN_DECL
* In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE` * In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE`
* both of which only make modifications that will not lose changes. * both of which only make modifications that will not lose changes.
* *
* | target == baseline | target != baseline | * | target == baseline | target != baseline |
* ---------------------|-----------------------|----------------------| * ---------------------|-----------------------|----------------------|
* workdir == baseline | no action | create, update, or | * workdir == baseline | no action | create, update, or |
* | | delete file | * | | delete file |
* ---------------------|-----------------------|----------------------| * ---------------------|-----------------------|----------------------|
* workdir exists and | no action | conflict (notify | * workdir exists and | no action | conflict (notify |
* is != baseline | notify dirty MODIFIED | and cancel checkout) | * is != baseline | notify dirty MODIFIED | and cancel checkout) |
* ---------------------|-----------------------|----------------------| * ---------------------|-----------------------|----------------------|
* workdir missing, | create if SAFE_CREATE | create file | * workdir missing, | create if SAFE_CREATE | create file |
* baseline present | notify dirty DELETED | | * baseline present | notify dirty DELETED | |
* ---------------------|-----------------------|----------------------| * ---------------------|-----------------------|----------------------|
* *
* The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE * The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE
* will cause a file to be checked out if it is missing from the working * will cause a file to be checked out if it is missing from the working
@ -106,7 +106,7 @@ GIT_BEGIN_DECL
* target contains that file. * target contains that file.
*/ */
typedef enum { typedef enum {
GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */ GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
/** Allow safe updates that cannot overwrite uncommitted data */ /** Allow safe updates that cannot overwrite uncommitted data */
GIT_CHECKOUT_SAFE = (1u << 0), GIT_CHECKOUT_SAFE = (1u << 0),

View File

@ -56,7 +56,7 @@ GIT_EXTERN(int) git_cherry_pick_init_options(
* @param cherry_pick_commit the commit to cherry-pick * @param cherry_pick_commit the commit to cherry-pick
* @param our_commit the commit to revert against (eg, HEAD) * @param our_commit the commit to revert against (eg, HEAD)
* @param mainline the parent of the revert commit, if it is a merge * @param mainline the parent of the revert commit, if it is a merge
* @param merge_tree_opts the merge tree options (or null for defaults) * @param merge_options the merge options (or null for defaults)
* @return zero on success, -1 on failure. * @return zero on success, -1 on failure.
*/ */
GIT_EXTERN(int) git_cherry_pick_commit( GIT_EXTERN(int) git_cherry_pick_commit(

View File

@ -23,6 +23,13 @@
*/ */
GIT_BEGIN_DECL GIT_BEGIN_DECL
typedef enum {
GIT_CLONE_LOCAL_AUTO,
GIT_CLONE_LOCAL,
GIT_CLONE_NO_LOCAL,
GIT_CLONE_LOCAL_NO_LINKS,
} git_clone_local_t;
/** /**
* Clone options structure * Clone options structure
* *
@ -57,6 +64,7 @@ typedef struct git_clone_options {
int bare; int bare;
int ignore_cert_errors; int ignore_cert_errors;
git_clone_local_t local;
const char *remote_name; const char *remote_name;
const char* checkout_branch; const char* checkout_branch;
git_signature *signature; git_signature *signature;
@ -123,6 +131,35 @@ GIT_EXTERN(int) git_clone_into(
const char *branch, const char *branch,
const git_signature *signature); const git_signature *signature);
/**
* Perform a local clone into a repository
*
* A "local clone" bypasses any git-aware protocols and simply copies
* over the object database from the source repository. It is often
* faster than a git-aware clone, but no verification of the data is
* performed, and can copy over too much data.
*
* @param repo the repository to use
* @param remote the remote repository to clone from
* @param co_opts options to use during checkout
* @param branch the branch to checkout after the clone, pass NULL for the
* remote's default branch
* @param link wether to use hardlinks instead of copying
* objects. This is only possible if both repositories are on the same
* filesystem.
* @param signature the identity used when updating the reflog
* @return 0 on success, any non-zero return value from a callback
* function, or a negative value to indicate an error (use
* `giterr_last` for a detailed error message)
*/
GIT_EXTERN(int) git_clone_local_into(
git_repository *repo,
git_remote *remote,
const git_checkout_options *co_opts,
const char *branch,
int link,
const git_signature *signature);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
#endif #endif

View File

@ -218,9 +218,9 @@ typedef struct git_diff git_diff;
* considered reserved for internal or future use. * considered reserved for internal or future use.
*/ */
typedef enum { typedef enum {
GIT_DIFF_FLAG_BINARY = (1u << 0), /** file(s) treated as binary data */ GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */
GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /** file(s) treated as text data */ GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */
GIT_DIFF_FLAG_VALID_ID = (1u << 2), /** `id` value is known correct */ GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */
} git_diff_flag_t; } git_diff_flag_t;
/** /**
@ -234,16 +234,16 @@ typedef enum {
* DELETED pairs). * DELETED pairs).
*/ */
typedef enum { typedef enum {
GIT_DELTA_UNMODIFIED = 0, /** no changes */ GIT_DELTA_UNMODIFIED = 0, /**< no changes */
GIT_DELTA_ADDED = 1, /** entry does not exist in old version */ GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */
GIT_DELTA_DELETED = 2, /** entry does not exist in new version */ GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */
GIT_DELTA_MODIFIED = 3, /** entry content changed between old and new */ GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */
GIT_DELTA_RENAMED = 4, /** entry was renamed between old and new */ GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */
GIT_DELTA_COPIED = 5, /** entry was copied from another old entry */ GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */
GIT_DELTA_IGNORED = 6, /** entry is ignored item in workdir */ GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
GIT_DELTA_UNTRACKED = 7, /** entry is untracked item in workdir */ GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */
GIT_DELTA_TYPECHANGE = 8, /** type of entry changed between old and new */ GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */
GIT_DELTA_UNREADABLE = 9, /** entry is unreadable */ GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
} git_delta_t; } git_delta_t;
/** /**
@ -423,12 +423,12 @@ typedef int (*git_diff_file_cb)(
*/ */
typedef struct git_diff_hunk git_diff_hunk; typedef struct git_diff_hunk git_diff_hunk;
struct git_diff_hunk { struct git_diff_hunk {
int old_start; /** Starting line number in old_file */ int old_start; /**< Starting line number in old_file */
int old_lines; /** Number of lines in old_file */ int old_lines; /**< Number of lines in old_file */
int new_start; /** Starting line number in new_file */ int new_start; /**< Starting line number in new_file */
int new_lines; /** Number of lines in new_file */ int new_lines; /**< Number of lines in new_file */
size_t header_len; /** Number of bytes in header text */ size_t header_len; /**< Number of bytes in header text */
char header[128]; /** Header text, NUL-byte terminated */ char header[128]; /**< Header text, NUL-byte terminated */
}; };
/** /**
@ -471,13 +471,13 @@ typedef enum {
*/ */
typedef struct git_diff_line git_diff_line; typedef struct git_diff_line git_diff_line;
struct git_diff_line { struct git_diff_line {
char origin; /** A git_diff_line_t value */ char origin; /**< A git_diff_line_t value */
int old_lineno; /** Line number in old file or -1 for added line */ int old_lineno; /**< Line number in old file or -1 for added line */
int new_lineno; /** Line number in new file or -1 for deleted line */ int new_lineno; /**< Line number in new file or -1 for deleted line */
int num_lines; /** Number of newline characters in content */ int num_lines; /**< Number of newline characters in content */
size_t content_len; /** Number of bytes of data */ size_t content_len; /**< Number of bytes of data */
git_off_t content_offset; /** Offset in the original file to the content */ git_off_t content_offset; /**< Offset in the original file to the content */
const char *content; /** Pointer to diff text, not NUL-byte terminated */ const char *content; /**< Pointer to diff text, not NUL-byte terminated */
}; };
/** /**
@ -489,10 +489,10 @@ struct git_diff_line {
* of lines of file and hunk headers. * of lines of file and hunk headers.
*/ */
typedef int (*git_diff_line_cb)( typedef int (*git_diff_line_cb)(
const git_diff_delta *delta, /** delta that contains this data */ const git_diff_delta *delta, /**< delta that contains this data */
const git_diff_hunk *hunk, /** hunk containing this data */ const git_diff_hunk *hunk, /**< hunk containing this data */
const git_diff_line *line, /** line data */ const git_diff_line *line, /**< line data */
void *payload); /** user reference data */ void *payload); /**< user reference data */
/** /**
* Flags to control the behavior of diff rename/copy detection. * Flags to control the behavior of diff rename/copy detection.

View File

@ -19,13 +19,13 @@ GIT_BEGIN_DECL
/** Generic return codes */ /** Generic return codes */
typedef enum { typedef enum {
GIT_OK = 0, /*< No error */ GIT_OK = 0, /**< No error */
GIT_ERROR = -1, /*< Generic error */ GIT_ERROR = -1, /**< Generic error */
GIT_ENOTFOUND = -3, /*< Requested object could not be found */ GIT_ENOTFOUND = -3, /**< Requested object could not be found */
GIT_EEXISTS = -4, /*< Object exists preventing operation */ GIT_EEXISTS = -4, /**< Object exists preventing operation */
GIT_EAMBIGUOUS = -5, /*< More than one object matches */ GIT_EAMBIGUOUS = -5, /**< More than one object matches */
GIT_EBUFS = -6, /*< Output buffer too short to hold data */ GIT_EBUFS = -6, /**< Output buffer too short to hold data */
/* GIT_EUSER is a special error that is never generated by libgit2 /* GIT_EUSER is a special error that is never generated by libgit2
* code. You can return it from a callback (e.g to stop an iteration) * code. You can return it from a callback (e.g to stop an iteration)
@ -33,17 +33,17 @@ typedef enum {
*/ */
GIT_EUSER = -7, GIT_EUSER = -7,
GIT_EBAREREPO = -8, /*< Operation not allowed on bare repository */ GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
GIT_EUNBORNBRANCH = -9, /*< HEAD refers to branch with no commits */ GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
GIT_EUNMERGED = -10, /*< Merge in progress prevented operation */ GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */
GIT_ENONFASTFORWARD = -11, /*< Reference was not fast-forwardable */ GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */
GIT_EINVALIDSPEC = -12, /*< Name/ref spec was not in a valid format */ GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */
GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */ GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */
GIT_ELOCKED = -14, /*< Lock file prevented operation */ GIT_ELOCKED = -14, /**< Lock file prevented operation */
GIT_EMODIFIED = -15, /*< Reference value does not match expected */ GIT_EMODIFIED = -15, /**< Reference value does not match expected */
GIT_PASSTHROUGH = -30, /*< Internal only */ GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
} git_error_code; } git_error_code;
/** /**

View File

@ -73,10 +73,13 @@ typedef struct git_index_entry {
*/ */
#define GIT_IDXENTRY_NAMEMASK (0x0fff) #define GIT_IDXENTRY_NAMEMASK (0x0fff)
#define GIT_IDXENTRY_STAGEMASK (0x3000) #define GIT_IDXENTRY_STAGEMASK (0x3000)
#define GIT_IDXENTRY_EXTENDED (0x4000)
#define GIT_IDXENTRY_VALID (0x8000)
#define GIT_IDXENTRY_STAGESHIFT 12 #define GIT_IDXENTRY_STAGESHIFT 12
typedef enum {
GIT_IDXENTRY_EXTENDED = (0x4000),
GIT_IDXENTRY_VALID = (0x8000),
} git_indxentry_flag_t;
#define GIT_IDXENTRY_STAGE(E) \ #define GIT_IDXENTRY_STAGE(E) \
(((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
@ -92,36 +95,36 @@ typedef struct git_index_entry {
* in-memory only and used by libgit2. Only the flags in * in-memory only and used by libgit2. Only the flags in
* `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk. * `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk.
* *
* These bitmasks match the three fields in the `git_index_entry` * Thee first three bitmasks match the three fields in the
* `flags_extended` value that belong on disk. You can use them to * `git_index_entry` `flags_extended` value that belong on disk. You
* interpret the data in the `flags_extended`.
*/
#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13)
#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14)
/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */
#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
/**
* Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended`
*
* These bitmasks match the other fields in the `git_index_entry`
* `flags_extended` value that are only used in-memory by libgit2. You
* can use them to interpret the data in the `flags_extended`. * can use them to interpret the data in the `flags_extended`.
*
* The rest of the bitmasks match the other fields in the `git_index_entry`
* `flags_extended` value that are only used in-memory by libgit2.
* You can use them to interpret the data in the `flags_extended`.
*
*/ */
#define GIT_IDXENTRY_UPDATE (1 << 0) typedef enum {
#define GIT_IDXENTRY_REMOVE (1 << 1)
#define GIT_IDXENTRY_UPTODATE (1 << 2)
#define GIT_IDXENTRY_ADDED (1 << 3)
#define GIT_IDXENTRY_HASHED (1 << 4) GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
#define GIT_IDXENTRY_UNHASHED (1 << 5) GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */ /** Reserved for future extension */
#define GIT_IDXENTRY_CONFLICTED (1 << 7) GIT_IDXENTRY_EXTENDED2 = (1 << 15),
#define GIT_IDXENTRY_UNPACKED (1 << 8) GIT_IDXENTRY_EXTENDED_FLAGS = (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE),
#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) GIT_IDXENTRY_UPDATE = (1 << 0),
GIT_IDXENTRY_REMOVE = (1 << 1),
GIT_IDXENTRY_UPTODATE = (1 << 2),
GIT_IDXENTRY_ADDED = (1 << 3),
GIT_IDXENTRY_HASHED = (1 << 4),
GIT_IDXENTRY_UNHASHED = (1 << 5),
GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
GIT_IDXENTRY_CONFLICTED = (1 << 7),
GIT_IDXENTRY_UNPACKED = (1 << 8),
GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9),
} git_idxentry_extended_flag_t;
/** Capabilities of system that affect index actions. */ /** Capabilities of system that affect index actions. */
typedef enum { typedef enum {
@ -412,10 +415,10 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
* *
* This entry is calculated from the entry's flag attribute like this: * This entry is calculated from the entry's flag attribute like this:
* *
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
* *
* @param entry The entry * @param entry The entry
* @returns the stage number * @return the stage number
*/ */
GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);

View File

@ -268,6 +268,26 @@ typedef enum {
GIT_MERGE_ANALYSIS_UNBORN = (1 << 3), GIT_MERGE_ANALYSIS_UNBORN = (1 << 3),
} git_merge_analysis_t; } git_merge_analysis_t;
typedef enum {
/*
* No configuration was found that suggests a preferred behavior for
* merge.
*/
GIT_MERGE_PREFERENCE_NONE = 0,
/**
* There is a `merge.ff=false` configuration setting, suggesting that
* the user does not want to allow a fast-forward merge.
*/
GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0),
/**
* There is a `merge.ff=only` configuration setting, suggesting that
* the user only wants fast-forward merges.
*/
GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1),
} git_merge_preference_t;
/** /**
* Analyzes the given branch(es) and determines the opportunities for * Analyzes the given branch(es) and determines the opportunities for
* merging them into the HEAD of the repository. * merging them into the HEAD of the repository.
@ -280,6 +300,7 @@ typedef enum {
*/ */
GIT_EXTERN(int) git_merge_analysis( GIT_EXTERN(int) git_merge_analysis(
git_merge_analysis_t *analysis_out, git_merge_analysis_t *analysis_out,
git_merge_preference_t *preference_out,
git_repository *repo, git_repository *repo,
const git_merge_head **their_heads, const git_merge_head **their_heads,
size_t their_heads_len); size_t their_heads_len);
@ -378,8 +399,8 @@ GIT_EXTERN(int) git_merge_head_from_id(
/** /**
* Gets the commit ID that the given `git_merge_head` refers to. * Gets the commit ID that the given `git_merge_head` refers to.
* *
* @param id pointer to commit id to be filled in
* @param head the given merge head * @param head the given merge head
* @return commit id
*/ */
GIT_EXTERN(const git_oid *) git_merge_head_id( GIT_EXTERN(const git_oid *) git_merge_head_id(
const git_merge_head *head); const git_merge_head *head);
@ -424,8 +445,8 @@ GIT_EXTERN(int) git_merge_file(
* @param out The git_merge_file_result to be filled in * @param out The git_merge_file_result to be filled in
* @param repo The repository * @param repo The repository
* @param ancestor The index entry for the ancestor file (stage level 1) * @param ancestor The index entry for the ancestor file (stage level 1)
* @param our_path The index entry for our file (stage level 2) * @param ours The index entry for our file (stage level 2)
* @param their_path The index entry for their file (stage level 3) * @param theirs The index entry for their file (stage level 3)
* @param opts The merge file options or NULL * @param opts The merge file options or NULL
* @return 0 on success or error code * @return 0 on success or error code
*/ */
@ -497,8 +518,8 @@ GIT_EXTERN(int) git_merge_commits(
* completes, resolve any conflicts and prepare a commit. * completes, resolve any conflicts and prepare a commit.
* *
* @param repo the repository to merge * @param repo the repository to merge
* @param merge_heads the heads to merge into * @param their_heads the heads to merge into
* @param merge_heads_len the number of heads to merge * @param their_heads_len the number of heads to merge
* @param merge_opts merge options * @param merge_opts merge options
* @param checkout_opts checkout options * @param checkout_opts checkout options
* @return 0 on success or error code * @return 0 on success or error code

View File

@ -29,12 +29,14 @@ GIT_BEGIN_DECL
* *
* @param message The message to be prettified. * @param message The message to be prettified.
* *
* @param strip_comments Non-zero to remove lines starting with "#", 0 to * @param strip_comments Non-zero to remove comment lines, 0 to leave them in.
* leave them in. *
* @param comment_char Comment character. Lines starting with this character
* are considered to be comments and removed if `strip_comments` is non-zero.
* *
* @return 0 or an error code. * @return 0 or an error code.
*/ */
GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments); GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL

View File

@ -41,6 +41,11 @@ struct git_remote_head {
git_oid oid; git_oid oid;
git_oid loid; git_oid loid;
char *name; char *name;
/**
* If the server send a symref mapping for this ref, this will
* point to the target.
*/
char *symref_target;
}; };
/** /**

View File

@ -69,7 +69,7 @@ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const g
* *
* @param repo the repository * @param repo the repository
* @param old_name the old name of the reference * @param old_name the old name of the reference
* @param new_name the new name of the reference * @param name the new name of the reference
* @return 0 on success, GIT_EINVALIDSPEC or an error code * @return 0 on success, GIT_EINVALIDSPEC or an error code
*/ */
GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name); GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);

View File

@ -178,7 +178,6 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
* @param name The name of the reference * @param name The name of the reference
* @param id The object id pointed to by the reference. * @param id The object id pointed to by the reference.
* @param force Overwrite existing references * @param force Overwrite existing references
* @param force Overwrite existing references
* @param signature The identity that will used to populate the reflog entry * @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog * @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
@ -221,7 +220,6 @@ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo,
* @param name The name of the reference * @param name The name of the reference
* @param id The object id pointed to by the reference. * @param id The object id pointed to by the reference.
* @param force Overwrite existing references * @param force Overwrite existing references
* @param force Overwrite existing references
* @param current_id The expected value of the reference at the time of update * @param current_id The expected value of the reference at the time of update
* @param signature The identity that will used to populate the reflog entry * @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog * @param log_message The one line long message to be appended to the reflog
@ -415,7 +413,7 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
* This method removes the named reference from the repository without * This method removes the named reference from the repository without
* looking at its old value. * looking at its old value.
* *
* @param ref The reference to remove * @param name The reference to remove
* @return 0 or an error code * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name); GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name);

View File

@ -610,6 +610,37 @@ GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value);
*/ */
GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name); GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
/**
* Delete an existing persisted remote.
*
* All remote-tracking branches and configuration settings
* for the remote will be removed.
*
* once deleted, the passed remote object will be freed and invalidated.
*
* @param remote A valid remote
* @return 0 on success, or an error code.
*/
GIT_EXTERN(int) git_remote_delete(git_remote *remote);
/**
* Retrieve the name of the remote's default branch
*
* The default branch of a repository is the branch which HEAD points
* to. If the remote does not support reporting this information
* directly, it performs the guess as git does; that is, if there are
* multiple branches which point to the same commit, the first one is
* chosen. If the master branch is a candidate, it wins.
*
* This function must only be called after connecting.
*
* @param out the buffern in which to store the reference name
* @param remote the remote
* @return 0, GIT_ENOTFOUND if the remote does not have any references
* or none of them point to HEAD's commit, or an error message.
*/
GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
#endif #endif

View File

@ -649,7 +649,7 @@ GIT_EXTERN(int) git_repository_set_head_detached(
* *
* @param repo Repository pointer * @param repo Repository pointer
* @param signature The identity that will used to populate the reflog entry * @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog * @param reflog_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch or an error code * branch or an error code
*/ */

View File

@ -19,9 +19,9 @@ GIT_BEGIN_DECL
* Kinds of reset operation * Kinds of reset operation
*/ */
typedef enum { typedef enum {
GIT_RESET_SOFT = 1, /** Move the head to the given commit */ GIT_RESET_SOFT = 1, /**< Move the head to the given commit */
GIT_RESET_MIXED = 2, /** SOFT plus reset index to the commit */ GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */
GIT_RESET_HARD = 3, /** MIXED plus changes in working tree discarded */ GIT_RESET_HARD = 3, /**< MIXED plus changes in working tree discarded */
} git_reset_t; } git_reset_t;
/** /**

View File

@ -56,7 +56,7 @@ GIT_EXTERN(int) git_revert_init_options(
* @param revert_commit the commit to revert * @param revert_commit the commit to revert
* @param our_commit the commit to revert against (eg, HEAD) * @param our_commit the commit to revert against (eg, HEAD)
* @param mainline the parent of the revert commit, if it is a merge * @param mainline the parent of the revert commit, if it is a merge
* @param merge_tree_opts the merge tree options (or null for defaults) * @param merge_options the merge options (or null for defaults)
* @return zero on success, -1 on failure. * @return zero on success, -1 on failure.
*/ */
int git_revert_commit( int git_revert_commit(
@ -71,9 +71,8 @@ int git_revert_commit(
* Reverts the given commit, producing changes in the working directory. * Reverts the given commit, producing changes in the working directory.
* *
* @param repo the repository to revert * @param repo the repository to revert
* @param commits the commits to revert * @param commit the commit to revert
* @param commits_len the number of commits to revert * @param given_opts merge flags
* @param flags merge flags
* @return zero on success, -1 on failure. * @return zero on success, -1 on failure.
*/ */
GIT_EXTERN(int) git_revert( GIT_EXTERN(int) git_revert(

View File

@ -69,7 +69,7 @@ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo)
* Call `git_signature_free()` to free the data. * Call `git_signature_free()` to free the data.
* *
* @param dest pointer where to store the copy * @param dest pointer where to store the copy
* @param entry signature to duplicate * @param sig signature to duplicate
* @return 0 or an error code * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig); GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig);

View File

@ -283,7 +283,7 @@ GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
* Resolve a submodule url relative to the given repository. * Resolve a submodule url relative to the given repository.
* *
* @param out buffer to store the absolute submodule url in * @param out buffer to store the absolute submodule url in
* @param repository Pointer to repository object * @param repo Pointer to repository object
* @param url Relative url * @param url Relative url
* @return 0 or an error code * @return 0 or an error code
*/ */

View File

@ -151,7 +151,7 @@ GIT_EXTERN(int) git_tree_entry_bypath(
* and must be freed explicitly with `git_tree_entry_free()`. * and must be freed explicitly with `git_tree_entry_free()`.
* *
* @param dest pointer where to store the copy * @param dest pointer where to store the copy
* @param entry tree entry to duplicate * @param source tree entry to duplicate
* @return 0 or an error code * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source); GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source);

View File

@ -154,15 +154,15 @@ typedef struct git_packbuilder git_packbuilder;
/** Time in a signature */ /** Time in a signature */
typedef struct git_time { typedef struct git_time {
git_time_t time; /** time in seconds from epoch */ git_time_t time; /**< time in seconds from epoch */
int offset; /** timezone offset, in minutes */ int offset; /**< timezone offset, in minutes */
} git_time; } git_time;
/** An action signature (e.g. for committers, taggers, etc) */ /** An action signature (e.g. for committers, taggers, etc) */
typedef struct git_signature { typedef struct git_signature {
char *name; /** full name of the author */ char *name; /**< full name of the author */
char *email; /** email of the author */ char *email; /**< email of the author */
git_time when; /** time when the action happened */ git_time when; /**< time when the action happened */
} git_signature; } git_signature;
/** In-memory representation of a reference. */ /** In-memory representation of a reference. */
@ -183,9 +183,9 @@ typedef struct git_status_list git_status_list;
/** Basic type of any Git reference. */ /** Basic type of any Git reference. */
typedef enum { typedef enum {
GIT_REF_INVALID = 0, /** Invalid reference */ GIT_REF_INVALID = 0, /**< Invalid reference */
GIT_REF_OID = 1, /** A reference which points at an object id */ GIT_REF_OID = 1, /**< A reference which points at an object id */
GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */ GIT_REF_SYMBOLIC = 2, /**< A reference which points at another reference */
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC, GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC,
} git_ref_t; } git_ref_t;
@ -314,12 +314,12 @@ typedef enum {
* when we don't want any particular ignore rule to be specified. * when we don't want any particular ignore rule to be specified.
*/ */
typedef enum { typedef enum {
GIT_SUBMODULE_IGNORE_RESET = -1, /* reset to on-disk value */ GIT_SUBMODULE_IGNORE_RESET = -1, /**< reset to on-disk value */
GIT_SUBMODULE_IGNORE_NONE = 1, /* any change or untracked == dirty */ GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */ GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
GIT_SUBMODULE_IGNORE_ALL = 4, /* never dirty */ GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
GIT_SUBMODULE_IGNORE_DEFAULT = 0 GIT_SUBMODULE_IGNORE_DEFAULT = 0
} git_submodule_ignore_t; } git_submodule_ignore_t;

6
script/install-deps-linux.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -x
sudo apt-get -qq update &&
sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server

5
script/install-deps-osx.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -x
brew install libssh2 cmake

View File

@ -281,7 +281,7 @@ uint32_t git_attr_file__name_hash(const char *name)
int git_attr_file__lookup_one( int git_attr_file__lookup_one(
git_attr_file *file, git_attr_file *file,
const git_attr_path *path, git_attr_path *path,
const char *attr, const char *attr,
const char **value) const char **value)
{ {
@ -342,14 +342,11 @@ int git_attr_file__load_standalone(git_attr_file **out, const char *path)
bool git_attr_fnmatch__match( bool git_attr_fnmatch__match(
git_attr_fnmatch *match, git_attr_fnmatch *match,
const git_attr_path *path) git_attr_path *path)
{ {
const char *filename; const char *filename;
int flags = 0; int flags = 0;
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir)
return false;
if (match->flags & GIT_ATTR_FNMATCH_ICASE) if (match->flags & GIT_ATTR_FNMATCH_ICASE)
flags |= FNM_CASEFOLD; flags |= FNM_CASEFOLD;
if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR) if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
@ -365,12 +362,28 @@ bool git_attr_fnmatch__match(
flags |= FNM_LEADING_DIR; flags |= FNM_LEADING_DIR;
} }
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
int matchval;
/* for attribute checks or root ignore checks, fail match */
if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
path->basename == path->path)
return false;
/* for ignore checks, use container of current item for check */
path->basename[-1] = '\0';
flags |= FNM_LEADING_DIR;
matchval = p_fnmatch(match->pattern, path->path, flags);
path->basename[-1] = '/';
return (matchval != FNM_NOMATCH);
}
return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH); return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
} }
bool git_attr_rule__match( bool git_attr_rule__match(
git_attr_rule *rule, git_attr_rule *rule,
const git_attr_path *path) git_attr_path *path)
{ {
bool matched = git_attr_fnmatch__match(&rule->match, path); bool matched = git_attr_fnmatch__match(&rule->match, path);

View File

@ -138,7 +138,7 @@ int git_attr_file__clear_rules(
int git_attr_file__lookup_one( int git_attr_file__lookup_one(
git_attr_file *file, git_attr_file *file,
const git_attr_path *path, git_attr_path *path,
const char *attr, const char *attr,
const char **value); const char **value);
@ -162,13 +162,13 @@ extern int git_attr_fnmatch__parse(
extern bool git_attr_fnmatch__match( extern bool git_attr_fnmatch__match(
git_attr_fnmatch *rule, git_attr_fnmatch *rule,
const git_attr_path *path); git_attr_path *path);
extern void git_attr_rule__free(git_attr_rule *rule); extern void git_attr_rule__free(git_attr_rule *rule);
extern bool git_attr_rule__match( extern bool git_attr_rule__match(
git_attr_rule *rule, git_attr_rule *rule,
const git_attr_path *path); git_attr_path *path);
extern git_attr_assignment *git_attr_rule__lookup_assignment( extern git_attr_assignment *git_attr_rule__lookup_assignment(
git_attr_rule *rule, const char *name); git_attr_rule *rule, const char *name);

View File

@ -53,7 +53,7 @@ int git_attr_cache__alloc_file_entry(
cachesize++; cachesize++;
} }
ce = git_pool_mallocz(pool, cachesize); ce = git_pool_mallocz(pool, (uint32_t)cachesize);
GITERR_CHECK_ALLOC(ce); GITERR_CHECK_ALLOC(ce);
if (baselen) { if (baselen) {
@ -349,14 +349,11 @@ int git_attr_cache__do_init(git_repository *repo)
{ {
int ret = 0; int ret = 0;
git_attr_cache *cache = git_repository_attr_cache(repo); git_attr_cache *cache = git_repository_attr_cache(repo);
git_config *cfg; git_config *cfg = NULL;
if (cache) if (cache)
return 0; return 0;
if ((ret = git_repository_config__weakptr(&cfg, repo)) < 0)
return ret;
cache = git__calloc(1, sizeof(git_attr_cache)); cache = git__calloc(1, sizeof(git_attr_cache));
GITERR_CHECK_ALLOC(cache); GITERR_CHECK_ALLOC(cache);
@ -367,6 +364,9 @@ int git_attr_cache__do_init(git_repository *repo)
return -1; return -1;
} }
if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
goto cancel;
/* cache config settings for attributes and ignores */ /* cache config settings for attributes and ignores */
ret = attr_cache__lookup_path( ret = attr_cache__lookup_path(
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG); &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
@ -390,11 +390,14 @@ int git_attr_cache__do_init(git_repository *repo)
if (cache) if (cache)
goto cancel; /* raced with another thread, free this but no error */ goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg);
/* insert default macros */ /* insert default macros */
return git_attr_add_macro(repo, "binary", "-diff -crlf -text"); return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
cancel: cancel:
attr_cache__free(cache); attr_cache__free(cache);
git_config_free(cfg);
return ret; return ret;
} }

View File

@ -334,7 +334,8 @@ int git_blob_is_binary(const git_blob *blob)
assert(blob); assert(blob);
content.ptr = blob->odb_object->buffer; content.ptr = blob->odb_object->buffer;
content.size = min(blob->odb_object->cached.size, 4000); content.size =
min(blob->odb_object->cached.size, GIT_FILTER_BYTES_TO_CHECK_NUL);
content.asize = 0; content.asize = 0;
return git_buf_text_is_binary(&content); return git_buf_text_is_binary(&content);

View File

@ -22,6 +22,7 @@
#include "refs.h" #include "refs.h"
#include "path.h" #include "path.h"
#include "repository.h" #include "repository.h"
#include "odb.h"
static int create_branch( static int create_branch(
git_reference **branch, git_reference **branch,
@ -105,54 +106,6 @@ static int create_tracking_branch(
git_reference_name(*branch)); git_reference_name(*branch));
} }
struct head_info {
git_repository *repo;
git_oid remote_head_oid;
git_buf branchname;
const git_refspec *refspec;
bool found;
};
static int reference_matches_remote_head(
const char *reference_name,
void *payload)
{
struct head_info *head_info = (struct head_info *)payload;
git_oid oid;
int error;
/* TODO: Should we guard against references
* which name doesn't start with refs/heads/ ?
*/
error = git_reference_name_to_id(&oid, head_info->repo, reference_name);
if (error == GIT_ENOTFOUND) {
/* If the reference doesn't exists, it obviously cannot match the
* expected oid. */
giterr_clear();
return 0;
}
if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) {
/* Determine the local reference name from the remote tracking one */
error = git_refspec_rtransform(
&head_info->branchname, head_info->refspec, reference_name);
if (!error &&
git_buf_len(&head_info->branchname) > 0 &&
!(error = git_buf_sets(
&head_info->branchname,
git_buf_cstr(&head_info->branchname) +
strlen(GIT_REFS_HEADS_DIR))))
{
head_info->found = true;
error = GIT_ITEROVER;
}
}
return error;
}
static int update_head_to_new_branch( static int update_head_to_new_branch(
git_repository *repo, git_repository *repo,
const git_oid *target, const git_oid *target,
@ -161,7 +114,12 @@ static int update_head_to_new_branch(
const char *reflog_message) const char *reflog_message)
{ {
git_reference *tracking_branch = NULL; git_reference *tracking_branch = NULL;
int error = create_tracking_branch(&tracking_branch, repo, target, name, int error;
if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
name += strlen(GIT_REFS_HEADS_DIR);
error = create_tracking_branch(&tracking_branch, repo, target, name,
signature, reflog_message); signature, reflog_message);
if (!error) if (!error)
@ -171,6 +129,10 @@ static int update_head_to_new_branch(
git_reference_free(tracking_branch); git_reference_free(tracking_branch);
/* if it already existed, then the user's refspec created it for us, ignore it' */
if (error == GIT_EEXISTS)
error = 0;
return error; return error;
} }
@ -180,12 +142,13 @@ static int update_head_to_remote(
const git_signature *signature, const git_signature *signature,
const char *reflog_message) const char *reflog_message)
{ {
int error = 0; int error = 0, found_branch = 0;
size_t refs_len; size_t refs_len;
git_refspec dummy_spec; git_refspec dummy_spec, *refspec;
const git_remote_head *remote_head, **refs; const git_remote_head *remote_head, **refs;
struct head_info head_info; const git_oid *remote_head_id;
git_buf remote_master_name = GIT_BUF_INIT; git_buf remote_master_name = GIT_BUF_INIT;
git_buf branch = GIT_BUF_INIT;
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
return error; return error;
@ -195,63 +158,45 @@ static int update_head_to_remote(
return setup_tracking_config( return setup_tracking_config(
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE); repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
error = git_remote_default_branch(&branch, remote);
if (error == GIT_ENOTFOUND) {
git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE);
} else {
found_branch = 1;
}
/* Get the remote's HEAD. This is always the first ref in the list. */ /* Get the remote's HEAD. This is always the first ref in the list. */
remote_head = refs[0]; remote_head = refs[0];
assert(remote_head); assert(remote_head);
memset(&head_info, 0, sizeof(head_info)); remote_head_id = &remote_head->oid;
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
head_info.repo = repo;
head_info.refspec =
git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
if (head_info.refspec == NULL) { if (refspec == NULL) {
memset(&dummy_spec, 0, sizeof(git_refspec)); memset(&dummy_spec, 0, sizeof(git_refspec));
head_info.refspec = &dummy_spec; refspec = &dummy_spec;
} }
/* Determine the remote tracking reference name from the local master */ /* Determine the remote tracking reference name from the local master */
if ((error = git_refspec_transform( if ((error = git_refspec_transform(
&remote_master_name, &remote_master_name,
head_info.refspec, refspec,
GIT_REFS_HEADS_MASTER_FILE)) < 0) git_buf_cstr(&branch))) < 0)
return error; return error;
/* Check to see if the remote HEAD points to the remote master */ if (found_branch) {
error = reference_matches_remote_head(
git_buf_cstr(&remote_master_name), &head_info);
if (error < 0 && error != GIT_ITEROVER)
goto cleanup;
if (head_info.found) {
error = update_head_to_new_branch( error = update_head_to_new_branch(
repo, repo,
&head_info.remote_head_oid, remote_head_id,
git_buf_cstr(&head_info.branchname), git_buf_cstr(&branch),
signature, reflog_message);
goto cleanup;
}
/* Not master. Check all the other refs. */
error = git_reference_foreach_name(
repo, reference_matches_remote_head, &head_info);
if (error < 0 && error != GIT_ITEROVER)
goto cleanup;
if (head_info.found) {
error = update_head_to_new_branch(
repo,
&head_info.remote_head_oid,
git_buf_cstr(&head_info.branchname),
signature, reflog_message); signature, reflog_message);
} else { } else {
error = git_repository_set_head_detached( error = git_repository_set_head_detached(
repo, &head_info.remote_head_oid, signature, reflog_message); repo, remote_head_id, signature, reflog_message);
} }
cleanup:
git_buf_free(&remote_master_name); git_buf_free(&remote_master_name);
git_buf_free(&head_info.branchname); git_buf_free(&branch);
return error; return error;
} }
@ -297,6 +242,15 @@ static int create_and_configure_origin(
int error; int error;
git_remote *origin = NULL; git_remote *origin = NULL;
const char *name; const char *name;
char buf[GIT_PATH_MAX];
/* If the path exists and is a dir, the url should be the absolute path */
if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
if (p_realpath(url, buf) == NULL)
return -1;
url = buf;
}
name = options->remote_name ? options->remote_name : "origin"; name = options->remote_name ? options->remote_name : "origin";
if ((error = git_remote_create(&origin, repo, name, url)) < 0) if ((error = git_remote_create(&origin, repo, name, url)) < 0)
@ -336,59 +290,86 @@ static bool should_checkout(
return !git_repository_head_unborn(repo); return !git_repository_head_unborn(repo);
} }
int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature) static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature, const char *reflog_message)
{ {
int error = 0, old_fetchhead; int error;
git_strarray refspecs;
git_buf reflog_message = GIT_BUF_INIT;
assert(repo && remote); if (branch)
error = update_head_to_branch(repo, git_remote_name(remote), branch,
signature, reflog_message);
/* Point HEAD to the same ref as the remote's head */
else
error = update_head_to_remote(repo, remote, signature, reflog_message);
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
error = git_checkout_head(repo, co_opts);
return error;
}
int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
{
int error;
git_buf reflog_message = GIT_BUF_INIT;
git_remote *remote;
const git_remote_callbacks *callbacks;
assert(repo && _remote);
if (!git_repository_is_empty(repo)) { if (!git_repository_is_empty(repo)) {
giterr_set(GITERR_INVALID, "the repository is not empty"); giterr_set(GITERR_INVALID, "the repository is not empty");
return -1; return -1;
} }
if ((error = git_remote_dup(&remote, _remote)) < 0)
if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
return error; return error;
callbacks = git_remote_get_callbacks(_remote);
if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
(error = git_remote_set_callbacks(remote, callbacks)) < 0)
goto cleanup;
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0) if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
return error; goto cleanup;
old_fetchhead = git_remote_update_fetchhead(remote);
git_remote_set_update_fetchhead(remote, 0); git_remote_set_update_fetchhead(remote, 0);
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0) if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
goto cleanup; goto cleanup;
if (branch) error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
error = update_head_to_branch(repo, git_remote_name(remote), branch,
signature, git_buf_cstr(&reflog_message));
/* Point HEAD to the same ref as the remote's head */
else
error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message));
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
error = git_checkout_head(repo, co_opts);
cleanup: cleanup:
git_remote_set_update_fetchhead(remote, old_fetchhead); git_remote_free(remote);
/* Go back to the original refspecs */
{
int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs);
if (!error)
error = error_alt;
}
git_strarray_free(&refspecs);
git_buf_free(&reflog_message); git_buf_free(&reflog_message);
return error; return error;
} }
int git_clone__should_clone_local(const char *url, git_clone_local_t local)
{
const char *path;
int is_url;
if (local == GIT_CLONE_NO_LOCAL)
return false;
is_url = !git__prefixcmp(url, "file://");
if (is_url && local != GIT_CLONE_LOCAL && local != GIT_CLONE_LOCAL_NO_LINKS )
return false;
path = url;
if (is_url)
path = url + strlen("file://");
if ((git_path_exists(path) && git_path_isdir(path)) && local != GIT_CLONE_NO_LOCAL)
return true;
return false;
}
int git_clone( int git_clone(
git_repository **out, git_repository **out,
const char *url, const char *url,
@ -423,8 +404,16 @@ int git_clone(
return error; return error;
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) { if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
error = git_clone_into( if (git_clone__should_clone_local(url, options.local)) {
repo, origin, &options.checkout_opts, options.checkout_branch, options.signature); int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
error = git_clone_local_into(
repo, origin, &options.checkout_opts,
options.checkout_branch, link, options.signature);
} else {
error = git_clone_into(
repo, origin, &options.checkout_opts,
options.checkout_branch, options.signature);
}
git_remote_free(origin); git_remote_free(origin);
} }
@ -451,3 +440,91 @@ int git_clone_init_options(git_clone_options *opts, unsigned int version)
opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT); opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
return 0; return 0;
} }
static const char *repository_base(git_repository *repo)
{
if (git_repository_is_bare(repo))
return git_repository_path(repo);
return git_repository_workdir(repo);
}
static bool can_link(const char *src, const char *dst, int link)
{
#ifdef GIT_WIN32
return false;
#else
struct stat st_src, st_dst;
if (!link)
return false;
if (p_stat(src, &st_src) < 0)
return false;
if (p_stat(dst, &st_dst) < 0)
return false;
return st_src.st_dev == st_dst.st_dev;
#endif
}
int git_clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
{
int error, flags;
git_repository *src;
git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
git_buf reflog_message = GIT_BUF_INIT;
assert(repo && remote);
if (!git_repository_is_empty(repo)) {
giterr_set(GITERR_INVALID, "the repository is not empty");
return -1;
}
/*
* Let's figure out what path we should use for the source
* repo, if it's not rooted, the path should be relative to
* the repository's worktree/gitdir.
*/
if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
return error;
/* Copy .git/objects/ from the source to the target */
if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
git_buf_free(&src_path);
return error;
}
git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
error = -1;
goto cleanup;
}
flags = 0;
if (can_link(git_repository_path(src), git_repository_path(repo), link))
flags |= GIT_CPDIR_LINK_FILES;
if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
flags, GIT_OBJECT_DIR_MODE)) < 0)
goto cleanup;
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
goto cleanup;
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
cleanup:
git_buf_free(&reflog_message);
git_buf_free(&src_path);
git_buf_free(&src_odb);
git_buf_free(&dst_odb);
git_repository_free(src);
return error;
}

12
src/clone.h Normal file
View File

@ -0,0 +1,12 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_clone_h__
#define INCLUDE_clone_h__
extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
#endif

View File

@ -139,7 +139,7 @@ int git_config_open_ondisk(git_config **out, const char *path)
int git_config_snapshot(git_config **out, git_config *in) int git_config_snapshot(git_config **out, git_config *in)
{ {
int error; int error = 0;
size_t i; size_t i;
file_internal *internal; file_internal *internal;
git_config *config; git_config *config;
@ -153,19 +153,19 @@ int git_config_snapshot(git_config **out, git_config *in)
git_config_backend *b; git_config_backend *b;
if ((error = internal->file->snapshot(&b, internal->file)) < 0) if ((error = internal->file->snapshot(&b, internal->file)) < 0)
goto on_error; break;
if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) { if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
b->free(b); b->free(b);
goto on_error; break;
} }
} }
*out = config; if (error < 0)
return error; git_config_free(config);
else
*out = config;
on_error:
git_config_free(config);
return error; return error;
} }

View File

@ -76,4 +76,10 @@ extern int git_config__get_bool_force(
extern int git_config__get_int_force( extern int git_config__get_int_force(
const git_config *cfg, const char *key, int fallback_value); const git_config *cfg, const char *key, int fallback_value);
/* API for repository cvar-style lookups from config - not cached, but
* uses cvar value maps and fallbacks
*/
extern int git_config__cvar(
int *out, git_config *config, git_cvar_cached cvar);
#endif #endif

View File

@ -7,11 +7,11 @@
#include "common.h" #include "common.h"
#include "fileops.h" #include "fileops.h"
#include "repository.h"
#include "config.h" #include "config.h"
#include "git2/config.h" #include "git2/config.h"
#include "vector.h" #include "vector.h"
#include "filter.h" #include "filter.h"
#include "repository.h"
struct map_data { struct map_data {
const char *cvar_name; const char *cvar_name;
@ -51,6 +51,12 @@ static git_cvar_map _cvar_map_autocrlf[] = {
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT} {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
}; };
static git_cvar_map _cvar_map_safecrlf[] = {
{GIT_CVAR_FALSE, NULL, GIT_SAFE_CRLF_FALSE},
{GIT_CVAR_TRUE, NULL, GIT_SAFE_CRLF_FAIL},
{GIT_CVAR_STRING, "warn", GIT_SAFE_CRLF_WARN}
};
/* /*
* Generic map for integer values * Generic map for integer values
*/ */
@ -68,33 +74,39 @@ static struct map_data _cvar_maps[] = {
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT }, {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT }, {"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT }, {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
{"core.safecrlf", NULL, 0, GIT_SAFE_CRLF_DEFAULT}, {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
{"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
}; };
int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
{
int error = 0;
struct map_data *data = &_cvar_maps[(int)cvar];
const git_config_entry *entry;
git_config__lookup_entry(&entry, config, data->cvar_name, false);
if (!entry)
*out = data->default_value;
else if (data->maps)
error = git_config_lookup_map_value(
out, data->maps, data->map_count, entry->value);
else
error = git_config_parse_bool(out, entry->value);
return error;
}
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar) int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
{ {
*out = repo->cvar_cache[(int)cvar]; *out = repo->cvar_cache[(int)cvar];
if (*out == GIT_CVAR_NOT_CACHED) { if (*out == GIT_CVAR_NOT_CACHED) {
struct map_data *data = &_cvar_maps[(int)cvar];
git_config *config;
int error; int error;
const git_config_entry *entry; git_config *config;
if ((error = git_repository_config__weakptr(&config, repo)) < 0) if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
return error; (error = git_config__cvar(out, config, cvar)) < 0)
git_config__lookup_entry(&entry, config, data->cvar_name, false);
if (!entry)
*out = data->default_value;
else if (data->maps)
error = git_config_lookup_map_value(
out, data->maps, data->map_count, entry->value);
else
error = git_config_parse_bool(out, entry->value);
if (error < 0)
return error; return error;
repo->cvar_cache[(int)cvar] = *out; repo->cvar_cache[(int)cvar] = *out;

View File

@ -270,7 +270,6 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
if ((res = refcounted_strmap_alloc(&b->header.values)) < 0) if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
return res; return res;
git_mutex_init(&b->header.values_mutex);
git_array_init(b->readers); git_array_init(b->readers);
reader = git_array_alloc(b->readers); reader = git_array_alloc(b->readers);
if (!reader) { if (!reader) {
@ -313,6 +312,7 @@ static int config__refresh(git_config_backend *cfg)
goto out; goto out;
reader = git_array_get(b->readers, git_array_size(b->readers) - 1); reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
GITERR_CHECK_ALLOC(reader);
if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0) if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0)
goto out; goto out;
@ -327,7 +327,8 @@ static int config__refresh(git_config_backend *cfg)
out: out:
refcounted_strmap_free(values); refcounted_strmap_free(values);
git_buf_free(&reader->buffer); if (reader)
git_buf_free(&reader->buffer);
return error; return error;
} }
@ -344,8 +345,8 @@ static int config_refresh(git_config_backend *cfg)
&reader->buffer, reader->file_path, &reader->buffer, reader->file_path,
&reader->file_mtime, &reader->file_size, &updated); &reader->file_mtime, &reader->file_size, &updated);
if (error < 0) if (error < 0 && error != GIT_ENOTFOUND)
return (error == GIT_ENOTFOUND) ? 0 : error; return error;
if (updated) if (updated)
any_updated = 1; any_updated = 1;
@ -373,6 +374,7 @@ static void backend_free(git_config_backend *_backend)
git__free(backend->file_path); git__free(backend->file_path);
refcounted_strmap_free(backend->header.values); refcounted_strmap_free(backend->header.values);
git_mutex_free(&backend->header.values_mutex);
git__free(backend); git__free(backend);
} }
@ -684,6 +686,7 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
GITERR_CHECK_ALLOC(backend); GITERR_CHECK_ALLOC(backend);
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
git_mutex_init(&backend->header.values_mutex);
backend->file_path = git__strdup(path); backend->file_path = git__strdup(path);
GITERR_CHECK_ALLOC(backend->file_path); GITERR_CHECK_ALLOC(backend->file_path);
@ -756,6 +759,7 @@ static void backend_readonly_free(git_config_backend *_backend)
return; return;
refcounted_strmap_free(backend->header.values); refcounted_strmap_free(backend->header.values);
git_mutex_free(&backend->header.values_mutex);
git__free(backend); git__free(backend);
} }
@ -782,6 +786,7 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
GITERR_CHECK_ALLOC(backend); GITERR_CHECK_ALLOC(backend);
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
git_mutex_init(&backend->header.values_mutex);
backend->snapshot_from = in; backend->snapshot_from = in;

View File

@ -138,6 +138,10 @@ static int crlf_apply_to_odb(
if (git_buf_text_gather_stats(&stats, from, false)) if (git_buf_text_gather_stats(&stats, from, false))
return GIT_PASSTHROUGH; return GIT_PASSTHROUGH;
/* If there are no CR characters to filter out, then just pass */
if (!stats.cr)
return GIT_PASSTHROUGH;
/* If safecrlf is enabled, sanity-check the result. */ /* If safecrlf is enabled, sanity-check the result. */
if (stats.cr != stats.crlf || stats.lf != stats.crlf) { if (stats.cr != stats.crlf || stats.lf != stats.crlf) {
switch (ca->safe_crlf) { switch (ca->safe_crlf) {

View File

@ -390,7 +390,7 @@ static int diff_list_apply_options(
git_diff *diff, git_diff *diff,
const git_diff_options *opts) const git_diff_options *opts)
{ {
git_config *cfg; git_config *cfg = NULL;
git_repository *repo = diff->repo; git_repository *repo = diff->repo;
git_pool *pool = &diff->pool; git_pool *pool = &diff->pool;
int val; int val;
@ -415,20 +415,20 @@ static int diff_list_apply_options(
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* load config values that affect diff behavior */ /* load config values that affect diff behavior */
if ((val = git_repository_config__weakptr(&cfg, repo)) < 0) if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
return val; return val;
if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val) if (!git_config__cvar(&val, cfg, GIT_CVAR_SYMLINKS) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val) if (!git_config__cvar(&val, cfg, GIT_CVAR_IGNORESTAT) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT; diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT;
if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 && if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val) !git_config__cvar(&val, cfg, GIT_CVAR_FILEMODE) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS; diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val) if (!git_config__cvar(&val, cfg, GIT_CVAR_TRUSTCTIME) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
@ -490,8 +490,6 @@ static int diff_list_apply_options(
/* strdup prefix from pool so we're not dependent on external data */ /* strdup prefix from pool so we're not dependent on external data */
diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix); diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix);
diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix); diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix);
if (!diff->opts.old_prefix || !diff->opts.new_prefix)
return -1;
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
const char *tmp_prefix = diff->opts.old_prefix; const char *tmp_prefix = diff->opts.old_prefix;
@ -499,7 +497,10 @@ static int diff_list_apply_options(
diff->opts.new_prefix = tmp_prefix; diff->opts.new_prefix = tmp_prefix;
} }
return 0; git_config_free(cfg);
/* check strdup results for error */
return (!diff->opts.old_prefix || !diff->opts.new_prefix) ? -1 : 0;
} }
static void diff_list_free(git_diff *diff) static void diff_list_free(git_diff *diff)
@ -642,7 +643,6 @@ typedef struct {
git_iterator *new_iter; git_iterator *new_iter;
const git_index_entry *oitem; const git_index_entry *oitem;
const git_index_entry *nitem; const git_index_entry *nitem;
git_buf ignore_prefix;
} diff_in_progress; } diff_in_progress;
#define MODE_BITS_MASK 0000777 #define MODE_BITS_MASK 0000777
@ -858,24 +858,13 @@ static int handle_unmatched_new_item(
/* check if this is a prefix of the other side */ /* check if this is a prefix of the other side */
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
/* check if this is contained in an ignored parent directory */ /* update delta_type if this item is ignored */
if (git_buf_len(&info->ignore_prefix)) { if (git_iterator_current_is_ignored(info->new_iter))
if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0) delta_type = GIT_DELTA_IGNORED;
delta_type = GIT_DELTA_IGNORED;
else
git_buf_clear(&info->ignore_prefix);
}
if (nitem->mode == GIT_FILEMODE_TREE) { if (nitem->mode == GIT_FILEMODE_TREE) {
bool recurse_into_dir = contains_oitem; bool recurse_into_dir = contains_oitem;
/* if not already inside an ignored dir, check if this is ignored */
if (delta_type != GIT_DELTA_IGNORED &&
git_iterator_current_is_ignored(info->new_iter)) {
delta_type = GIT_DELTA_IGNORED;
git_buf_sets(&info->ignore_prefix, nitem->path);
}
/* check if user requests recursion into this type of dir */ /* check if user requests recursion into this type of dir */
recurse_into_dir = contains_oitem || recurse_into_dir = contains_oitem ||
(delta_type == GIT_DELTA_UNTRACKED && (delta_type == GIT_DELTA_UNTRACKED &&
@ -952,27 +941,12 @@ static int handle_unmatched_new_item(
} }
} }
/* In core git, the next two checks are effectively reversed --
* i.e. when an file contained in an ignored directory is explicitly
* ignored, it shows up as an ignored file in the diff list, even though
* other untracked files in the same directory are skipped completely.
*
* To me, this seems odd. If the directory is ignored and the file is
* untracked, we should skip it consistently, regardless of whether it
* happens to match a pattern in the ignore file.
*
* To match the core git behavior, reverse the following two if checks
* so that individual file ignores are checked before container
* directory exclusions are used to skip the file.
*/
else if (delta_type == GIT_DELTA_IGNORED && else if (delta_type == GIT_DELTA_IGNORED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
git_iterator_current_tree_is_ignored(info->new_iter))
/* item contained in ignored directory, so skip over it */ /* item contained in ignored directory, so skip over it */
return git_iterator_advance(&info->nitem, info->new_iter); return git_iterator_advance(&info->nitem, info->new_iter);
else if (git_iterator_current_is_ignored(info->new_iter))
delta_type = GIT_DELTA_IGNORED;
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
delta_type = GIT_DELTA_ADDED; delta_type = GIT_DELTA_ADDED;
@ -1089,7 +1063,6 @@ int git_diff__from_iterators(
info.repo = repo; info.repo = repo;
info.old_iter = old_iter; info.old_iter = old_iter;
info.new_iter = new_iter; info.new_iter = new_iter;
git_buf_init(&info.ignore_prefix, 0);
/* make iterators have matching icase behavior */ /* make iterators have matching icase behavior */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
@ -1144,8 +1117,6 @@ cleanup:
else else
git_diff_free(diff); git_diff_free(diff);
git_buf_free(&info.ignore_prefix);
return error; return error;
} }

View File

@ -233,17 +233,17 @@ static int git_diff_driver_load(
return 0; return 0;
} }
drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
GITERR_CHECK_ALLOC(drv);
drv->type = DIFF_DRIVER_AUTO;
memcpy(drv->name, driver_name, namelen);
/* if you can't read config for repo, just use default driver */ /* if you can't read config for repo, just use default driver */
if (git_repository_config_snapshot(&cfg, repo) < 0) { if (git_repository_config_snapshot(&cfg, repo) < 0) {
giterr_clear(); giterr_clear();
goto done; goto done;
} }
drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
GITERR_CHECK_ALLOC(drv);
drv->type = DIFF_DRIVER_AUTO;
memcpy(drv->name, driver_name, namelen);
if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0)
goto done; goto done;
@ -397,7 +397,11 @@ void git_diff_driver_update_options(
int git_diff_driver_content_is_binary( int git_diff_driver_content_is_binary(
git_diff_driver *driver, const char *content, size_t content_len) git_diff_driver *driver, const char *content, size_t content_len)
{ {
const git_buf search = { (char *)content, 0, min(content_len, 4000) }; git_buf search;
search.ptr = (char *)content;
search.size = min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL);
search.asize = 0;
GIT_UNUSED(driver); GIT_UNUSED(driver);

View File

@ -287,33 +287,46 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
{ {
git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL; git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL;
const void *old_data, *new_data; const void *old_data, *new_data;
size_t old_data_len, new_data_len, delta_data_len, inflated_len, remain; git_off_t old_data_len, new_data_len;
unsigned long delta_data_len, inflated_len;
const char *out_type = "literal"; const char *out_type = "literal";
char *ptr; char *scan, *end;
int error; int error;
old_data = old ? git_blob_rawcontent(old) : NULL; old_data = old ? git_blob_rawcontent(old) : NULL;
new_data = new ? git_blob_rawcontent(new) : NULL; new_data = new ? git_blob_rawcontent(new) : NULL;
old_data_len = old ? (size_t)git_blob_rawsize(old) : 0; old_data_len = old ? git_blob_rawsize(old) : 0;
new_data_len = new ? (size_t)git_blob_rawsize(new) : 0; new_data_len = new ? git_blob_rawsize(new) : 0;
/* The git_delta function accepts unsigned long only */
if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len))
return GIT_EBUFS;
out = &deflate; out = &deflate;
inflated_len = new_data_len; inflated_len = (unsigned long)new_data_len;
if ((error = git_zstream_deflatebuf( if ((error = git_zstream_deflatebuf(
&deflate, new_data, new_data_len)) < 0) out, new_data, (size_t)new_data_len)) < 0)
goto done; goto done;
if (old && new) { /* The git_delta function accepts unsigned long only */
void *delta_data; if (!git__is_ulong((git_off_t)deflate.size)) {
error = GIT_EBUFS;
goto done;
}
delta_data = git_delta(old_data, old_data_len, new_data, if (old && new) {
new_data_len, &delta_data_len, deflate.size); void *delta_data = git_delta(
old_data, (unsigned long)old_data_len,
new_data, (unsigned long)new_data_len,
&delta_data_len, (unsigned long)deflate.size);
if (delta_data) { if (delta_data) {
error = git_zstream_deflatebuf(&delta, delta_data, delta_data_len); error = git_zstream_deflatebuf(
free(delta_data); &delta, delta_data, (size_t)delta_data_len);
git__free(delta_data);
if (error < 0) if (error < 0)
goto done; goto done;
@ -326,18 +339,20 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
} }
} }
git_buf_printf(pi->buf, "%s %" PRIuZ "\n", out_type, inflated_len); git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len);
pi->line.num_lines++; pi->line.num_lines++;
for (ptr = out->ptr, remain = out->size; remain > 0; ) { for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) {
size_t chunk_len = (52 < remain) ? 52 : remain; size_t chunk_len = end - scan;
if (chunk_len > 52)
chunk_len = 52;
if (chunk_len <= 26) if (chunk_len <= 26)
git_buf_putc(pi->buf, chunk_len + 'A' - 1); git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
else else
git_buf_putc(pi->buf, chunk_len - 26 + 'a' - 1); git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
git_buf_put_base85(pi->buf, ptr, chunk_len); git_buf_put_base85(pi->buf, scan, chunk_len);
git_buf_putc(pi->buf, '\n'); git_buf_putc(pi->buf, '\n');
if (git_buf_oom(pi->buf)) { if (git_buf_oom(pi->buf)) {
@ -345,8 +360,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
goto done; goto done;
} }
ptr += chunk_len; scan += chunk_len;
remain -= chunk_len;
pi->line.num_lines++; pi->line.num_lines++;
} }
@ -365,26 +379,33 @@ static int diff_print_patch_file_binary(
git_blob *old = NULL, *new = NULL; git_blob *old = NULL, *new = NULL;
const git_oid *old_id, *new_id; const git_oid *old_id, *new_id;
int error; int error;
size_t pre_binary_size;
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) { if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
pi->line.num_lines = 1; goto noshow;
return diff_delta_format_with_paths(
pi->buf, delta, oldpfx, newpfx,
"Binary files %s%s and %s%s differ\n");
}
pre_binary_size = pi->buf->size;
git_buf_printf(pi->buf, "GIT binary patch\n"); git_buf_printf(pi->buf, "GIT binary patch\n");
pi->line.num_lines++; pi->line.num_lines++;
old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL; old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL;
new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL; new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL;
if ((old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) || if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0)
(new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) || goto done;
(error = print_binary_hunk(pi, old, new)) < 0 || if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0)
goto done;
if ((error = print_binary_hunk(pi, old, new)) < 0 ||
(error = git_buf_putc(pi->buf, '\n')) < 0 || (error = git_buf_putc(pi->buf, '\n')) < 0 ||
(error = print_binary_hunk(pi, new, old)) < 0) (error = print_binary_hunk(pi, new, old)) < 0)
goto done; {
if (error == GIT_EBUFS) {
giterr_clear();
git_buf_truncate(pi->buf, pre_binary_size);
goto noshow;
}
}
pi->line.num_lines++; pi->line.num_lines++;
@ -393,6 +414,12 @@ done:
git_blob_free(new); git_blob_free(new);
return error; return error;
noshow:
pi->line.num_lines = 1;
return diff_delta_format_with_paths(
pi->buf, delta, oldpfx, newpfx,
"Binary files %s%s and %s%s differ\n");
} }
static int diff_print_patch_file( static int diff_print_patch_file(

View File

@ -740,9 +740,11 @@ static int _cp_r_callback(void *ref, git_buf *from)
return error; return error;
/* make symlink or regular file */ /* make symlink or regular file */
if (S_ISLNK(from_st.st_mode)) if (info->flags & GIT_CPDIR_LINK_FILES) {
error = p_link(from->ptr, info->to.ptr);
} else if (S_ISLNK(from_st.st_mode)) {
error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size); error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
else { } else {
mode_t usemode = from_st.st_mode; mode_t usemode = from_st.st_mode;
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0) if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)

View File

@ -173,6 +173,7 @@ extern int git_futils_cp(
* - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
* source file to the target; with this flag, always use 0666 (or 0777 if * source file to the target; with this flag, always use 0666 (or 0777 if
* source has exec bits set) for target. * source has exec bits set) for target.
* - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
*/ */
typedef enum { typedef enum {
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0), GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
@ -181,6 +182,7 @@ typedef enum {
GIT_CPDIR_OVERWRITE = (1u << 3), GIT_CPDIR_OVERWRITE = (1u << 3),
GIT_CPDIR_CHMOD_DIRS = (1u << 4), GIT_CPDIR_CHMOD_DIRS = (1u << 4),
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5), GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
GIT_CPDIR_LINK_FILES = (1u << 6),
} git_futils_cpdir_flags; } git_futils_cpdir_flags;
/** /**

View File

@ -10,6 +10,10 @@
#include "common.h" #include "common.h"
#include "git2/filter.h" #include "git2/filter.h"
/* Amount of file to examine for NUL byte when checking binary-ness */
#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
/* Possible CRLF values */
typedef enum { typedef enum {
GIT_CRLF_GUESS = -1, GIT_CRLF_GUESS = -1,
GIT_CRLF_BINARY = 0, GIT_CRLF_BINARY = 0,

View File

@ -5,6 +5,40 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
/*
* This file contains code originally derrived from OpenBSD fnmatch.c
*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* /*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern. * Compares a filename or pathname to a pattern.

View File

@ -1,8 +1,29 @@
/* /*
* Copyright (C) the libgit2 contributors. All rights reserved. * Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
* *
* This file is part of libgit2, distributed under the GNU GPL v2 with * Redistribution and use in source and binary forms, with or without
* a Linking Exception. For full terms see the included COPYING file. * modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/ */
#ifndef INCLUDE_fnmatch__compat_h__ #ifndef INCLUDE_fnmatch__compat_h__
#define INCLUDE_fnmatch__compat_h__ #define INCLUDE_fnmatch__compat_h__

View File

@ -76,7 +76,7 @@ static void git__shutdown(void)
#if defined(GIT_THREADS) && defined(GIT_WIN32) #if defined(GIT_THREADS) && defined(GIT_WIN32)
static DWORD _tls_index; static DWORD _tls_index;
static DWORD _mutex = 0; static volatile LONG _mutex = 0;
static int synchronized_threads_init() static int synchronized_threads_init()
{ {

View File

@ -248,14 +248,15 @@ void git_ignore__free(git_ignores *ignores)
} }
static bool ignore_lookup_in_rules( static bool ignore_lookup_in_rules(
git_attr_file *file, git_attr_path *path, int *ignored) int *ignored, git_attr_file *file, git_attr_path *path)
{ {
size_t j; size_t j;
git_attr_fnmatch *match; git_attr_fnmatch *match;
git_vector_rforeach(&file->rules, j, match) { git_vector_rforeach(&file->rules, j, match) {
if (git_attr_fnmatch__match(match, path)) { if (git_attr_fnmatch__match(match, path)) {
*ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0); *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ?
GIT_IGNORE_TRUE : GIT_IGNORE_FALSE;
return true; return true;
} }
} }
@ -264,34 +265,34 @@ static bool ignore_lookup_in_rules(
} }
int git_ignore__lookup( int git_ignore__lookup(
git_ignores *ignores, const char *pathname, int *ignored) int *out, git_ignores *ignores, const char *pathname)
{ {
unsigned int i; unsigned int i;
git_attr_file *file; git_attr_file *file;
git_attr_path path; git_attr_path path;
*out = GIT_IGNORE_NOTFOUND;
if (git_attr_path__init( if (git_attr_path__init(
&path, pathname, git_repository_workdir(ignores->repo)) < 0) &path, pathname, git_repository_workdir(ignores->repo)) < 0)
return -1; return -1;
/* first process builtins - success means path was found */ /* first process builtins - success means path was found */
if (ignore_lookup_in_rules(ignores->ign_internal, &path, ignored)) if (ignore_lookup_in_rules(out, ignores->ign_internal, &path))
goto cleanup; goto cleanup;
/* next process files in the path */ /* next process files in the path */
git_vector_foreach(&ignores->ign_path, i, file) { git_vector_foreach(&ignores->ign_path, i, file) {
if (ignore_lookup_in_rules(file, &path, ignored)) if (ignore_lookup_in_rules(out, file, &path))
goto cleanup; goto cleanup;
} }
/* last process global ignores */ /* last process global ignores */
git_vector_foreach(&ignores->ign_global, i, file) { git_vector_foreach(&ignores->ign_global, i, file) {
if (ignore_lookup_in_rules(file, &path, ignored)) if (ignore_lookup_in_rules(out, file, &path))
goto cleanup; goto cleanup;
} }
*ignored = 0;
cleanup: cleanup:
git_attr_path__free(&path); git_attr_path__free(&path);
return 0; return 0;
@ -335,8 +336,6 @@ int git_ignore_path_is_ignored(
int error; int error;
const char *workdir; const char *workdir;
git_attr_path path; git_attr_path path;
char *tail, *end;
bool full_is_dir;
git_ignores ignores; git_ignores ignores;
unsigned int i; unsigned int i;
git_attr_file *file; git_attr_file *file;
@ -345,55 +344,42 @@ int git_ignore_path_is_ignored(
workdir = repo ? git_repository_workdir(repo) : NULL; workdir = repo ? git_repository_workdir(repo) : NULL;
if ((error = git_attr_path__init(&path, pathname, workdir)) < 0) memset(&path, 0, sizeof(path));
return error; memset(&ignores, 0, sizeof(ignores));
tail = path.path; if ((error = git_attr_path__init(&path, pathname, workdir)) < 0 ||
end = &path.full.ptr[path.full.size]; (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
full_is_dir = path.is_dir; goto cleanup;
while (1) { while (1) {
/* advance to next component of path */
path.basename = tail;
while (tail < end && *tail != '/') tail++;
*tail = '\0';
path.full.size = (tail - path.full.ptr);
path.is_dir = (tail == end) ? full_is_dir : true;
/* initialize ignores the first time through */
if (path.basename == path.path &&
(error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
break;
/* first process builtins - success means path was found */ /* first process builtins - success means path was found */
if (ignore_lookup_in_rules(ignores.ign_internal, &path, ignored)) if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path))
goto cleanup; goto cleanup;
/* next process files in the path */ /* next process files in the path */
git_vector_foreach(&ignores.ign_path, i, file) { git_vector_foreach(&ignores.ign_path, i, file) {
if (ignore_lookup_in_rules(file, &path, ignored)) if (ignore_lookup_in_rules(ignored, file, &path))
goto cleanup; goto cleanup;
} }
/* last process global ignores */ /* last process global ignores */
git_vector_foreach(&ignores.ign_global, i, file) { git_vector_foreach(&ignores.ign_global, i, file) {
if (ignore_lookup_in_rules(file, &path, ignored)) if (ignore_lookup_in_rules(ignored, file, &path))
goto cleanup; goto cleanup;
} }
/* if we found no rules before reaching the end, we're done */ /* move up one directory */
if (tail == end) if (path.basename == path.path)
break; break;
path.basename[-1] = '\0';
while (path.basename > path.path && *path.basename != '/')
path.basename--;
if (path.basename > path.path)
path.basename++;
path.is_dir = 1;
/* now add this directory to list of ignores */ if ((error = git_ignore__pop_dir(&ignores)) < 0)
if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
break; break;
/* reinstate divider in path */
*tail = '/';
while (*tail == '/') tail++;
} }
*ignored = 0; *ignored = 0;
@ -404,7 +390,6 @@ cleanup:
return error; return error;
} }
int git_ignore__check_pathspec_for_exact_ignores( int git_ignore__check_pathspec_for_exact_ignores(
git_repository *repo, git_repository *repo,
git_vector *vspec, git_vector *vspec,

View File

@ -42,7 +42,14 @@ extern int git_ignore__pop_dir(git_ignores *ign);
extern void git_ignore__free(git_ignores *ign); extern void git_ignore__free(git_ignores *ign);
extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored); enum {
GIT_IGNORE_UNCHECKED = -2,
GIT_IGNORE_NOTFOUND = -1,
GIT_IGNORE_FALSE = 0,
GIT_IGNORE_TRUE = 1,
};
extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path);
/* command line Git sometimes generates an error message if given a /* command line Git sometimes generates an error message if given a
* pathspec that contains an exact match to an ignored file (provided * pathspec that contains an exact match to an ignored file (provided

View File

@ -1104,6 +1104,15 @@ int git_index_remove_bypath(git_index *index, const char *path)
return 0; return 0;
} }
static bool valid_filemode(const int filemode)
{
return (filemode == GIT_FILEMODE_BLOB ||
filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
filemode == GIT_FILEMODE_LINK ||
filemode == GIT_FILEMODE_COMMIT);
}
int git_index_add(git_index *index, const git_index_entry *source_entry) int git_index_add(git_index *index, const git_index_entry *source_entry)
{ {
git_index_entry *entry = NULL; git_index_entry *entry = NULL;
@ -1111,6 +1120,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
assert(index && source_entry && source_entry->path); assert(index && source_entry && source_entry->path);
if (!valid_filemode(source_entry->mode)) {
giterr_set(GITERR_INDEX, "invalid filemode");
return -1;
}
if ((ret = index_entry_dup(&entry, source_entry)) < 0 || if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
(ret = index_insert(index, &entry, 1)) < 0) (ret = index_insert(index, &entry, 1)) < 0)
return ret; return ret;

View File

@ -34,7 +34,6 @@ struct git_indexer {
have_delta :1; have_delta :1;
struct git_pack_header hdr; struct git_pack_header hdr;
struct git_pack_file *pack; struct git_pack_file *pack;
git_filebuf pack_file;
unsigned int mode; unsigned int mode;
git_off_t off; git_off_t off;
git_off_t entry_start; git_off_t entry_start;
@ -67,33 +66,18 @@ const git_oid *git_indexer_hash(const git_indexer *idx)
return &idx->hash; return &idx->hash;
} }
static int open_pack(struct git_pack_file **out, const char *filename)
{
struct git_pack_file *pack;
if (git_packfile_alloc(&pack, filename) < 0)
return -1;
if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
giterr_set(GITERR_OS, "Failed to open packfile.");
git_packfile_free(pack);
return -1;
}
*out = pack;
return 0;
}
static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
{ {
int error; int error;
git_map map;
if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
return error;
memcpy(hdr, map.data, sizeof(*hdr));
p_munmap(&map);
/* Verify we recognize this pack file format. */ /* Verify we recognize this pack file format. */
if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
giterr_set(GITERR_OS, "Failed to read in pack header");
return error;
}
if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) { if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
giterr_set(GITERR_INDEXER, "Wrong pack signature"); giterr_set(GITERR_INDEXER, "Wrong pack signature");
return -1; return -1;
@ -124,9 +108,9 @@ int git_indexer_new(
void *progress_payload) void *progress_payload)
{ {
git_indexer *idx; git_indexer *idx;
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT;
static const char suff[] = "/pack"; static const char suff[] = "/pack";
int error; int error, fd = -1;
idx = git__calloc(1, sizeof(git_indexer)); idx = git__calloc(1, sizeof(git_indexer));
GITERR_CHECK_ALLOC(idx); GITERR_CHECK_ALLOC(idx);
@ -140,19 +124,30 @@ int git_indexer_new(
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
error = git_filebuf_open(&idx->pack_file, path.ptr, fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode);
GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
idx->mode);
git_buf_free(&path); git_buf_free(&path);
if (fd < 0)
goto cleanup;
error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path));
git_buf_free(&tmp_path);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
idx->pack->mwf.fd = fd;
if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
goto cleanup;
*out = idx; *out = idx;
return 0; return 0;
cleanup: cleanup:
if (fd != -1)
p_close(fd);
git_buf_free(&path); git_buf_free(&path);
git_filebuf_cleanup(&idx->pack_file); git_buf_free(&tmp_path);
git__free(idx); git__free(idx);
return -1; return -1;
} }
@ -429,6 +424,42 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
idx->inbuf_len += size - to_expell; idx->inbuf_len += size - to_expell;
} }
static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
{
git_file fd = idx->pack->mwf.fd;
long page_size = git__page_size();
git_off_t page_start, page_offset;
unsigned char *map_data;
git_map map;
int error;
/* the offset needs to be at the beginning of the a page boundary */
page_start = (offset / page_size) * page_size;
page_offset = offset - page_start;
if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
return error;
map_data = (unsigned char *)map.data;
memcpy(map_data + page_offset, data, size);
p_munmap(&map);
return 0;
}
static int append_to_pack(git_indexer *idx, const void *data, size_t size)
{
git_off_t current_size = idx->pack->mwf.size;
/* add the extra space we need at the end */
if (p_ftruncate(idx->pack->mwf.fd, current_size + size) < 0) {
giterr_system_set(errno);
return -1;
}
return write_at(idx, data, idx->pack->mwf.size, size);
}
int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats) int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats)
{ {
int error = -1; int error = -1;
@ -440,22 +471,13 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran
processed = stats->indexed_objects; processed = stats->indexed_objects;
if ((error = git_filebuf_write(&idx->pack_file, data, size)) < 0) if ((error = append_to_pack(idx, data, size)) < 0)
return error; return error;
hash_partially(idx, data, (int)size); hash_partially(idx, data, (int)size);
/* Make sure we set the new size of the pack */ /* Make sure we set the new size of the pack */
if (idx->opened_pack) { idx->pack->mwf.size += size;
idx->pack->mwf.size += size;
} else {
if ((error = open_pack(&idx->pack, idx->pack_file.path_lock)) < 0)
return error;
idx->opened_pack = 1;
mwf = &idx->pack->mwf;
if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
return error;
}
if (!idx->parsed_header) { if (!idx->parsed_header) {
unsigned int total_objects; unsigned int total_objects;
@ -616,17 +638,10 @@ static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
* Rewind the packfile by the trailer, as we might need to fix the * Rewind the packfile by the trailer, as we might need to fix the
* packfile by injecting objects at the tail and must overwrite it. * packfile by injecting objects at the tail and must overwrite it.
*/ */
static git_off_t seek_back_trailer(git_indexer *idx) static void seek_back_trailer(git_indexer *idx)
{ {
git_off_t off;
if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
return -1;
idx->pack->mwf.size -= GIT_OID_RAWSZ; idx->pack->mwf.size -= GIT_OID_RAWSZ;
git_mwindow_free_all(&idx->pack->mwf); git_mwindow_free_all(&idx->pack->mwf);
return off;
} }
static int inject_object(git_indexer *idx, git_oid *id) static int inject_object(git_indexer *idx, git_oid *id)
@ -642,7 +657,8 @@ static int inject_object(git_indexer *idx, git_oid *id)
size_t len, hdr_len; size_t len, hdr_len;
int error; int error;
entry_start = seek_back_trailer(idx); seek_back_trailer(idx);
entry_start = idx->pack->mwf.size;
if (git_odb_read(&obj, idx->odb, id) < 0) if (git_odb_read(&obj, idx->odb, id) < 0)
return -1; return -1;
@ -657,7 +673,9 @@ static int inject_object(git_indexer *idx, git_oid *id)
/* Write out the object header */ /* Write out the object header */
hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj)); hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
git_filebuf_write(&idx->pack_file, hdr, hdr_len); if ((error = append_to_pack(idx, hdr, hdr_len)) < 0)
goto cleanup;
idx->pack->mwf.size += hdr_len; idx->pack->mwf.size += hdr_len;
entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len); entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
@ -665,13 +683,16 @@ static int inject_object(git_indexer *idx, git_oid *id)
goto cleanup; goto cleanup;
/* And then the compressed object */ /* And then the compressed object */
git_filebuf_write(&idx->pack_file, buf.ptr, buf.size); if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0)
goto cleanup;
idx->pack->mwf.size += buf.size; idx->pack->mwf.size += buf.size;
entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size)); entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
git_buf_free(&buf); git_buf_free(&buf);
/* Write a fake trailer so the pack functions play ball */ /* Write a fake trailer so the pack functions play ball */
if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0)
goto cleanup; goto cleanup;
idx->pack->mwf.size += GIT_OID_RAWSZ; idx->pack->mwf.size += GIT_OID_RAWSZ;
@ -703,7 +724,7 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
size_t size; size_t size;
git_otype type; git_otype type;
git_mwindow *w = NULL; git_mwindow *w = NULL;
git_off_t curpos; git_off_t curpos = 0;
unsigned char *base_info; unsigned char *base_info;
unsigned int left = 0; unsigned int left = 0;
git_oid base; git_oid base;
@ -817,19 +838,12 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
ctx = &idx->trailer; ctx = &idx->trailer;
git_hash_ctx_init(ctx); git_hash_ctx_init(ctx);
git_mwindow_free_all(mwf);
/* Update the header to include the numer of local objects we injected */ /* Update the header to include the numer of local objects we injected */
idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects); idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) { if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
return -1; return -1;
}
if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
giterr_set(GITERR_OS, "failed to update the pack header");
return -1;
}
/* /*
* We now use the same technique as before to determine the * We now use the same technique as before to determine the
@ -837,6 +851,7 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
* hash_partially() keep the existing trailer out of the * hash_partially() keep the existing trailer out of the
* calculation. * calculation.
*/ */
git_mwindow_free_all(mwf);
idx->inbuf_len = 0; idx->inbuf_len = 0;
while (hashed < mwf->size) { while (hashed < mwf->size) {
ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left); ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
@ -906,13 +921,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
return -1; return -1;
git_hash_final(&trailer_hash, &idx->trailer); git_hash_final(&trailer_hash, &idx->trailer);
if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0) write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
return -1;
if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
giterr_set(GITERR_OS, "failed to update pack trailer");
return -1;
}
} }
git_vector_sort(&idx->objects); git_vector_sort(&idx->objects);
@ -995,14 +1004,18 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
git_mwindow_free_all(&idx->pack->mwf); git_mwindow_free_all(&idx->pack->mwf);
/* We need to close the descriptor here so Windows doesn't choke on commit_at */ /* We need to close the descriptor here so Windows doesn't choke on commit_at */
p_close(idx->pack->mwf.fd); if (p_close(idx->pack->mwf.fd) < 0) {
giterr_set(GITERR_OS, "failed to close packfile");
goto on_error;
}
idx->pack->mwf.fd = -1; idx->pack->mwf.fd = -1;
if (index_path(&filename, idx, ".pack") < 0) if (index_path(&filename, idx, ".pack") < 0)
goto on_error; goto on_error;
/* And don't forget to rename the packfile to its new place. */ /* And don't forget to rename the packfile to its new place. */
if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0) p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
return -1;
git_buf_free(&filename); git_buf_free(&filename);
return 0; return 0;
@ -1022,7 +1035,7 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->objects); git_vector_free_deep(&idx->objects);
if (idx->pack) { if (idx->pack && idx->pack->idx_cache) {
struct git_pack_entry *pentry; struct git_pack_entry *pentry;
kh_foreach_value( kh_foreach_value(
idx->pack->idx_cache, pentry, { git__free(pentry); }); idx->pack->idx_cache, pentry, { git__free(pentry); });
@ -1032,6 +1045,5 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->deltas); git_vector_free_deep(&idx->deltas);
git_packfile_free(idx->pack); git_packfile_free(idx->pack);
git_filebuf_cleanup(&idx->pack_file);
git__free(idx); git__free(idx);
} }

View File

@ -897,6 +897,7 @@ struct fs_iterator_frame {
fs_iterator_frame *next; fs_iterator_frame *next;
git_vector entries; git_vector entries;
size_t index; size_t index;
int is_ignored;
}; };
typedef struct fs_iterator fs_iterator; typedef struct fs_iterator fs_iterator;
@ -1290,16 +1291,28 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
static int workdir_iterator__enter_dir(fs_iterator *fi) static int workdir_iterator__enter_dir(fs_iterator *fi)
{ {
workdir_iterator *wi = (workdir_iterator *)fi;
fs_iterator_frame *ff = fi->stack; fs_iterator_frame *ff = fi->stack;
size_t pos; size_t pos;
git_path_with_stat *entry; git_path_with_stat *entry;
bool found_submodules = false; bool found_submodules = false;
/* only push new ignores if this is not top level directory */ /* check if this directory is ignored */
if (git_ignore__lookup(
&ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len) < 0) {
giterr_clear();
ff->is_ignored = GIT_IGNORE_NOTFOUND;
}
/* if this is not the top level directory... */
if (ff->next != NULL) { if (ff->next != NULL) {
workdir_iterator *wi = (workdir_iterator *)fi;
ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/'); ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
/* inherit ignored from parent if no rule specified */
if (ff->is_ignored <= GIT_IGNORE_NOTFOUND)
ff->is_ignored = ff->next->is_ignored;
/* push new ignores for files in this directory */
(void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]); (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
} }
@ -1342,7 +1355,7 @@ static int workdir_iterator__update_entry(fs_iterator *fi)
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
/* reset is_ignored since we haven't checked yet */ /* reset is_ignored since we haven't checked yet */
wi->is_ignored = -1; wi->is_ignored = GIT_IGNORE_UNCHECKED;
return 0; return 0;
} }
@ -1487,6 +1500,19 @@ int git_iterator_current_parent_tree(
return 0; return 0;
} }
static void workdir_iterator_update_is_ignored(workdir_iterator *wi)
{
if (git_ignore__lookup(
&wi->is_ignored, &wi->ignores, wi->fi.entry.path) < 0) {
giterr_clear();
wi->is_ignored = GIT_IGNORE_NOTFOUND;
}
/* use ignore from containing frame stack */
if (wi->is_ignored <= GIT_IGNORE_NOTFOUND)
wi->is_ignored = wi->fi.stack->is_ignored;
}
bool git_iterator_current_is_ignored(git_iterator *iter) bool git_iterator_current_is_ignored(git_iterator *iter)
{ {
workdir_iterator *wi = (workdir_iterator *)iter; workdir_iterator *wi = (workdir_iterator *)iter;
@ -1494,14 +1520,22 @@ bool git_iterator_current_is_ignored(git_iterator *iter)
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
return false; return false;
if (wi->is_ignored != -1) if (wi->is_ignored != GIT_IGNORE_UNCHECKED)
return (bool)(wi->is_ignored != 0); return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
if (git_ignore__lookup( workdir_iterator_update_is_ignored(wi);
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
wi->is_ignored = true;
return (bool)wi->is_ignored; return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
}
bool git_iterator_current_tree_is_ignored(git_iterator *iter)
{
workdir_iterator *wi = (workdir_iterator *)iter;
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
return false;
return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE);
} }
int git_iterator_cmp(git_iterator *iter, const char *path_prefix) int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
@ -1549,10 +1583,8 @@ int git_iterator_advance_over_with_status(
return error; return error;
if (!S_ISDIR(entry->mode)) { if (!S_ISDIR(entry->mode)) {
if (git_ignore__lookup( workdir_iterator_update_is_ignored(wi);
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0) if (wi->is_ignored == GIT_IGNORE_TRUE)
wi->is_ignored = true;
if (wi->is_ignored)
*status = GIT_ITERATOR_STATUS_IGNORED; *status = GIT_ITERATOR_STATUS_IGNORED;
return git_iterator_advance(entryptr, iter); return git_iterator_advance(entryptr, iter);
} }
@ -1564,14 +1596,12 @@ int git_iterator_advance_over_with_status(
/* scan inside directory looking for a non-ignored item */ /* scan inside directory looking for a non-ignored item */
while (entry && !iter->prefixcomp(entry->path, base)) { while (entry && !iter->prefixcomp(entry->path, base)) {
if (git_ignore__lookup( workdir_iterator_update_is_ignored(wi);
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
wi->is_ignored = true;
/* if we found an explicitly ignored item, then update from /* if we found an explicitly ignored item, then update from
* EMPTY to IGNORED * EMPTY to IGNORED
*/ */
if (wi->is_ignored) if (wi->is_ignored == GIT_IGNORE_TRUE)
*status = GIT_ITERATOR_STATUS_IGNORED; *status = GIT_ITERATOR_STATUS_IGNORED;
else if (S_ISDIR(entry->mode)) { else if (S_ISDIR(entry->mode)) {
error = git_iterator_advance_into(&entry, iter); error = git_iterator_advance_into(&entry, iter);
@ -1580,7 +1610,7 @@ int git_iterator_advance_over_with_status(
continue; continue;
else if (error == GIT_ENOTFOUND) { else if (error == GIT_ENOTFOUND) {
error = 0; error = 0;
wi->is_ignored = true; /* mark empty directories as ignored */ wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
} else } else
break; /* real error, stop here */ break; /* real error, stop here */
} else { } else {

View File

@ -245,6 +245,8 @@ extern int git_iterator_current_parent_tree(
extern bool git_iterator_current_is_ignored(git_iterator *iter); extern bool git_iterator_current_is_ignored(git_iterator *iter);
extern bool git_iterator_current_tree_is_ignored(git_iterator *iter);
extern int git_iterator_cmp( extern int git_iterator_cmp(
git_iterator *iter, const char *path_prefix); git_iterator *iter, const char *path_prefix);

View File

@ -42,5 +42,6 @@ typedef struct { /* memory mapped buffer */
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset); extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
extern int p_munmap(git_map *map); extern int p_munmap(git_map *map);
extern long git__page_size(void);
#endif /* INCLUDE_map_h__ */ #endif /* INCLUDE_map_h__ */

View File

@ -2564,8 +2564,42 @@ done:
return error; return error;
} }
static int merge_preference(git_merge_preference_t *out, git_repository *repo)
{
git_config *config;
const char *value;
int bool_value, error = 0;
*out = GIT_MERGE_PREFERENCE_NONE;
if ((error = git_repository_config_snapshot(&config, repo)) < 0)
goto done;
if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) {
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
goto done;
}
if (git_config_parse_bool(&bool_value, value) == 0) {
if (!bool_value)
*out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD;
} else {
if (strcasecmp(value, "only") == 0)
*out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY;
}
done:
git_config_free(config);
return error;
}
int git_merge_analysis( int git_merge_analysis(
git_merge_analysis_t *out, git_merge_analysis_t *analysis_out,
git_merge_preference_t *preference_out,
git_repository *repo, git_repository *repo,
const git_merge_head **their_heads, const git_merge_head **their_heads,
size_t their_heads_len) size_t their_heads_len)
@ -2573,14 +2607,7 @@ int git_merge_analysis(
git_merge_head *ancestor_head = NULL, *our_head = NULL; git_merge_head *ancestor_head = NULL, *our_head = NULL;
int error = 0; int error = 0;
assert(out && repo && their_heads); assert(analysis_out && preference_out && repo && their_heads);
*out = GIT_MERGE_ANALYSIS_NONE;
if (git_repository_head_unborn(repo)) {
*out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
goto done;
}
if (their_heads_len != 1) { if (their_heads_len != 1) {
giterr_set(GITERR_MERGE, "Can only merge a single branch"); giterr_set(GITERR_MERGE, "Can only merge a single branch");
@ -2588,20 +2615,30 @@ int git_merge_analysis(
goto done; goto done;
} }
*analysis_out = GIT_MERGE_ANALYSIS_NONE;
if ((error = merge_preference(preference_out, repo)) < 0)
goto done;
if (git_repository_head_unborn(repo)) {
*analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
goto done;
}
if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0) if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
goto done; goto done;
/* We're up-to-date if we're trying to merge our own common ancestor. */ /* We're up-to-date if we're trying to merge our own common ancestor. */
if (ancestor_head && git_oid_equal(&ancestor_head->oid, &their_heads[0]->oid)) if (ancestor_head && git_oid_equal(&ancestor_head->oid, &their_heads[0]->oid))
*out = GIT_MERGE_ANALYSIS_UP_TO_DATE; *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE;
/* We're fastforwardable if we're our own common ancestor. */ /* We're fastforwardable if we're our own common ancestor. */
else if (ancestor_head && git_oid_equal(&ancestor_head->oid, &our_head->oid)) else if (ancestor_head && git_oid_equal(&ancestor_head->oid, &our_head->oid))
*out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL; *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
/* Otherwise, just a normal merge is possible. */ /* Otherwise, just a normal merge is possible. */
else else
*out = GIT_MERGE_ANALYSIS_NORMAL; *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
done: done:
git_merge_head_free(ancestor_head); git_merge_head_free(ancestor_head);

View File

@ -21,7 +21,7 @@ static size_t line_length_without_trailing_spaces(const char *line, size_t len)
/* Greatly inspired from git.git "stripspace" */ /* Greatly inspired from git.git "stripspace" */
/* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */ /* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */
int git_message_prettify(git_buf *message_out, const char *message, int strip_comments) int git_message_prettify(git_buf *message_out, const char *message, int strip_comments, char comment_char)
{ {
const size_t message_len = strlen(message); const size_t message_len = strlen(message);
@ -40,7 +40,7 @@ int git_message_prettify(git_buf *message_out, const char *message, int strip_co
line_length = message_len - i; line_length = message_len - i;
} }
if (strip_comments && line_length && message[i] == '#') if (strip_comments && line_length && message[i] == comment_char)
continue; continue;
rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length); rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);

View File

@ -206,6 +206,8 @@ static int gitno_ssl_teardown(gitno_ssl *ssl)
return ret; return ret;
} }
#endif
/* Match host names according to RFC 2818 rules */ /* Match host names according to RFC 2818 rules */
int gitno__match_host(const char *pattern, const char *host) int gitno__match_host(const char *pattern, const char *host)
{ {
@ -256,6 +258,8 @@ static int check_host_name(const char *name, const char *host)
return 0; return 0;
} }
#ifdef GIT_SSL
static int verify_server_cert(gitno_ssl *ssl, const char *host) static int verify_server_cert(gitno_ssl *ssl, const char *host)
{ {
X509 *cert; X509 *cert;

View File

@ -783,6 +783,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
return error; return error;
} }
giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL) if ((object = odb_object__alloc(id, &raw)) == NULL)
return -1; return -1;

View File

@ -83,16 +83,12 @@ static void cache_free(git_pack_cache *cache)
} }
git_offmap_free(cache->entries); git_offmap_free(cache->entries);
git_mutex_free(&cache->lock); cache->entries = NULL;
} }
memset(cache, 0, sizeof(*cache));
} }
static int cache_init(git_pack_cache *cache) static int cache_init(git_pack_cache *cache)
{ {
memset(cache, 0, sizeof(*cache));
cache->entries = git_offmap_alloc(); cache->entries = git_offmap_alloc();
GITERR_CHECK_ALLOC(cache->entries); GITERR_CHECK_ALLOC(cache->entries);
@ -514,72 +510,102 @@ int git_packfile_resolve_header(
return error; return error;
} }
static int packfile_unpack_delta( #define SMALL_STACK_SIZE 64
git_rawobj *obj,
struct git_pack_file *p, /**
git_mwindow **w_curs, * Generate the chain of dependencies which we need to get to the
git_off_t *curpos, * object at `off`. `chain` is used a stack, popping gives the right
size_t delta_size, * order to apply deltas on. If an object is found in the pack's base
git_otype delta_type, * cache, we stop calculating there.
git_off_t obj_offset) */
static int pack_dependency_chain(git_dependency_chain *chain_out,
git_pack_cache_entry **cached_out, git_off_t *cached_off,
struct pack_chain_elem *small_stack, size_t *stack_sz,
struct git_pack_file *p, git_off_t obj_offset)
{ {
git_off_t base_offset, base_key; git_dependency_chain chain = GIT_ARRAY_INIT;
git_rawobj base, delta; git_mwindow *w_curs = NULL;
git_pack_cache_entry *cached = NULL; git_off_t curpos = obj_offset, base_offset;
int error, found_base = 0; int error = 0, use_heap = 0;
size_t size, elem_pos;
git_otype type;
base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset); elem_pos = 0;
git_mwindow_close(w_curs); while (true) {
if (base_offset == 0) struct pack_chain_elem *elem;
return packfile_error("delta offset is zero"); git_pack_cache_entry *cached = NULL;
if (base_offset < 0) /* must actually be an error code */
return (int)base_offset;
if (!p->bases.entries && (cache_init(&p->bases) < 0)) /* if we have a base cached, we can stop here instead */
return -1; if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
*cached_out = cached;
*cached_off = obj_offset;
break;
}
base_key = base_offset; /* git_packfile_unpack modifies base_offset */ /* if we run out of space on the small stack, use the array */
if ((cached = cache_get(&p->bases, base_offset)) != NULL) { if (elem_pos == SMALL_STACK_SIZE) {
memcpy(&base, &cached->raw, sizeof(git_rawobj)); git_array_init_to_size(chain, elem_pos);
found_base = 1; GITERR_CHECK_ARRAY(chain);
} memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
chain.size = elem_pos;
use_heap = 1;
}
if (!cached) { /* have to inflate it */ curpos = obj_offset;
error = git_packfile_unpack(&base, p, &base_offset); if (!use_heap) {
elem = &small_stack[elem_pos];
} else {
elem = git_array_alloc(chain);
if (!elem) {
error = -1;
goto on_error;
}
}
elem->base_key = obj_offset;
error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
git_mwindow_close(&w_curs);
/*
* TODO: git.git tries to load the base from other packfiles
* or loose objects.
*
* We'll need to do this in order to support thin packs.
*/
if (error < 0) if (error < 0)
return error; goto on_error;
elem->offset = curpos;
elem->size = size;
elem->type = type;
elem->base_key = obj_offset;
if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
break;
base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
git_mwindow_close(&w_curs);
if (base_offset == 0) {
error = packfile_error("delta offset is zero");
goto on_error;
}
if (base_offset < 0) { /* must actually be an error code */
error = (int)base_offset;
goto on_error;
}
/* we need to pass the pos *after* the delta-base bit */
elem->offset = curpos;
/* go through the loop again, but with the new object */
obj_offset = base_offset;
elem_pos++;
} }
error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
git_mwindow_close(w_curs); *stack_sz = elem_pos + 1;
*chain_out = chain;
if (error < 0) { return error;
if (!found_base)
git__free(base.data);
return error;
}
obj->type = base.type;
error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
if (error < 0)
goto on_error;
if (found_base)
git_atomic_dec(&cached->refcount);
else if (cache_add(&p->bases, &base, base_key) < 0)
git__free(base.data);
on_error: on_error:
git__free(delta.data); git_array_clear(chain);
return error;
return error; /* error set by git__delta_apply */
} }
int git_packfile_unpack( int git_packfile_unpack(
@ -589,48 +615,138 @@ int git_packfile_unpack(
{ {
git_mwindow *w_curs = NULL; git_mwindow *w_curs = NULL;
git_off_t curpos = *obj_offset; git_off_t curpos = *obj_offset;
int error; int error, free_base = 0;
git_dependency_chain chain = GIT_ARRAY_INIT;
size_t size = 0; struct pack_chain_elem *elem = NULL, *stack;
git_otype type; git_pack_cache_entry *cached = NULL;
struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
size_t stack_size, elem_pos;
git_otype base_type;
/* /*
* TODO: optionally check the CRC on the packfile * TODO: optionally check the CRC on the packfile
*/ */
error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset);
if (error < 0)
return error;
obj->data = NULL; obj->data = NULL;
obj->len = 0; obj->len = 0;
obj->type = GIT_OBJ_BAD; obj->type = GIT_OBJ_BAD;
error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); /* let's point to the right stack */
git_mwindow_close(&w_curs); stack = chain.ptr ? chain.ptr : small_stack;
elem_pos = stack_size;
if (cached) {
memcpy(obj, &cached->raw, sizeof(git_rawobj));
base_type = obj->type;
elem_pos--; /* stack_size includes the base, which isn't actually there */
} else {
elem = &stack[--elem_pos];
base_type = elem->type;
}
if (error < 0) if (error < 0)
return error; goto cleanup;
switch (type) {
case GIT_OBJ_OFS_DELTA:
case GIT_OBJ_REF_DELTA:
error = packfile_unpack_delta(
obj, p, &w_curs, &curpos,
size, type, *obj_offset);
break;
switch (base_type) {
case GIT_OBJ_COMMIT: case GIT_OBJ_COMMIT:
case GIT_OBJ_TREE: case GIT_OBJ_TREE:
case GIT_OBJ_BLOB: case GIT_OBJ_BLOB:
case GIT_OBJ_TAG: case GIT_OBJ_TAG:
error = packfile_unpack_compressed( if (!cached) {
obj, p, &w_curs, &curpos, curpos = elem->offset;
size, type); error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type);
git_mwindow_close(&w_curs);
base_type = elem->type;
}
if (error < 0)
goto cleanup;
break; break;
case GIT_OBJ_OFS_DELTA:
case GIT_OBJ_REF_DELTA:
error = packfile_error("dependency chain ends in a delta");
goto cleanup;
default: default:
error = packfile_error("invalid packfile type in header");; error = packfile_error("invalid packfile type in header");
break; goto cleanup;
} }
*obj_offset = curpos; /*
* Finding the object we want a cached base element is
* problematic, as we need to make sure we don't accidentally
* give the caller the cached object, which it would then feel
* free to free, so we need to copy the data.
*/
if (cached && stack_size == 1) {
void *data = obj->data;
obj->data = git__malloc(obj->len + 1);
GITERR_CHECK_ALLOC(obj->data);
memcpy(obj->data, data, obj->len + 1);
git_atomic_dec(&cached->refcount);
goto cleanup;
}
/* we now apply each consecutive delta until we run out */
while (elem_pos > 0 && !error) {
git_rawobj base, delta;
/*
* We can now try to add the base to the cache, as
* long as it's not already the cached one.
*/
if (!cached)
free_base = !!cache_add(&p->bases, obj, elem->base_key);
elem = &stack[elem_pos - 1];
curpos = elem->offset;
error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
git_mwindow_close(&w_curs);
if (error < 0)
break;
/* the current object becomes the new base, on which we apply the delta */
base = *obj;
obj->data = NULL;
obj->len = 0;
obj->type = GIT_OBJ_BAD;
error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
obj->type = base_type;
/*
* We usually don't want to free the base at this
* point, as we put it into the cache in the previous
* iteration. free_base lets us know that we got the
* base object directly from the packfile, so we can free it.
*/
git__free(delta.data);
if (free_base) {
free_base = 0;
git__free(base.data);
}
if (cached) {
git_atomic_dec(&cached->refcount);
cached = NULL;
}
if (error < 0)
break;
elem_pos--;
}
cleanup:
if (error < 0)
git__free(obj->data);
if (elem)
*obj_offset = elem->offset;
git_array_clear(chain);
return error; return error;
} }
@ -660,7 +776,7 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p,
st = inflateInit(&obj->zstream); st = inflateInit(&obj->zstream);
if (st != Z_OK) { if (st != Z_OK) {
git__free(obj); git__free(obj);
giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); giterr_set(GITERR_ZLIB, "failed to init packfile stream");
return -1; return -1;
} }
@ -691,7 +807,7 @@ ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t
written = len - obj->zstream.avail_out; written = len - obj->zstream.avail_out;
if (st != Z_OK && st != Z_STREAM_END) { if (st != Z_OK && st != Z_STREAM_END) {
giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); giterr_set(GITERR_ZLIB, "error reading from the zlib stream");
return -1; return -1;
} }
@ -736,7 +852,7 @@ int packfile_unpack_compressed(
st = inflateInit(&stream); st = inflateInit(&stream);
if (st != Z_OK) { if (st != Z_OK) {
git__free(buffer); git__free(buffer);
giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); giterr_set(GITERR_ZLIB, "failed to init zlib stream on unpack");
return -1; return -1;
} }
@ -763,7 +879,7 @@ int packfile_unpack_compressed(
if ((st != Z_STREAM_END) || stream.total_out != size) { if ((st != Z_STREAM_END) || stream.total_out != size) {
git__free(buffer); git__free(buffer);
giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); giterr_set(GITERR_ZLIB, "error inflating zlib stream");
return -1; return -1;
} }
@ -862,6 +978,7 @@ void git_packfile_free(struct git_pack_file *p)
git__free(p->bad_object_sha1); git__free(p->bad_object_sha1);
git_mutex_free(&p->lock); git_mutex_free(&p->lock);
git_mutex_free(&p->bases.lock);
git__free(p); git__free(p);
} }
@ -997,6 +1114,11 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
return -1; return -1;
} }
if (cache_init(&p->bases) < 0) {
git__free(p);
return -1;
}
*pack_out = p; *pack_out = p;
return 0; return 0;

View File

@ -17,6 +17,7 @@
#include "mwindow.h" #include "mwindow.h"
#include "odb.h" #include "odb.h"
#include "oidmap.h" #include "oidmap.h"
#include "array.h"
#define GIT_PACK_FILE_MODE 0444 #define GIT_PACK_FILE_MODE 0444
@ -60,6 +61,15 @@ typedef struct git_pack_cache_entry {
git_rawobj raw; git_rawobj raw;
} git_pack_cache_entry; } git_pack_cache_entry;
struct pack_chain_elem {
git_off_t base_key;
git_off_t offset;
size_t size;
git_otype type;
};
typedef git_array_t(struct pack_chain_elem) git_dependency_chain;
#include "offmap.h" #include "offmap.h"
GIT__USE_OFFMAP; GIT__USE_OFFMAP;

View File

@ -1135,3 +1135,21 @@ int git_path_dirload_with_stat(
return error; return error;
} }
int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
{
int error;
/* If url_or_path begins with file:// treat it as a URL */
if (!git__prefixcmp(url_or_path, "file://")) {
if ((error = git_path_fromurl(local_path_out, url_or_path)) < 0) {
return error;
}
} else { /* We assume url_or_path is already a path */
if ((error = git_buf_sets(local_path_out, url_or_path)) < 0) {
return error;
}
}
return 0;
}

View File

@ -438,4 +438,7 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
extern bool git_path_does_fs_decompose_unicode(const char *root); extern bool git_path_does_fs_decompose_unicode(const char *root);
/* Used for paths to repositories on the filesystem */
extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
#endif #endif

View File

@ -207,6 +207,13 @@ int p_write(git_file fd, const void *buf, size_t cnt)
#include "map.h" #include "map.h"
long git__page_size(void)
{
/* dummy; here we don't need any alignment anyway */
return 4096;
}
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{ {
GIT_MMAP_VALIDATE(out, len, prot, flags); GIT_MMAP_VALIDATE(out, len, prot, flags);

View File

@ -73,6 +73,7 @@ extern int p_rename(const char *from, const char *to);
#define p_rmdir(p) rmdir(p) #define p_rmdir(p) rmdir(p)
#define p_chmod(p,m) chmod(p, m) #define p_chmod(p,m) chmod(p, m)
#define p_access(p,m) access(p,m) #define p_access(p,m) access(p,m)
#define p_ftruncate(fd, sz) ftruncate(fd, sz)
#define p_recv(s,b,l,f) recv(s,b,l,f) #define p_recv(s,b,l,f) recv(s,b,l,f)
#define p_send(s,b,l,f) send(s,b,l,f) #define p_send(s,b,l,f) send(s,b,l,f)
typedef int GIT_SOCKET; typedef int GIT_SOCKET;

View File

@ -458,6 +458,7 @@ typedef struct {
git_pool pool; git_pool pool;
git_vector loose; git_vector loose;
git_sortedcache *cache;
size_t loose_pos; size_t loose_pos;
size_t packed_pos; size_t packed_pos;
} refdb_fs_iter; } refdb_fs_iter;
@ -468,6 +469,7 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
git_vector_free(&iter->loose); git_vector_free(&iter->loose);
git_pool_clear(&iter->pool); git_pool_clear(&iter->pool);
git_sortedcache_free(iter->cache);
git__free(iter); git__free(iter);
} }
@ -539,10 +541,14 @@ static int refdb_fs_backend__iterator_next(
giterr_clear(); giterr_clear();
} }
git_sortedcache_rlock(backend->refcache); if (!iter->cache) {
if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
return error;
}
while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) { error = GIT_ITEROVER;
ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++); while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */ if (!ref) /* stop now if another thread deleted refs and we past end */
break; break;
@ -556,7 +562,6 @@ static int refdb_fs_backend__iterator_next(
break; break;
} }
git_sortedcache_runlock(backend->refcache);
return error; return error;
} }
@ -579,10 +584,14 @@ static int refdb_fs_backend__iterator_next_name(
giterr_clear(); giterr_clear();
} }
git_sortedcache_rlock(backend->refcache); if (!iter->cache) {
if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
return error;
}
while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) { error = GIT_ITEROVER;
ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++); while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
if (!ref) /* stop now if another thread deleted refs and we past end */ if (!ref) /* stop now if another thread deleted refs and we past end */
break; break;
@ -596,7 +605,6 @@ static int refdb_fs_backend__iterator_next_name(
break; break;
} }
git_sortedcache_runlock(backend->refcache);
return error; return error;
} }
@ -927,19 +935,15 @@ static int has_reflog(git_repository *repo, const char *name);
/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */ /* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */
static int should_write_reflog(int *write, git_repository *repo, const char *name) static int should_write_reflog(int *write, git_repository *repo, const char *name)
{ {
git_config *config; int error, logall;
int error, logall, is_bare;
error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
if (error < 0)
return error;
/* Defaults to the opposite of the repo being bare */ /* Defaults to the opposite of the repo being bare */
is_bare = git_repository_is_bare(repo); if (logall == GIT_LOGALLREFUPDATES_UNSET)
logall = !is_bare; logall = !git_repository_is_bare(repo);
if ((error = git_repository_config__weakptr(&config, repo)) < 0)
return error;
error = git_config_get_bool(&logall, config, "core.logallrefupdates");
if (error < 0 && error != GIT_ENOTFOUND)
return error;
if (!logall) { if (!logall) {
*write = 0; *write = 0;

View File

@ -159,8 +159,7 @@ int git_reference_name_to_id(
} }
static int reference_normalize_for_repo( static int reference_normalize_for_repo(
char *out, git_refname_t out,
size_t out_size,
git_repository *repo, git_repository *repo,
const char *name) const char *name)
{ {
@ -171,7 +170,7 @@ static int reference_normalize_for_repo(
precompose) precompose)
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE; flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
return git_reference_normalize_name(out, out_size, name, flags); return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
} }
int git_reference_lookup_resolved( int git_reference_lookup_resolved(
@ -180,7 +179,7 @@ int git_reference_lookup_resolved(
const char *name, const char *name,
int max_nesting) int max_nesting)
{ {
char scan_name[GIT_REFNAME_MAX]; git_refname_t scan_name;
git_ref_t scan_type; git_ref_t scan_type;
int error = 0, nesting; int error = 0, nesting;
git_reference *ref = NULL; git_reference *ref = NULL;
@ -197,8 +196,7 @@ int git_reference_lookup_resolved(
scan_type = GIT_REF_SYMBOLIC; scan_type = GIT_REF_SYMBOLIC;
if ((error = reference_normalize_for_repo( if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
scan_name, sizeof(scan_name), repo, name)) < 0)
return error; return error;
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
@ -354,7 +352,7 @@ static int reference__create(
const git_oid *old_id, const git_oid *old_id,
const char *old_target) const char *old_target)
{ {
char normalized[GIT_REFNAME_MAX]; git_refname_t normalized;
git_refdb *refdb; git_refdb *refdb;
git_reference *ref = NULL; git_reference *ref = NULL;
int error = 0; int error = 0;
@ -365,7 +363,7 @@ static int reference__create(
if (ref_out) if (ref_out)
*ref_out = NULL; *ref_out = NULL;
error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name); error = reference_normalize_for_repo(normalized, repo, name);
if (error < 0) if (error < 0)
return error; return error;
@ -388,15 +386,14 @@ static int reference__create(
return -1; return -1;
} }
ref = git_reference__alloc(name, oid, NULL); ref = git_reference__alloc(normalized, oid, NULL);
} else { } else {
char normalized_target[GIT_REFNAME_MAX]; git_refname_t normalized_target;
if ((error = git_reference__normalize_name_lax( if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
normalized_target, sizeof(normalized_target), symbolic)) < 0)
return error; return error;
ref = git_reference__alloc_symbolic(name, normalized_target); ref = git_reference__alloc_symbolic(normalized, normalized_target);
} }
GITERR_CHECK_ALLOC(ref); GITERR_CHECK_ALLOC(ref);
@ -569,18 +566,14 @@ int git_reference_symbolic_set_target(
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force, static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
const git_signature *signature, const char *message) const git_signature *signature, const char *message)
{ {
unsigned int normalization_flags; git_refname_t normalized;
char normalized[GIT_REFNAME_MAX];
bool should_head_be_updated = false; bool should_head_be_updated = false;
int error = 0; int error = 0;
assert(ref && new_name && signature); assert(ref && new_name && signature);
normalization_flags = ref->type == GIT_REF_SYMBOLIC ? if ((error = reference_normalize_for_repo(
GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL; normalized, git_reference_owner(ref), new_name)) < 0)
if ((error = git_reference_normalize_name(
normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
return error; return error;
@ -590,12 +583,12 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
should_head_be_updated = (error > 0); should_head_be_updated = (error > 0);
if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, signature, message)) < 0) if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
return error; return error;
/* Update HEAD it was pointing to the reference being renamed */ /* Update HEAD it was pointing to the reference being renamed */
if (should_head_be_updated && if (should_head_be_updated &&
(error = git_repository_set_head(ref->db->repo, new_name, signature, message)) < 0) { (error = git_repository_set_head(ref->db->repo, normalized, signature, message)) < 0) {
giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
return error; return error;
} }
@ -1018,17 +1011,6 @@ cleanup:
return error; return error;
} }
int git_reference__normalize_name_lax(
char *buffer_out,
size_t out_size,
const char *name)
{
return git_reference_normalize_name(
buffer_out,
out_size,
name,
GIT_REF_FORMAT_ALLOW_ONELEVEL);
}
#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC) #define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
int git_reference_cmp( int git_reference_cmp(

View File

@ -51,6 +51,8 @@
#define GIT_REFNAME_MAX 1024 #define GIT_REFNAME_MAX 1024
typedef char git_refname_t[GIT_REFNAME_MAX];
struct git_reference { struct git_reference {
git_refdb *db; git_refdb *db;
git_ref_t type; git_ref_t type;
@ -66,7 +68,6 @@ struct git_reference {
git_reference *git_reference__set_name(git_reference *ref, const char *name); git_reference *git_reference__set_name(git_reference *ref, const char *name);
int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags); int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *signature, const char *log_message); int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *signature, const char *log_message);
int git_reference__is_valid_name(const char *refname, unsigned int flags); int git_reference__is_valid_name(const char *refname, unsigned int flags);

View File

@ -403,6 +403,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if (!optional_setting_found) { if (!optional_setting_found) {
error = GIT_ENOTFOUND; error = GIT_ENOTFOUND;
giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
goto cleanup; goto cleanup;
} }
@ -1304,13 +1305,14 @@ static int rename_remote_config_section(
if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
goto cleanup; goto cleanup;
if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0) if (new_name &&
goto cleanup; (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0))
goto cleanup;
error = git_config_rename_section( error = git_config_rename_section(
repo, repo,
git_buf_cstr(&old_section_name), git_buf_cstr(&old_section_name),
git_buf_cstr(&new_section_name)); new_name ? git_buf_cstr(&new_section_name) : NULL);
cleanup: cleanup:
git_buf_free(&old_section_name); git_buf_free(&old_section_name);
@ -1743,3 +1745,217 @@ int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT); opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
return 0; return 0;
} }
/* asserts a branch.<foo>.remote format */
static const char *name_offset(size_t *len_out, const char *name)
{
size_t prefix_len;
const char *dot;
prefix_len = strlen("remote.");
dot = strchr(name + prefix_len, '.');
assert(dot);
*len_out = dot - name - prefix_len;
return name + prefix_len;
}
static int remove_branch_config_related_entries(
git_repository *repo,
const char *remote_name)
{
int error;
git_config *config;
git_config_entry *entry;
git_config_iterator *iter;
git_buf buf = GIT_BUF_INIT;
if ((error = git_repository_config__weakptr(&config, repo)) < 0)
return error;
if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
return error;
/* find any branches with us as upstream and remove that config */
while ((error = git_config_next(&entry, iter)) == 0) {
const char *branch;
size_t branch_len;
if (strcmp(remote_name, entry->value))
continue;
branch = name_offset(&branch_len, entry->name);
git_buf_clear(&buf);
if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
break;
if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
break;
git_buf_clear(&buf);
if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
break;
if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
break;
}
if (error == GIT_ITEROVER)
error = 0;
git_buf_free(&buf);
git_config_iterator_free(iter);
return error;
}
static int remove_refs(git_repository *repo, const git_refspec *spec)
{
git_reference_iterator *iter = NULL;
git_vector refs;
const char *name;
char *dup;
int error;
size_t i;
if ((error = git_vector_init(&refs, 8, NULL)) < 0)
return error;
if ((error = git_reference_iterator_new(&iter, repo)) < 0)
goto cleanup;
while ((error = git_reference_next_name(&name, iter)) == 0) {
if (!git_refspec_dst_matches(spec, name))
continue;
dup = git__strdup(name);
if (!dup) {
error = -1;
goto cleanup;
}
if ((error = git_vector_insert(&refs, dup)) < 0)
goto cleanup;
}
if (error == GIT_ITEROVER)
error = 0;
if (error < 0)
goto cleanup;
git_vector_foreach(&refs, i, name) {
if ((error = git_reference_remove(repo, name)) < 0)
break;
}
cleanup:
git_reference_iterator_free(iter);
git_vector_foreach(&refs, i, dup) {
git__free(dup);
}
git_vector_free(&refs);
return error;
}
static int remove_remote_tracking(git_repository *repo, const char *remote_name)
{
git_remote *remote;
int error;
size_t i, count;
/* we want to use what's on the config, regardless of changes to the instance in memory */
if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
return error;
count = git_remote_refspec_count(remote);
for (i = 0; i < count; i++) {
const git_refspec *refspec = git_remote_get_refspec(remote, i);
/* shouldn't ever actually happen */
if (refspec == NULL)
continue;
if ((error = remove_refs(repo, refspec)) < 0)
break;
}
git_remote_free(remote);
return error;
}
int git_remote_delete(git_remote *remote)
{
int error;
git_repository *repo;
assert(remote);
if (!remote->name) {
giterr_set(GITERR_INVALID, "Can't delete an anonymous remote.");
return -1;
}
repo = git_remote_owner(remote);
if ((error = remove_branch_config_related_entries(repo,
git_remote_name(remote))) < 0)
return error;
if ((error = remove_remote_tracking(repo, git_remote_name(remote))) < 0)
return error;
if ((error = rename_remote_config_section(
repo, git_remote_name(remote), NULL)) < 0)
return error;
git_remote_free(remote);
return 0;
}
int git_remote_default_branch(git_buf *out, git_remote *remote)
{
const git_remote_head **heads;
const git_remote_head *guess = NULL;
const git_oid *head_id;
size_t heads_len, i;
int error;
if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
return error;
if (heads_len == 0)
return GIT_ENOTFOUND;
git_buf_sanitize(out);
/* the first one must be HEAD so if that has the symref info, we're done */
if (heads[0]->symref_target)
return git_buf_puts(out, heads[0]->symref_target);
/*
* If there's no symref information, we have to look over them
* and guess. We return the first match unless the master
* branch is a candidate. Then we return the master branch.
*/
head_id = &heads[0]->oid;
for (i = 1; i < heads_len; i++) {
if (git_oid_cmp(head_id, &heads[i]->oid))
continue;
if (!guess) {
guess = heads[i];
continue;
}
if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
guess = heads[i];
break;
}
}
if (!guess)
return GIT_ENOTFOUND;
return git_buf_puts(out, guess->name);
}

View File

@ -443,7 +443,6 @@ int git_repository_open_ext(
int error; int error;
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
git_repository *repo; git_repository *repo;
git_config *config;
if (repo_ptr) if (repo_ptr)
*repo_ptr = NULL; *repo_ptr = NULL;
@ -458,23 +457,24 @@ int git_repository_open_ext(
repo->path_repository = git_buf_detach(&path); repo->path_repository = git_buf_detach(&path);
GITERR_CHECK_ALLOC(repo->path_repository); GITERR_CHECK_ALLOC(repo->path_repository);
if ((error = git_repository_config_snapshot(&config, repo)) < 0)
return error;
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
repo->is_bare = 1; repo->is_bare = 1;
else if ((error = load_config_data(repo, config)) < 0 || else {
(error = load_workdir(repo, config, &parent)) < 0) git_config *config = NULL;
{
if ((error = git_repository_config_snapshot(&config, repo)) < 0 ||
(error = load_config_data(repo, config)) < 0 ||
(error = load_workdir(repo, config, &parent)) < 0)
git_repository_free(repo);
git_config_free(config); git_config_free(config);
git_repository_free(repo);
return error;
} }
git_config_free(config); if (!error)
*repo_ptr = repo;
git_buf_free(&parent); git_buf_free(&parent);
*repo_ptr = repo;
return 0; return error;
} }
int git_repository_open(git_repository **repo_out, const char *path) int git_repository_open(git_repository **repo_out, const char *path)
@ -1190,6 +1190,7 @@ static int repo_init_structure(
bool external_tpl = bool external_tpl =
((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0); ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
mode_t dmode = pick_dir_mode(opts); mode_t dmode = pick_dir_mode(opts);
bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
/* Hide the ".git" directory */ /* Hide the ".git" directory */
#ifdef GIT_WIN32 #ifdef GIT_WIN32
@ -1230,10 +1231,12 @@ static int repo_init_structure(
default_template = true; default_template = true;
} }
if (tdir) if (tdir) {
error = git_futils_cp_r(tdir, repo_dir, uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE;
GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS | if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
GIT_CPDIR_SIMPLE_TO_MODE, dmode); cpflags |= GIT_CPDIR_CHMOD_DIRS;
error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
}
git_buf_free(&template_buf); git_buf_free(&template_buf);
git_config_free(cfg); git_config_free(cfg);
@ -1254,9 +1257,14 @@ static int repo_init_structure(
* - only create files if no external template was specified * - only create files if no external template was specified
*/ */
for (tpl = repo_template; !error && tpl->path; ++tpl) { for (tpl = repo_template; !error && tpl->path; ++tpl) {
if (!tpl->content) if (!tpl->content) {
uint32_t mkdir_flags = GIT_MKDIR_PATH;
if (chmod)
mkdir_flags |= GIT_MKDIR_CHMOD;
error = git_futils_mkdir( error = git_futils_mkdir(
tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD); tpl->path, repo_dir, dmode, mkdir_flags);
}
else if (!external_tpl) { else if (!external_tpl) {
const char *content = tpl->content; const char *content = tpl->content;

View File

@ -39,6 +39,7 @@ typedef enum {
GIT_CVAR_ABBREV, /* core.abbrev */ GIT_CVAR_ABBREV, /* core.abbrev */
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */ GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_SAFE_CRLF, /* core.safecrlf */ GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
GIT_CVAR_CACHE_MAX GIT_CVAR_CACHE_MAX
} git_cvar_cached; } git_cvar_cached;
@ -92,6 +93,9 @@ typedef enum {
GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE, GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
/* core.safecrlf */ /* core.safecrlf */
GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE, GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE,
/* core.logallrefupdates */
GIT_LOGALLREFUPDATES_UNSET = 2,
GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
} git_cvar_value; } git_cvar_value;
/* internal repository init flags */ /* internal repository init flags */

View File

@ -363,20 +363,22 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
} }
/* write the buffer */ /* write the buffer */
if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0) if ((error = git_odb_open_wstream(
return -1; &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
return error;
git_odb_stream_write(stream, buffer, strlen(buffer)); if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
error = git_odb_stream_finalize_write(oid, stream);
error = git_odb_stream_finalize_write(oid, stream);
git_odb_stream_free(stream); git_odb_stream_free(stream);
if (error < 0) { if (error < 0) {
git_buf_free(&ref_name); git_buf_free(&ref_name);
return -1; return error;
} }
error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL); error = git_reference_create(
&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL);
git_reference_free(new_ref); git_reference_free(new_ref);
git_buf_free(&ref_name); git_buf_free(&ref_name);

View File

@ -40,17 +40,29 @@ typedef struct {
have_refs : 1; have_refs : 1;
} transport_local; } transport_local;
static void free_head(git_remote_head *head)
{
git__free(head->name);
git__free(head->symref_target);
git__free(head);
}
static int add_ref(transport_local *t, const char *name) static int add_ref(transport_local *t, const char *name)
{ {
const char peeled[] = "^{}"; const char peeled[] = "^{}";
git_oid head_oid; git_reference *ref, *resolved;
git_remote_head *head; git_remote_head *head;
git_oid obj_id;
git_object *obj = NULL, *target = NULL; git_object *obj = NULL, *target = NULL;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
int error; int error;
error = git_reference_name_to_id(&head_oid, t->repo, name); if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
return error;
error = git_reference_resolve(&resolved, ref);
if (error < 0) { if (error < 0) {
git_reference_free(ref);
if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) { if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
/* This is actually okay. Empty repos often have a HEAD that /* This is actually okay. Empty repos often have a HEAD that
* points to a nonexistent "refs/heads/master". */ * points to a nonexistent "refs/heads/master". */
@ -60,17 +72,25 @@ static int add_ref(transport_local *t, const char *name)
return error; return error;
} }
git_oid_cpy(&obj_id, git_reference_target(resolved));
git_reference_free(resolved);
head = git__calloc(1, sizeof(git_remote_head)); head = git__calloc(1, sizeof(git_remote_head));
GITERR_CHECK_ALLOC(head); GITERR_CHECK_ALLOC(head);
head->name = git__strdup(name); head->name = git__strdup(name);
GITERR_CHECK_ALLOC(head->name); GITERR_CHECK_ALLOC(head->name);
git_oid_cpy(&head->oid, &head_oid); git_oid_cpy(&head->oid, &obj_id);
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
head->symref_target = git__strdup(git_reference_symbolic_target(ref));
GITERR_CHECK_ALLOC(head->symref_target);
}
git_reference_free(ref);
if ((error = git_vector_insert(&t->refs, head)) < 0) { if ((error = git_vector_insert(&t->refs, head)) < 0) {
git__free(head->name); free_head(head);
git__free(head);
return error; return error;
} }
@ -103,8 +123,7 @@ static int add_ref(transport_local *t, const char *name)
git_oid_cpy(&head->oid, git_object_id(target)); git_oid_cpy(&head->oid, git_object_id(target));
if ((error = git_vector_insert(&t->refs, head)) < 0) { if ((error = git_vector_insert(&t->refs, head)) < 0) {
git__free(head->name); free_head(head);
git__free(head);
} }
} }
@ -156,27 +175,9 @@ on_error:
return -1; return -1;
} }
static int path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
{
int error;
/* If url_or_path begins with file:// treat it as a URL */
if (!git__prefixcmp(url_or_path, "file://")) {
if ((error = git_path_fromurl(local_path_out, url_or_path)) < 0) {
return error;
}
} else { /* We assume url_or_path is already a path */
if ((error = git_buf_sets(local_path_out, url_or_path)) < 0) {
return error;
}
}
return 0;
}
/* /*
* Try to open the url as a git directory. The direction doesn't * Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves. * matter in this case because we're calculating the heads ourselves.
*/ */
static int local_connect( static int local_connect(
git_transport *transport, git_transport *transport,
@ -203,7 +204,7 @@ static int local_connect(
t->flags = flags; t->flags = flags;
/* 'url' may be a url or path; convert to a path */ /* 'url' may be a url or path; convert to a path */
if ((error = path_from_url_or_path(&buf, url)) < 0) { if ((error = git_path_from_url_or_path(&buf, url)) < 0) {
git_buf_free(&buf); git_buf_free(&buf);
return error; return error;
} }
@ -367,7 +368,7 @@ static int local_push(
size_t j; size_t j;
/* 'push->remote->url' may be a url or path; convert to a path */ /* 'push->remote->url' may be a url or path; convert to a path */
if ((error = path_from_url_or_path(&buf, push->remote->url)) < 0) { if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) {
git_buf_free(&buf); git_buf_free(&buf);
return error; return error;
} }
@ -626,10 +627,8 @@ static void local_free(git_transport *transport)
size_t i; size_t i;
git_remote_head *head; git_remote_head *head;
git_vector_foreach(&t->refs, i, head) { git_vector_foreach(&t->refs, i, head)
git__free(head->name); free_head(head);
git__free(head);
}
git_vector_free(&t->refs); git_vector_free(&t->refs);

View File

@ -7,6 +7,7 @@
#include "git2.h" #include "git2.h"
#include "smart.h" #include "smart.h"
#include "refs.h" #include "refs.h"
#include "refspec.h"
static int git_smart__recv_cb(gitno_buffer *buf) static int git_smart__recv_cb(gitno_buffer *buf)
{ {
@ -63,7 +64,7 @@ static int git_smart__set_callbacks(
return 0; return 0;
} }
int git_smart__update_heads(transport_smart *t) int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
{ {
size_t i; size_t i;
git_pkt *pkt; git_pkt *pkt;
@ -74,6 +75,25 @@ int git_smart__update_heads(transport_smart *t)
if (pkt->type != GIT_PKT_REF) if (pkt->type != GIT_PKT_REF)
continue; continue;
if (symrefs) {
git_refspec *spec;
git_buf buf = GIT_BUF_INIT;
size_t j;
int error = 0;
git_vector_foreach(symrefs, j, spec) {
git_buf_clear(&buf);
if (git_refspec_src_matches(spec, ref->head.name) &&
!(error = git_refspec_transform(&buf, spec, ref->head.name)))
ref->head.symref_target = git_buf_detach(&buf);
}
git_buf_free(&buf);
if (error < 0)
return error;
}
if (git_vector_insert(&t->heads, &ref->head) < 0) if (git_vector_insert(&t->heads, &ref->head) < 0)
return -1; return -1;
} }
@ -81,6 +101,19 @@ int git_smart__update_heads(transport_smart *t)
return 0; return 0;
} }
static void free_symrefs(git_vector *symrefs)
{
git_refspec *spec;
size_t i;
git_vector_foreach(symrefs, i, spec) {
git_refspec__free(spec);
git__free(spec);
}
git_vector_free(symrefs);
}
static int git_smart__connect( static int git_smart__connect(
git_transport *transport, git_transport *transport,
const char *url, const char *url,
@ -94,6 +127,7 @@ static int git_smart__connect(
int error; int error;
git_pkt *pkt; git_pkt *pkt;
git_pkt_ref *first; git_pkt_ref *first;
git_vector symrefs;
git_smart_service_t service; git_smart_service_t service;
if (git_smart__reset_stream(t, true) < 0) if (git_smart__reset_stream(t, true) < 0)
@ -147,8 +181,11 @@ static int git_smart__connect(
first = (git_pkt_ref *)git_vector_get(&t->refs, 0); first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
return error;
/* Detect capabilities */ /* Detect capabilities */
if (git_smart__detect_caps(first, &t->caps) < 0) if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
return -1; return -1;
/* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */ /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
@ -159,7 +196,9 @@ static int git_smart__connect(
} }
/* Keep a list of heads for _ls */ /* Keep a list of heads for _ls */
git_smart__update_heads(t); git_smart__update_heads(t, &symrefs);
free_symrefs(&symrefs);
if (t->rpc && git_smart__reset_stream(t, false) < 0) if (t->rpc && git_smart__reset_stream(t, false) < 0)
return -1; return -1;
@ -272,6 +311,18 @@ static int git_smart__close(git_transport *transport)
unsigned int i; unsigned int i;
git_pkt *p; git_pkt *p;
int ret; int ret;
git_smart_subtransport_stream *stream;
const char flush[] = "0000";
/*
* If we're still connected at this point and not using RPC,
* we should say goodbye by sending a flush, or git-daemon
* will complain that we disconnected unexpectedly.
*/
if (t->connected && !t->rpc &&
!t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) {
t->current_stream->write(t->current_stream, flush, 4);
}
ret = git_smart__reset_stream(t, true); ret = git_smart__reset_stream(t, true);

View File

@ -23,6 +23,7 @@
#define GIT_CAP_DELETE_REFS "delete-refs" #define GIT_CAP_DELETE_REFS "delete-refs"
#define GIT_CAP_REPORT_STATUS "report-status" #define GIT_CAP_REPORT_STATUS "report-status"
#define GIT_CAP_THIN_PACK "thin-pack" #define GIT_CAP_THIN_PACK "thin-pack"
#define GIT_CAP_SYMREF "symref"
enum git_pkt_type { enum git_pkt_type {
GIT_PKT_CMD, GIT_PKT_CMD,
@ -154,7 +155,7 @@ typedef struct {
/* smart_protocol.c */ /* smart_protocol.c */
int git_smart__store_refs(transport_smart *t, int flushes); int git_smart__store_refs(transport_smart *t, int flushes);
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps); int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
int git_smart__push(git_transport *transport, git_push *push); int git_smart__push(git_transport *transport, git_push *push);
int git_smart__negotiate_fetch( int git_smart__negotiate_fetch(
@ -174,7 +175,7 @@ int git_smart__download_pack(
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len); int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out); int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
int git_smart__update_heads(transport_smart *t); int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
/* smart_pkt.c */ /* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);

View File

@ -433,6 +433,7 @@ void git_pkt_free(git_pkt *pkt)
if (pkt->type == GIT_PKT_REF) { if (pkt->type == GIT_PKT_REF) {
git_pkt_ref *p = (git_pkt_ref *) pkt; git_pkt_ref *p = (git_pkt_ref *) pkt;
git__free(p->head.name); git__free(p->head.name);
git__free(p->head.symref_target);
} }
if (pkt->type == GIT_PKT_OK) { if (pkt->type == GIT_PKT_OK) {

View File

@ -26,17 +26,16 @@ int git_smart__store_refs(transport_smart *t, int flushes)
int error, flush = 0, recvd; int error, flush = 0, recvd;
const char *line_end = NULL; const char *line_end = NULL;
git_pkt *pkt = NULL; git_pkt *pkt = NULL;
git_pkt_ref *ref;
size_t i; size_t i;
/* Clear existing refs in case git_remote_connect() is called again /* Clear existing refs in case git_remote_connect() is called again
* after git_remote_disconnect(). * after git_remote_disconnect().
*/ */
git_vector_foreach(refs, i, ref) { git_vector_foreach(refs, i, pkt) {
git__free(ref->head.name); git_pkt_free(pkt);
git__free(ref);
} }
git_vector_clear(refs); git_vector_clear(refs);
pkt = NULL;
do { do {
if (buf->offset > 0) if (buf->offset > 0)
@ -78,7 +77,52 @@ int git_smart__store_refs(transport_smart *t, int flushes)
return flush; return flush;
} }
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps) static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
{
int error;
const char *end;
git_buf buf = GIT_BUF_INIT;
git_refspec *mapping;
ptr += strlen(GIT_CAP_SYMREF);
if (*ptr != '=')
goto on_invalid;
ptr++;
if (!(end = strchr(ptr, ' ')) &&
!(end = strchr(ptr, '\0')))
goto on_invalid;
if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
return error;
/* symref mapping has refspec format */
mapping = git__malloc(sizeof(git_refspec));
GITERR_CHECK_ALLOC(mapping);
error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
git_buf_free(&buf);
/* if the error isn't OOM, then it's a parse error; let's use a nicer message */
if (error < 0) {
if (giterr_last()->klass != GITERR_NOMEMORY)
goto on_invalid;
return error;
}
if ((error = git_vector_insert(symrefs, mapping)) < 0)
return error;
*out = end;
return 0;
on_invalid:
giterr_set(GITERR_NET, "remote sent invalid symref");
return -1;
}
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
{ {
const char *ptr; const char *ptr;
@ -141,6 +185,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
continue; continue;
} }
if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
int error;
if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
return error;
continue;
}
/* We don't know this capability, so skip it */ /* We don't know this capability, so skip it */
ptr = strchr(ptr, ' '); ptr = strchr(ptr, ' ');
} }
@ -969,7 +1022,7 @@ int git_smart__push(git_transport *transport, git_push *push)
if (error < 0) if (error < 0)
goto done; goto done;
error = git_smart__update_heads(t); error = git_smart__update_heads(t, NULL);
} }
done: done:

View File

@ -10,8 +10,14 @@
#include "map.h" #include "map.h"
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h>
#include <errno.h> #include <errno.h>
long git__page_size(void)
{
return sysconf(_SC_PAGE_SIZE);
}
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{ {
int mprot = 0; int mprot = 0;

View File

@ -133,6 +133,13 @@ GIT_INLINE(int) git__is_uint32(size_t p)
return p == (size_t)r; return p == (size_t)r;
} }
/** @return true if p fits into the range of an unsigned long */
GIT_INLINE(int) git__is_ulong(git_off_t p)
{
unsigned long r = (unsigned long)p;
return p == (git_off_t)r;
}
/* 32-bit cross-platform rotl */ /* 32-bit cross-platform rotl */
#ifdef _MSC_VER /* use built-in method in MSVC */ #ifdef _MSC_VER /* use built-in method in MSVC */
# define git__rotl(v, s) (uint32_t)_rotl(v, s) # define git__rotl(v, s) (uint32_t)_rotl(v, s)

View File

@ -23,6 +23,11 @@ static DWORD get_page_size(void)
return page_size; return page_size;
} }
long git__page_size(void)
{
return (long)get_page_size();
}
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{ {
HANDLE fh = (HANDLE)_get_osfhandle(fd); HANDLE fh = (HANDLE)_get_osfhandle(fd);

View File

@ -10,6 +10,7 @@
#if defined(__MINGW32__) #if defined(__MINGW32__)
/* use a 64-bit file offset type */ /* use a 64-bit file offset type */
# undef lseek
# define lseek _lseeki64 # define lseek _lseeki64
# undef stat # undef stat
# define stat _stati64 # define stat _stati64

View File

@ -19,6 +19,12 @@
# define EAFNOSUPPORT (INT_MAX-1) # define EAFNOSUPPORT (INT_MAX-1)
#endif #endif
#ifdef _MSC_VER
# define p_ftruncate(fd, sz) _chsize_s(fd, sz)
#else /* MinGW */
# define p_ftruncate(fd, sz) _chsize(fd, sz)
#endif
GIT_INLINE(int) p_link(const char *old, const char *new) GIT_INLINE(int) p_link(const char *old, const char *new)
{ {
GIT_UNUSED(old); GIT_UNUSED(old);

View File

@ -19,6 +19,15 @@
# define FILE_NAME_NORMALIZED 0 # define FILE_NAME_NORMALIZED 0
#endif #endif
/* Options which we always provide to _wopen.
*
* _O_BINARY - Raw access; no translation of CR or LF characters
* _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
* The Windows default is 'not inheritable', but the CRT's default (following
* POSIX convention) is 'inheritable'. We have no desire for our handles to be
* inheritable on Windows, so specify the flag to get default behavior back. */
#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
/* GetFinalPathNameByHandleW signature */ /* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD); typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
@ -317,7 +326,7 @@ int p_open(const char *path, int flags, ...)
va_end(arg_list); va_end(arg_list);
} }
return _wopen(buf, flags | _O_BINARY, mode); return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode);
} }
int p_creat(const char *path, mode_t mode) int p_creat(const char *path, mode_t mode)
@ -327,7 +336,7 @@ int p_creat(const char *path, mode_t mode)
if (utf8_to_16_with_errno(buf, path) < 0) if (utf8_to_16_with_errno(buf, path) < 0)
return -1; return -1;
return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, mode);
} }
int p_getcwd(char *buffer_out, size_t size) int p_getcwd(char *buffer_out, size_t size)
@ -569,7 +578,7 @@ int p_mkstemp(char *tmp_path)
return -1; return -1;
#endif #endif
return p_creat(tmp_path, 0744); //-V536 return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
} }
int p_access(const char* path, mode_t mode) int p_access(const char* path, mode_t mode)

View File

@ -518,3 +518,16 @@ void cl_fake_home(void)
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_buf_free(&path); git_buf_free(&path);
} }
void cl_sandbox_set_search_path_defaults(void)
{
const char *sandbox_path = clar_sandbox_path();
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, sandbox_path);
}

View File

@ -139,4 +139,6 @@ void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value
void cl_fake_home(void); void cl_fake_home(void);
void cl_fake_home_cleanup(void *); void cl_fake_home_cleanup(void *);
void cl_sandbox_set_search_path_defaults(void);
#endif #endif

105
tests/clone/local.c Normal file
View File

@ -0,0 +1,105 @@
#include "clar_libgit2.h"
#include "git2/clone.h"
#include "clone.h"
#include "buffer.h"
#include "path.h"
#include "posix.h"
#include "fileops.h"
void test_clone_local__should_clone_local(void)
{
git_buf buf = GIT_BUF_INIT;
const char *path;
/* we use a fixture path because it needs to exist for us to want to clone */
cl_git_pass(git_buf_printf(&buf, "file://%s", cl_fixture("testrepo.git")));
cl_assert_equal_i(false, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(true, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
cl_assert_equal_i(true, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(false, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
git_buf_free(&buf);
path = cl_fixture("testrepo.git");
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_AUTO));
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL));
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS));
cl_assert_equal_i(false, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL));
}
void test_clone_local__hardlinks(void)
{
git_repository *repo;
git_remote *remote;
git_signature *sig;
git_buf buf = GIT_BUF_INIT;
struct stat st;
/*
* In this first clone, we just copy over, since the temp dir
* will often be in a different filesystem, so we cannot
* link. It also allows us to control the number of links
*/
cl_git_pass(git_repository_init(&repo, "./clone.git", true));
cl_git_pass(git_remote_create(&remote, repo, "origin", cl_fixture("testrepo.git")));
cl_git_pass(git_signature_now(&sig, "foo", "bar"));
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, false, sig));
git_remote_free(remote);
git_repository_free(repo);
/* This second clone is in the same filesystem, so we can hardlink */
cl_git_pass(git_repository_init(&repo, "./clone2.git", true));
cl_git_pass(git_buf_puts(&buf, cl_git_path_url("clone.git")));
cl_git_pass(git_remote_create(&remote, repo, "origin", buf.ptr));
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, true, sig));
#ifndef GIT_WIN32
git_buf_clear(&buf);
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
cl_git_pass(p_stat(buf.ptr, &st));
cl_assert_equal_i(2, st.st_nlink);
#endif
git_remote_free(remote);
git_repository_free(repo);
git_buf_clear(&buf);
cl_git_pass(git_repository_init(&repo, "./clone3.git", true));
cl_git_pass(git_buf_puts(&buf, cl_git_path_url("clone.git")));
cl_git_pass(git_remote_create(&remote, repo, "origin", buf.ptr));
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, false, sig));
git_buf_clear(&buf);
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
cl_git_pass(p_stat(buf.ptr, &st));
cl_assert_equal_i(1, st.st_nlink);
git_remote_free(remote);
git_repository_free(repo);
/* this one should automatically use links */
cl_git_pass(git_clone(&repo, "./clone.git", "./clone4.git", NULL));
#ifndef GIT_WIN32
git_buf_clear(&buf);
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
cl_git_pass(p_stat(buf.ptr, &st));
cl_assert_equal_i(3, st.st_nlink);
#endif
git_buf_free(&buf);
git_signature_free(sig);
git_repository_free(repo);
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("./clone2.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("./clone3.git", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("./clone4.git", NULL, GIT_RMDIR_REMOVE_FILES));
}

View File

@ -35,3 +35,31 @@ void assert_config_entry_value(
cl_assert_equal_s(expected_value, out); cl_assert_equal_s(expected_value, out);
} }
static int count_config_entries_cb(
const git_config_entry *entry,
void *payload)
{
int *how_many = (int *)payload;
GIT_UNUSED(entry);
(*how_many)++;
return 0;
}
int count_config_entries_match(git_repository *repo, const char *pattern)
{
git_config *config;
int how_many = 0;
cl_git_pass(git_repository_config(&config, repo));
cl_assert_equal_i(0, git_config_foreach_match(
config, pattern, count_config_entries_cb, &how_many));
git_config_free(config);
return how_many;
}

View File

@ -7,3 +7,7 @@ extern void assert_config_entry_value(
git_repository *repo, git_repository *repo,
const char *name, const char *name,
const char *expected_value); const char *expected_value);
extern int count_config_entries_match(
git_repository *repo,
const char *pattern);

View File

@ -2,25 +2,10 @@
#include "buffer.h" #include "buffer.h"
#include "fileops.h" #include "fileops.h"
static git_config_level_t setting[3] = {
GIT_CONFIG_LEVEL_GLOBAL,
GIT_CONFIG_LEVEL_XDG,
GIT_CONFIG_LEVEL_SYSTEM
};
static char *restore[3];
void test_config_global__initialize(void) void test_config_global__initialize(void)
{ {
int i;
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
/* snapshot old settings to restore later */
for (i = 0; i < 3; ++i) {
cl_git_pass(
git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, setting[i], &path));
restore[i] = git_buf_detach(&path);
}
cl_git_pass(git_futils_mkdir_r("home", NULL, 0777)); cl_git_pass(git_futils_mkdir_r("home", NULL, 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL)); cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts( cl_git_pass(git_libgit2_opts(
@ -41,18 +26,7 @@ void test_config_global__initialize(void)
void test_config_global__cleanup(void) void test_config_global__cleanup(void)
{ {
int i; cl_sandbox_set_search_path_defaults();
for (i = 0; i < 3; ++i) {
cl_git_pass(
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, setting[i], restore[i]));
git__free(restore[i]);
restore[i] = NULL;
}
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
} }
void test_config_global__open_global(void) void test_config_global__open_global(void)

View File

@ -47,6 +47,8 @@ void test_config_include__homedir(void)
cl_assert_equal_s(str, "huzzah"); cl_assert_equal_s(str, "huzzah");
git_config_free(cfg); git_config_free(cfg);
cl_sandbox_set_search_path_defaults();
} }
void test_config_include__refresh(void) void test_config_include__refresh(void)

View File

@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void)
cl_assert(!git_path_isdir("an_dir")); cl_assert(!git_path_isdir("an_dir"));
} }
void assert_hard_link(const char *path)
{
/* we assert this by checking that there's more than one link to the file */
struct stat st;
cl_assert(git_path_isfile(path));
cl_git_pass(p_stat(path, &st));
cl_assert(st.st_nlink > 1);
}
void test_core_copy__tree(void) void test_core_copy__tree(void)
{ {
struct stat st; struct stat st;
@ -122,5 +132,21 @@ void test_core_copy__tree(void)
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("t2")); cl_assert(!git_path_isdir("t2"));
#ifndef GIT_WIN32
cl_git_pass(git_futils_cp_r("src", "t3", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_LINK_FILES, 0));
cl_assert(git_path_isdir("t3"));
cl_assert(git_path_isdir("t3"));
cl_assert(git_path_isdir("t3/b"));
cl_assert(git_path_isdir("t3/c"));
cl_assert(git_path_isdir("t3/c/d"));
cl_assert(git_path_isdir("t3/c/e"));
assert_hard_link("t3/f1");
assert_hard_link("t3/b/f2");
assert_hard_link("t3/c/f3");
assert_hard_link("t3/c/d/f4");
#endif
cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
} }

View File

@ -40,12 +40,12 @@ void test_core_env__initialize(void)
} }
} }
static void reset_global_search_path(void) static void set_global_search_path_from_env(void)
{ {
cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL)); cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL));
} }
static void reset_system_search_path(void) static void set_system_search_path_from_env(void)
{ {
cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, NULL)); cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, NULL));
} }
@ -69,9 +69,7 @@ void test_core_env__cleanup(void)
(void)p_rmdir(*val); (void)p_rmdir(*val);
} }
/* reset search paths to default */ cl_sandbox_set_search_path_defaults();
reset_global_search_path();
reset_system_search_path();
} }
static void setenv_and_check(const char *name, const char *value) static void setenv_and_check(const char *name, const char *value)
@ -124,12 +122,12 @@ void test_core_env__0(void)
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
setenv_and_check("HOME", path.ptr); setenv_and_check("HOME", path.ptr);
reset_global_search_path(); set_global_search_path_from_env();
cl_git_pass(git_sysdir_find_global_file(&found, testfile)); cl_git_pass(git_sysdir_find_global_file(&found, testfile));
cl_setenv("HOME", env_save[0]); cl_setenv("HOME", env_save[0]);
reset_global_search_path(); set_global_search_path_from_env();
cl_assert_equal_i( cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
@ -138,7 +136,7 @@ void test_core_env__0(void)
setenv_and_check("HOMEDRIVE", NULL); setenv_and_check("HOMEDRIVE", NULL);
setenv_and_check("HOMEPATH", NULL); setenv_and_check("HOMEPATH", NULL);
setenv_and_check("USERPROFILE", path.ptr); setenv_and_check("USERPROFILE", path.ptr);
reset_global_search_path(); set_global_search_path_from_env();
cl_git_pass(git_sysdir_find_global_file(&found, testfile)); cl_git_pass(git_sysdir_find_global_file(&found, testfile));
@ -148,7 +146,7 @@ void test_core_env__0(void)
if (root >= 0) { if (root >= 0) {
setenv_and_check("USERPROFILE", NULL); setenv_and_check("USERPROFILE", NULL);
reset_global_search_path(); set_global_search_path_from_env();
cl_assert_equal_i( cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
@ -158,7 +156,7 @@ void test_core_env__0(void)
setenv_and_check("HOMEDRIVE", path.ptr); setenv_and_check("HOMEDRIVE", path.ptr);
path.ptr[root] = old; path.ptr[root] = old;
setenv_and_check("HOMEPATH", &path.ptr[root]); setenv_and_check("HOMEPATH", &path.ptr[root]);
reset_global_search_path(); set_global_search_path_from_env();
cl_git_pass(git_sysdir_find_global_file(&found, testfile)); cl_git_pass(git_sysdir_find_global_file(&found, testfile));
} }
@ -185,7 +183,7 @@ void test_core_env__1(void)
cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist")); cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist"));
cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist")); cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist"));
#endif #endif
reset_global_search_path(); set_global_search_path_from_env();
cl_assert_equal_i( cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
@ -195,8 +193,8 @@ void test_core_env__1(void)
cl_git_pass(cl_setenv("HOMEPATH", NULL)); cl_git_pass(cl_setenv("HOMEPATH", NULL));
cl_git_pass(cl_setenv("USERPROFILE", NULL)); cl_git_pass(cl_setenv("USERPROFILE", NULL));
#endif #endif
reset_global_search_path(); set_global_search_path_from_env();
reset_system_search_path(); set_system_search_path_from_env();
cl_assert_equal_i( cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
@ -206,7 +204,7 @@ void test_core_env__1(void)
#ifdef GIT_WIN32 #ifdef GIT_WIN32
cl_git_pass(cl_setenv("PROGRAMFILES", NULL)); cl_git_pass(cl_setenv("PROGRAMFILES", NULL));
reset_system_search_path(); set_system_search_path_from_env();
cl_assert_equal_i( cl_assert_equal_i(
GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile")); GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile"));

View File

@ -647,7 +647,7 @@ static void workdir_iterator_test(
void test_diff_iterator__workdir_0(void) void test_diff_iterator__workdir_0(void)
{ {
workdir_iterator_test("attr", NULL, NULL, 27, 1, NULL, "ign"); workdir_iterator_test("attr", NULL, NULL, 23, 5, NULL, "ign");
} }
static const char *status_paths[] = { static const char *status_paths[] = {

View File

@ -1580,3 +1580,117 @@ void test_diff_workdir__can_update_index(void)
git_diff_free(diff); git_diff_free(diff);
} }
#define STR7 "0123456"
#define STR8 "01234567"
#define STR40 STR8 STR8 STR8 STR8 STR8
#define STR200 STR40 STR40 STR40 STR40 STR40
#define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
STR8 STR8 STR8 STR8 STR7 "\0"
#define STR1000 STR200 STR200 STR200 STR200 STR200
#define STR3999Z STR1000 STR1000 STR1000 STR999Z
#define STR4000 STR1000 STR1000 STR1000 STR1000
static void assert_delta_binary(git_diff *diff, size_t idx, int is_binary)
{
git_patch *patch;
const git_diff_delta *delta;
cl_git_pass(git_patch_from_diff(&patch, diff, idx));
delta = git_patch_get_delta(patch);
cl_assert_equal_b((delta->flags & GIT_DIFF_FLAG_BINARY), is_binary);
git_patch_free(patch);
}
void test_diff_workdir__binary_detection(void)
{
git_index *idx;
git_diff *diff = NULL;
git_buf b = GIT_BUF_INIT;
int i;
git_buf data[10] = {
{ "1234567890", 0, 0 }, /* 0 - all ascii text control */
{ "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 0 }, /* 1 - UTF-8 multibyte text */
{ "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 0 }, /* 2 - UTF-8 with BOM */
{ STR999Z, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
{ STR3999Z, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
{ STR4000 STR3999Z "x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
{ STR4000 STR4000 "\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
{ "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
"\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
{ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
0, 26 }, /* 8 - All non-printable characters (no NUL) */
{ "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
"\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
};
g_repo = cl_git_sandbox_init("empty_standard_repo");
cl_git_pass(git_repository_index(&idx, g_repo));
/* We start with ASCII in index and test data in workdir,
* then we will try with test data in index and ASCII in workdir.
*/
cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0"));
for (i = 0; i < 10; ++i) {
b.ptr[b.size - 1] = '0' + i;
cl_git_mkfile(b.ptr, "baseline");
cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
if (data[i].size == 0)
data[i].size = strlen(data[i].ptr);
cl_git_write2file(
b.ptr, data[i].ptr, data[i].size, O_WRONLY|O_TRUNC, 0664);
}
git_index_write(idx);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
cl_assert_equal_i(10, git_diff_num_deltas(diff));
/* using diff binary detection (i.e. looking for NUL byte) */
assert_delta_binary(diff, 0, false);
assert_delta_binary(diff, 1, false);
assert_delta_binary(diff, 2, false);
assert_delta_binary(diff, 3, true);
assert_delta_binary(diff, 4, true);
assert_delta_binary(diff, 5, true);
assert_delta_binary(diff, 6, false);
assert_delta_binary(diff, 7, true);
assert_delta_binary(diff, 8, false);
assert_delta_binary(diff, 9, false);
/* The above have been checked to match command-line Git */
git_diff_free(diff);
cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0"));
for (i = 0; i < 10; ++i) {
b.ptr[b.size - 1] = '0' + i;
cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
cl_git_write2file(b.ptr, "baseline\n", 9, O_WRONLY|O_TRUNC, 0664);
}
git_index_write(idx);
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
cl_assert_equal_i(10, git_diff_num_deltas(diff));
/* using diff binary detection (i.e. looking for NUL byte) */
assert_delta_binary(diff, 0, false);
assert_delta_binary(diff, 1, false);
assert_delta_binary(diff, 2, false);
assert_delta_binary(diff, 3, true);
assert_delta_binary(diff, 4, true);
assert_delta_binary(diff, 5, true);
assert_delta_binary(diff, 6, false);
assert_delta_binary(diff, 7, true);
assert_delta_binary(diff, 8, false);
assert_delta_binary(diff, 9, false);
git_diff_free(diff);
git_index_free(idx);
git_buf_free(&b);
}

View File

@ -103,12 +103,12 @@ void test_filter_crlf__with_safecrlf(void)
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in)); cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER); cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
/* Normalized \n fails with safecrlf */ /* Normalized \n is reversible, so does not fail with safecrlf */
in.ptr = "Normal\nLF\nonly\nline-endings.\n"; in.ptr = "Normal\nLF\nonly\nline-endings.\n";
in.size = strlen(in.ptr); in.size = strlen(in.ptr);
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in)); cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER); cl_assert_equal_s(in.ptr, out.ptr);
git_filter_list_free(fl); git_filter_list_free(fl);
git_buf_free(&out); git_buf_free(&out);
@ -196,3 +196,44 @@ void test_filter_crlf__no_safecrlf(void)
git_buf_free(&out); git_buf_free(&out);
} }
void test_filter_crlf__safecrlf_warn(void)
{
git_filter_list *fl;
git_filter *crlf;
git_buf in = {0}, out = GIT_BUF_INIT;
cl_repo_set_string(g_repo, "core.safecrlf", "warn");
cl_git_pass(git_filter_list_new(
&fl, g_repo, GIT_FILTER_TO_ODB, 0));
crlf = git_filter_lookup(GIT_FILTER_CRLF);
cl_assert(crlf != NULL);
cl_git_pass(git_filter_list_push(fl, crlf, NULL));
/* Normalized \r\n succeeds with safecrlf=warn */
in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
/* Mix of line endings succeeds with safecrlf=warn */
in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
/* TODO: check for warning */
cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
/* Normalized \n is reversible, so does not fail with safecrlf=warn */
in.ptr = "Normal\nLF\nonly\nline-endings.\n";
in.size = strlen(in.ptr);
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
cl_assert_equal_s(in.ptr, out.ptr);
git_filter_list_free(fl);
git_buf_free(&out);
}

View File

@ -134,3 +134,21 @@ void test_index_crlf__autocrlf_input_text_auto_attr(void)
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF)); cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
cl_assert(git_oid_cmp(&oid, &entry->id) == 0); cl_assert(git_oid_cmp(&oid, &entry->id) == 0);
} }
void test_index_crlf__safecrlf_true_no_attrs(void)
{
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_repo_set_bool(g_repo, "core.safecrlf", true);
cl_git_mkfile("crlf/newfile.txt", ALL_LF_TEXT_RAW);
cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
cl_git_mkfile("crlf/newfile.txt", ALL_CRLF_TEXT_RAW);
cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
cl_git_mkfile("crlf/newfile.txt", MORE_CRLF_TEXT_RAW);
cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
cl_git_mkfile("crlf/newfile.txt", MORE_LF_TEXT_RAW);
cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
}

View File

@ -152,3 +152,20 @@ void test_index_filemodes__trusted(void)
git_index_free(index); git_index_free(index);
} }
void test_index_filemodes__invalid(void)
{
git_index *index;
git_index_entry entry;
cl_git_pass(git_repository_index(&index, g_repo));
entry.path = "foo";
entry.mode = GIT_OBJ_BLOB;
cl_git_fail(git_index_add(index, &entry));
entry.mode = GIT_FILEMODE_BLOB;
cl_git_pass(git_index_add(index, &entry));
git_index_free(index);
}

View File

@ -6,16 +6,12 @@ int __cdecl main(int argc, char *argv[])
int main(int argc, char *argv[]) int main(int argc, char *argv[])
#endif #endif
{ {
const char *sandbox_path;
int res; int res;
clar_test_init(argc, argv); clar_test_init(argc, argv);
git_threads_init(); git_threads_init();
cl_sandbox_set_search_path_defaults();
sandbox_path = clar_sandbox_path();
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path);
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path);
/* Run the test suite */ /* Run the test suite */
res = clar_test_run(); res = clar_test_run();

View File

@ -36,72 +36,105 @@ void test_merge_workdir_analysis__cleanup(void)
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
} }
static git_merge_analysis_t analysis_from_branch(const char *branchname) static void analysis_from_branch(
git_merge_analysis_t *merge_analysis,
git_merge_preference_t *merge_pref,
const char *branchname)
{ {
git_buf refname = GIT_BUF_INIT; git_buf refname = GIT_BUF_INIT;
git_reference *their_ref; git_reference *their_ref;
git_merge_head *their_head; git_merge_head *their_head;
git_merge_analysis_t analysis;
git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname); git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname);
cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname))); cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname)));
cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref)); cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
cl_git_pass(git_merge_analysis(&analysis, repo, (const git_merge_head **)&their_head, 1)); cl_git_pass(git_merge_analysis(merge_analysis, merge_pref, repo, (const git_merge_head **)&their_head, 1));
git_buf_free(&refname); git_buf_free(&refname);
git_merge_head_free(their_head); git_merge_head_free(their_head);
git_reference_free(their_ref); git_reference_free(their_ref);
return analysis;
} }
void test_merge_workdir_analysis__fastforward(void) void test_merge_workdir_analysis__fastforward(void)
{ {
git_merge_analysis_t analysis; git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis = analysis_from_branch(FASTFORWARD_BRANCH); analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)); cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (analysis & GIT_MERGE_ANALYSIS_NORMAL)); cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
} }
void test_merge_workdir_analysis__no_fastforward(void) void test_merge_workdir_analysis__no_fastforward(void)
{ {
git_merge_analysis_t analysis; git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis = analysis_from_branch(NOFASTFORWARD_BRANCH); analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, analysis); cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
} }
void test_merge_workdir_analysis__uptodate(void) void test_merge_workdir_analysis__uptodate(void)
{ {
git_merge_analysis_t analysis; git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis = analysis_from_branch(UPTODATE_BRANCH); analysis_from_branch(&merge_analysis, &merge_pref, UPTODATE_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, analysis); cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
} }
void test_merge_workdir_analysis__uptodate_merging_prev_commit(void) void test_merge_workdir_analysis__uptodate_merging_prev_commit(void)
{ {
git_merge_analysis_t analysis; git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis = analysis_from_branch(PREVIOUS_BRANCH); analysis_from_branch(&merge_analysis, &merge_pref, PREVIOUS_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, analysis); cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
} }
void test_merge_workdir_analysis__unborn(void) void test_merge_workdir_analysis__unborn(void)
{ {
git_merge_analysis_t analysis; git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
git_buf master = GIT_BUF_INIT; git_buf master = GIT_BUF_INIT;
git_buf_joinpath(&master, git_repository_path(repo), "refs/heads/master"); git_buf_joinpath(&master, git_repository_path(repo), "refs/heads/master");
p_unlink(git_buf_cstr(&master)); p_unlink(git_buf_cstr(&master));
analysis = analysis_from_branch(NOFASTFORWARD_BRANCH); analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)); cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (analysis & GIT_MERGE_ANALYSIS_UNBORN)); cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (merge_analysis & GIT_MERGE_ANALYSIS_UNBORN));
git_buf_free(&master); git_buf_free(&master);
} }
void test_merge_workdir_analysis__fastforward_with_config_noff(void)
{
git_config *config;
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
git_repository_config(&config, repo);
git_config_set_string(config, "merge.ff", "false");
analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
cl_assert_equal_i(GIT_MERGE_PREFERENCE_NO_FASTFORWARD, (merge_pref & GIT_MERGE_PREFERENCE_NO_FASTFORWARD));
}
void test_merge_workdir_analysis__no_fastforward_with_config_ffonly(void)
{
git_config *config;
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
git_repository_config(&config, repo);
git_config_set_string(config, "merge.ff", "only");
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
cl_assert_equal_i(GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, (merge_pref & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY));
}

View File

@ -86,3 +86,29 @@ void test_network_fetchlocal__partial(void)
git_strarray_free(&refnames); git_strarray_free(&refnames);
git_remote_free(origin); git_remote_free(origin);
} }
void test_network_fetchlocal__clone_into_mirror(void)
{
git_buf path = GIT_BUF_INIT;
git_repository *repo;
git_remote *remote;
git_reference *head;
cl_git_pass(git_repository_init(&repo, "./foo.git", true));
cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git")));
git_remote_clear_refspecs(remote);
cl_git_pass(git_remote_add_fetch(remote, "+refs/*:refs/*"));
cl_git_pass(git_clone_into(repo, remote, NULL, NULL, NULL));
cl_git_pass(git_reference_lookup(&head, repo, "HEAD"));
cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
git_remote_free(remote);
git_reference_free(head);
git_repository_free(repo);
git_buf_free(&path);
cl_fixture_cleanup("./foo.git");
}

View File

@ -0,0 +1,50 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "refspec.h"
#include "remote.h"
static git_remote *g_remote;
static git_repository *g_repo_a, *g_repo_b;
void test_network_remote_defaultbranch__initialize(void)
{
g_repo_a = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_init(&g_repo_b, "repo-b.git", true));
cl_git_pass(git_remote_create(&g_remote, g_repo_b, "origin", git_repository_path(g_repo_a)));
}
void test_network_remote_defaultbranch__cleanup(void)
{
git_remote_free(g_remote);
git_repository_free(g_repo_b);
cl_git_sandbox_cleanup();
cl_fixture_cleanup("repo-b.git");
}
static void assert_default_branch(const char *should)
{
git_buf name = GIT_BUF_INIT;
cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_default_branch(&name, g_remote));
cl_assert_equal_s(should, name.ptr);
git_buf_free(&name);
}
void test_network_remote_defaultbranch__master(void)
{
assert_default_branch("refs/heads/master");
}
void test_network_remote_defaultbranch__master_does_not_win(void)
{
cl_git_pass(git_repository_set_head(g_repo_a, "refs/heads/not-good", NULL, NULL));
assert_default_branch("refs/heads/not-good");
}
void test_network_remote_defaultbranch__master_on_detached(void)
{
cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
assert_default_branch("refs/heads/master");
}

Some files were not shown because too many files have changed in this diff Show More