Commit dc49e1b5 by Alan Rogers

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

Conflicts:
	include/git2/diff.h
parents 54c02d21 90befde4
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
language: c language: c
os:
- linux
- osx
compiler: compiler:
- gcc - gcc
- clang - clang
...@@ -17,17 +21,21 @@ env: ...@@ -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: ...@@ -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:
......
...@@ -39,12 +39,6 @@ These are good small projects to get started with libgit2. ...@@ -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
......
...@@ -189,6 +189,8 @@ Here are the bindings to libgit2 that are currently available: ...@@ -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
......
...@@ -54,8 +54,9 @@ struct log_options { ...@@ -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); ...@@ -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[]) ...@@ -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[]) ...@@ -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( ...@@ -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))
......
...@@ -14,9 +14,10 @@ ...@@ -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
/** /**
......
...@@ -210,7 +210,7 @@ GIT_EXTERN(int) git_blob_create_frombuffer( ...@@ -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
......
...@@ -43,17 +43,17 @@ GIT_BEGIN_DECL ...@@ -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 ...@@ -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),
......
...@@ -56,7 +56,7 @@ GIT_EXTERN(int) git_cherry_pick_init_options( ...@@ -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(
......
...@@ -23,6 +23,13 @@ ...@@ -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 { ...@@ -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( ...@@ -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
...@@ -218,9 +218,9 @@ typedef struct git_diff git_diff; ...@@ -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 { ...@@ -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)( ...@@ -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 { ...@@ -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 { ...@@ -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.
......
...@@ -19,13 +19,13 @@ GIT_BEGIN_DECL ...@@ -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 { ...@@ -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;
/** /**
......
...@@ -73,10 +73,13 @@ typedef struct git_index_entry { ...@@ -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 { ...@@ -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`. * 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_INTENT_TO_ADD (1 << 13) typedef enum {
#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) GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
/** Reserved for future extension */
GIT_IDXENTRY_EXTENDED2 = (1 << 15),
/** 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` GIT_IDXENTRY_UPDATE = (1 << 0),
* GIT_IDXENTRY_REMOVE = (1 << 1),
* These bitmasks match the other fields in the `git_index_entry` GIT_IDXENTRY_UPTODATE = (1 << 2),
* `flags_extended` value that are only used in-memory by libgit2. You GIT_IDXENTRY_ADDED = (1 << 3),
* can use them to interpret the data in the `flags_extended`.
*/
#define GIT_IDXENTRY_UPDATE (1 << 0)
#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_HASHED = (1 << 4),
#define GIT_IDXENTRY_UNHASHED (1 << 5) GIT_IDXENTRY_UNHASHED = (1 << 5),
#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */ GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
#define GIT_IDXENTRY_CONFLICTED (1 << 7) GIT_IDXENTRY_CONFLICTED = (1 << 7),
#define GIT_IDXENTRY_UNPACKED (1 << 8) GIT_IDXENTRY_UNPACKED = (1 << 8),
#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) 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 ...@@ -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);
......
...@@ -268,6 +268,26 @@ typedef enum { ...@@ -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 { ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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
......
...@@ -29,12 +29,14 @@ GIT_BEGIN_DECL ...@@ -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
......
...@@ -41,6 +41,11 @@ struct git_remote_head { ...@@ -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;
}; };
/** /**
......
...@@ -69,7 +69,7 @@ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const g ...@@ -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);
......
...@@ -178,7 +178,6 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor ...@@ -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, ...@@ -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); ...@@ -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);
......
...@@ -610,6 +610,37 @@ GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value); ...@@ -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
...@@ -649,7 +649,7 @@ GIT_EXTERN(int) git_repository_set_head_detached( ...@@ -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
*/ */
......
...@@ -19,9 +19,9 @@ GIT_BEGIN_DECL ...@@ -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;
/** /**
......
...@@ -56,7 +56,7 @@ GIT_EXTERN(int) git_revert_init_options( ...@@ -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( ...@@ -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(
......
...@@ -69,7 +69,7 @@ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo) ...@@ -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);
......
...@@ -283,7 +283,7 @@ GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule); ...@@ -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
*/ */
......
...@@ -151,7 +151,7 @@ GIT_EXTERN(int) git_tree_entry_bypath( ...@@ -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);
......
...@@ -154,15 +154,15 @@ typedef struct git_packbuilder git_packbuilder; ...@@ -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; ...@@ -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 { ...@@ -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;
......
#!/bin/sh
set -x
sudo apt-get -qq update &&
sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
#!/bin/sh
set -x
brew install libssh2 cmake
...@@ -281,7 +281,7 @@ uint32_t git_attr_file__name_hash(const char *name) ...@@ -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) ...@@ -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( ...@@ -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);
......
...@@ -138,7 +138,7 @@ int git_attr_file__clear_rules( ...@@ -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( ...@@ -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);
......
...@@ -53,7 +53,7 @@ int git_attr_cache__alloc_file_entry( ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
} }
......
...@@ -334,7 +334,8 @@ int git_blob_is_binary(const git_blob *blob) ...@@ -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);
......
/*
* 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
...@@ -139,7 +139,7 @@ int git_config_open_ondisk(git_config **out, const char *path) ...@@ -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) ...@@ -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;
} }
......
...@@ -76,4 +76,10 @@ extern int git_config__get_bool_force( ...@@ -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
...@@ -7,11 +7,11 @@ ...@@ -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[] = { ...@@ -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[] = { ...@@ -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)
return error;
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) if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
(error = git_config__cvar(out, config, cvar)) < 0)
return error; return error;
repo->cvar_cache[(int)cvar] = *out; repo->cvar_cache[(int)cvar] = *out;
......
...@@ -270,7 +270,6 @@ static int config_open(git_config_backend *cfg, git_config_level_t level) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -138,6 +138,10 @@ static int crlf_apply_to_odb( ...@@ -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) {
......
...@@ -390,7 +390,7 @@ static int diff_list_apply_options( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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 { ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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: ...@@ -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;
} }
......
...@@ -233,17 +233,17 @@ static int git_diff_driver_load( ...@@ -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( ...@@ -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);
......
...@@ -287,33 +287,46 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) ...@@ -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) ...@@ -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) ...@@ -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( ...@@ -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: ...@@ -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(
......
...@@ -740,9 +740,11 @@ static int _cp_r_callback(void *ref, git_buf *from) ...@@ -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)
......
...@@ -173,6 +173,7 @@ extern int git_futils_cp( ...@@ -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 { ...@@ -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;
/** /**
......
...@@ -10,6 +10,10 @@ ...@@ -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,
......
...@@ -6,6 +6,40 @@ ...@@ -6,6 +6,40 @@
*/ */
/* /*
* 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.
*/ */
......
/* /*
* 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__
......
...@@ -76,7 +76,7 @@ static void git__shutdown(void) ...@@ -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()
{ {
......
...@@ -248,14 +248,15 @@ void git_ignore__free(git_ignores *ignores) ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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';
/* now add this directory to list of ignores */ while (path.basename > path.path && *path.basename != '/')
if ((error = git_ignore__push_dir(&ignores, path.path)) < 0) path.basename--;
if (path.basename > path.path)
path.basename++;
path.is_dir = 1;
if ((error = git_ignore__pop_dir(&ignores)) < 0)
break; break;
/* reinstate divider in path */
*tail = '/';
while (*tail == '/') tail++;
} }
*ignored = 0; *ignored = 0;
...@@ -404,7 +390,6 @@ cleanup: ...@@ -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,
......
...@@ -42,7 +42,14 @@ extern int git_ignore__pop_dir(git_ignores *ign); ...@@ -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
......
...@@ -1104,6 +1104,15 @@ int git_index_remove_bypath(git_index *index, const char *path) ...@@ -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) ...@@ -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;
......
...@@ -34,7 +34,6 @@ struct git_indexer { ...@@ -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) ...@@ -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;
/* Verify we recognize this pack file format. */ if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
giterr_set(GITERR_OS, "Failed to read in pack header");
return error; return error;
}
memcpy(hdr, map.data, sizeof(*hdr));
p_munmap(&map);
/* Verify we recognize this pack file format. */
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( ...@@ -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( ...@@ -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) ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
} }
...@@ -897,6 +897,7 @@ struct fs_iterator_frame { ...@@ -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) ...@@ -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) ...@@ -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( ...@@ -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) ...@@ -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 == 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->is_ignored; 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( ...@@ -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( ...@@ -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( ...@@ -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 {
......
...@@ -245,6 +245,8 @@ extern int git_iterator_current_parent_tree( ...@@ -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);
......
...@@ -42,5 +42,6 @@ typedef struct { /* memory mapped buffer */ ...@@ -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__ */
...@@ -2564,8 +2564,42 @@ done: ...@@ -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( ...@@ -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( ...@@ -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);
......
...@@ -21,7 +21,7 @@ static size_t line_length_without_trailing_spaces(const char *line, size_t len) ...@@ -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 ...@@ -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);
......
...@@ -206,6 +206,8 @@ static int gitno_ssl_teardown(gitno_ssl *ssl) ...@@ -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) ...@@ -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;
......
...@@ -783,6 +783,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) ...@@ -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;
......
...@@ -17,6 +17,7 @@ ...@@ -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 { ...@@ -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;
......
...@@ -1135,3 +1135,21 @@ int git_path_dirload_with_stat( ...@@ -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;
}
...@@ -438,4 +438,7 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen); ...@@ -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
...@@ -207,6 +207,13 @@ int p_write(git_file fd, const void *buf, size_t cnt) ...@@ -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);
......
...@@ -73,6 +73,7 @@ extern int p_rename(const char *from, const char *to); ...@@ -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;
......
...@@ -458,6 +458,7 @@ typedef struct { ...@@ -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) ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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); ...@@ -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;
/* Defaults to the opposite of the repo being bare */
is_bare = git_repository_is_bare(repo);
logall = !is_bare;
if ((error = git_repository_config__weakptr(&config, repo)) < 0) error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
if (error < 0)
return error; return error;
error = git_config_get_bool(&logall, config, "core.logallrefupdates"); /* Defaults to the opposite of the repo being bare */
if (error < 0 && error != GIT_ENOTFOUND) if (logall == GIT_LOGALLREFUPDATES_UNSET)
return error; logall = !git_repository_is_bare(repo);
if (!logall) { if (!logall) {
*write = 0; *write = 0;
......
...@@ -159,8 +159,7 @@ int git_reference_name_to_id( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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 ...@@ -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: ...@@ -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(
......
...@@ -51,6 +51,8 @@ ...@@ -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 { ...@@ -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);
......
...@@ -403,6 +403,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) ...@@ -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( ...@@ -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) ...@@ -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);
}
...@@ -443,7 +443,6 @@ int git_repository_open_ext( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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;
......
...@@ -39,6 +39,7 @@ typedef enum { ...@@ -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 { ...@@ -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 */
......
...@@ -363,20 +363,22 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu ...@@ -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);
......
...@@ -40,17 +40,29 @@ typedef struct { ...@@ -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) ...@@ -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) ...@@ -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: ...@@ -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( ...@@ -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( ...@@ -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) ...@@ -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);
......
...@@ -7,6 +7,7 @@ ...@@ -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( ...@@ -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) ...@@ -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) ...@@ -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( ...@@ -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( ...@@ -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( ...@@ -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) ...@@ -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);
......
...@@ -23,6 +23,7 @@ ...@@ -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 { ...@@ -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( ...@@ -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);
......
...@@ -433,6 +433,7 @@ void git_pkt_free(git_pkt *pkt) ...@@ -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) {
......
...@@ -26,17 +26,16 @@ int git_smart__store_refs(transport_smart *t, int flushes) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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:
......
...@@ -10,8 +10,14 @@ ...@@ -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;
......
...@@ -133,6 +133,13 @@ GIT_INLINE(int) git__is_uint32(size_t p) ...@@ -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)
......
...@@ -23,6 +23,11 @@ static DWORD get_page_size(void) ...@@ -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);
......
...@@ -10,6 +10,7 @@ ...@@ -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
......
...@@ -19,6 +19,12 @@ ...@@ -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);
......
...@@ -19,6 +19,15 @@ ...@@ -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, ...) ...@@ -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) ...@@ -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) ...@@ -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)
......
...@@ -518,3 +518,16 @@ void cl_fake_home(void) ...@@ -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);
}
...@@ -139,4 +139,6 @@ void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value ...@@ -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
#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));
}
...@@ -35,3 +35,31 @@ void assert_config_entry_value( ...@@ -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;
}
...@@ -7,3 +7,7 @@ extern void assert_config_entry_value( ...@@ -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);
...@@ -2,25 +2,10 @@ ...@@ -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) ...@@ -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)
......
...@@ -47,6 +47,8 @@ void test_config_include__homedir(void) ...@@ -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)
......
...@@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void) ...@@ -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) ...@@ -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));
} }
...@@ -40,12 +40,12 @@ void test_core_env__initialize(void) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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"));
......
...@@ -647,7 +647,7 @@ static void workdir_iterator_test( ...@@ -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[] = {
......
...@@ -1580,3 +1580,117 @@ void test_diff_workdir__can_update_index(void) ...@@ -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);
}
...@@ -103,12 +103,12 @@ void test_filter_crlf__with_safecrlf(void) ...@@ -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) ...@@ -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);
}
...@@ -134,3 +134,21 @@ void test_index_crlf__autocrlf_input_text_auto_attr(void) ...@@ -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"));
}
...@@ -152,3 +152,20 @@ void test_index_filemodes__trusted(void) ...@@ -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);
}
...@@ -6,16 +6,12 @@ int __cdecl main(int argc, char *argv[]) ...@@ -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();
......
...@@ -36,72 +36,105 @@ void test_merge_workdir_analysis__cleanup(void) ...@@ -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));
}
...@@ -86,3 +86,29 @@ void test_network_fetchlocal__partial(void) ...@@ -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");
}
#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");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment