mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 06:20:56 +00:00
Merge remote-tracking branch 'origin/development' into fix-git-status-list-new-unreadable-folder
Conflicts: include/git2/diff.h
This commit is contained in:
commit
dc49e1b5b3
16
.travis.yml
16
.travis.yml
@ -3,6 +3,10 @@
|
||||
|
||||
language: c
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
@ -17,17 +21,21 @@ env:
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
include:
|
||||
- compiler: i586-mingw32msvc-gcc
|
||||
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF"
|
||||
os: linux
|
||||
- compiler: gcc
|
||||
env: COVERITY=1
|
||||
os: linux
|
||||
allow_failures:
|
||||
- env: COVERITY=1
|
||||
|
||||
install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
|
||||
- ./script/install-deps-${TRAVIS_OS_NAME}.sh
|
||||
|
||||
# Run the Build script and tests
|
||||
script:
|
||||
@ -35,8 +43,8 @@ script:
|
||||
|
||||
# Run Tests
|
||||
after_success:
|
||||
- sudo apt-get -qq install valgrind
|
||||
- valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install valgrind; fi
|
||||
- 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
|
||||
branches:
|
||||
|
@ -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
|
||||
`print_commit()` routine (along with a way of passing the option
|
||||
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
|
||||
`-i` option to use `strcasestr()` for matches.
|
||||
* 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:
|
||||
* GitPowerShell <https://github.com/ethomson/gitpowershell>
|
||||
* Python
|
||||
* pygit2 <https://github.com/libgit2/pygit2>
|
||||
* R
|
||||
* git2r <https://github.com/ropensci/git2r>
|
||||
* Ruby
|
||||
* Rugged <https://github.com/libgit2/rugged>
|
||||
* Vala
|
||||
|
@ -54,8 +54,9 @@ struct log_options {
|
||||
int min_parents, max_parents;
|
||||
git_time_t before;
|
||||
git_time_t after;
|
||||
char *author;
|
||||
char *committer;
|
||||
const char *author;
|
||||
const char *committer;
|
||||
const char *grep;
|
||||
};
|
||||
|
||||
/** utility functions that parse options and help with log output */
|
||||
@ -65,6 +66,9 @@ static void print_time(const git_time *intime, const char *prefix);
|
||||
static void print_commit(git_commit *commit);
|
||||
static 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[])
|
||||
{
|
||||
@ -128,6 +132,15 @@ int main(int argc, char *argv[])
|
||||
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)
|
||||
continue;
|
||||
if (opt.limit != -1 && printed++ >= opt.limit) {
|
||||
@ -172,6 +185,32 @@ int main(int argc, char *argv[])
|
||||
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. */
|
||||
static void push_rev(struct log_state *s, git_object *obj, int hide)
|
||||
{
|
||||
@ -401,6 +440,12 @@ static int parse_options(
|
||||
set_sorting(s, GIT_SORT_TOPOLOGICAL);
|
||||
else if (!strcmp(a, "--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"))
|
||||
/** Found git-dir. */;
|
||||
else if (match_int_arg(&opt->skip, &args, "--skip", 0))
|
||||
|
@ -14,9 +14,10 @@
|
||||
|
||||
#include "common.h"
|
||||
#ifdef _WIN32
|
||||
#define sleep(a) Sleep(a * 1000)
|
||||
# include <Windows.h>
|
||||
# define sleep(a) Sleep(a * 1000)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -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:
|
||||
* 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
|
||||
* @return 1 if the content of the blob is detected
|
||||
|
@ -43,17 +43,17 @@ GIT_BEGIN_DECL
|
||||
* In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE`
|
||||
* both of which only make modifications that will not lose changes.
|
||||
*
|
||||
* | target == baseline | target != baseline |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* workdir == baseline | no action | create, update, or |
|
||||
* | | delete file |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* workdir exists and | no action | conflict (notify |
|
||||
* is != baseline | notify dirty MODIFIED | and cancel checkout) |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* workdir missing, | create if SAFE_CREATE | create file |
|
||||
* baseline present | notify dirty DELETED | |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* | target == baseline | target != baseline |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* workdir == baseline | no action | create, update, or |
|
||||
* | | delete file |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* workdir exists and | no action | conflict (notify |
|
||||
* is != baseline | notify dirty MODIFIED | and cancel checkout) |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
* workdir missing, | create if SAFE_CREATE | create file |
|
||||
* baseline present | notify dirty DELETED | |
|
||||
* ---------------------|-----------------------|----------------------|
|
||||
*
|
||||
* 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
|
||||
@ -106,7 +106,7 @@ GIT_BEGIN_DECL
|
||||
* target contains that file.
|
||||
*/
|
||||
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 */
|
||||
GIT_CHECKOUT_SAFE = (1u << 0),
|
||||
|
@ -56,7 +56,7 @@ GIT_EXTERN(int) git_cherry_pick_init_options(
|
||||
* @param cherry_pick_commit the commit to cherry-pick
|
||||
* @param our_commit the commit to revert against (eg, HEAD)
|
||||
* @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.
|
||||
*/
|
||||
GIT_EXTERN(int) git_cherry_pick_commit(
|
||||
|
@ -23,6 +23,13 @@
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -57,6 +64,7 @@ typedef struct git_clone_options {
|
||||
|
||||
int bare;
|
||||
int ignore_cert_errors;
|
||||
git_clone_local_t local;
|
||||
const char *remote_name;
|
||||
const char* checkout_branch;
|
||||
git_signature *signature;
|
||||
@ -123,6 +131,35 @@ GIT_EXTERN(int) git_clone_into(
|
||||
const char *branch,
|
||||
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
|
||||
#endif
|
||||
|
@ -218,9 +218,9 @@ typedef struct git_diff git_diff;
|
||||
* considered reserved for internal or future use.
|
||||
*/
|
||||
typedef enum {
|
||||
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_VALID_ID = (1u << 2), /** `id` value is known correct */
|
||||
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_VALID_ID = (1u << 2), /**< `id` value is known correct */
|
||||
} git_diff_flag_t;
|
||||
|
||||
/**
|
||||
@ -234,16 +234,16 @@ typedef enum {
|
||||
* DELETED pairs).
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_DELTA_UNMODIFIED = 0, /** no changes */
|
||||
GIT_DELTA_ADDED = 1, /** entry does not exist in old 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_RENAMED = 4, /** entry was renamed between old and new */
|
||||
GIT_DELTA_COPIED = 5, /** entry was copied from another old entry */
|
||||
GIT_DELTA_IGNORED = 6, /** entry is ignored 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_UNREADABLE = 9, /** entry is unreadable */
|
||||
GIT_DELTA_UNMODIFIED = 0, /**< no changes */
|
||||
GIT_DELTA_ADDED = 1, /**< entry does not exist in old 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_RENAMED = 4, /**< entry was renamed between old and new */
|
||||
GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */
|
||||
GIT_DELTA_IGNORED = 6, /**< entry is ignored 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_UNREADABLE = 9, /**< entry is unreadable */
|
||||
} git_delta_t;
|
||||
|
||||
/**
|
||||
@ -423,12 +423,12 @@ typedef int (*git_diff_file_cb)(
|
||||
*/
|
||||
typedef struct git_diff_hunk git_diff_hunk;
|
||||
struct git_diff_hunk {
|
||||
int old_start; /** Starting line number in old_file */
|
||||
int old_lines; /** Number of lines in old_file */
|
||||
int new_start; /** Starting line number in new_file */
|
||||
int new_lines; /** Number of lines in new_file */
|
||||
size_t header_len; /** Number of bytes in header text */
|
||||
char header[128]; /** Header text, NUL-byte terminated */
|
||||
int old_start; /**< Starting line number in old_file */
|
||||
int old_lines; /**< Number of lines in old_file */
|
||||
int new_start; /**< Starting line number in new_file */
|
||||
int new_lines; /**< Number of lines in new_file */
|
||||
size_t header_len; /**< Number of bytes in header text */
|
||||
char header[128]; /**< Header text, NUL-byte terminated */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -471,13 +471,13 @@ typedef enum {
|
||||
*/
|
||||
typedef struct git_diff_line git_diff_line;
|
||||
struct git_diff_line {
|
||||
char origin; /** A git_diff_line_t value */
|
||||
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 num_lines; /** Number of newline characters in content */
|
||||
size_t content_len; /** Number of bytes of data */
|
||||
git_off_t content_offset; /** Offset in the original file to the content */
|
||||
const char *content; /** Pointer to diff text, not NUL-byte terminated */
|
||||
char origin; /**< A git_diff_line_t value */
|
||||
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 num_lines; /**< Number of newline characters in content */
|
||||
size_t content_len; /**< Number of bytes of data */
|
||||
git_off_t content_offset; /**< Offset in the original file to the content */
|
||||
const char *content; /**< Pointer to diff text, not NUL-byte terminated */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -489,10 +489,10 @@ struct git_diff_line {
|
||||
* of lines of file and hunk headers.
|
||||
*/
|
||||
typedef int (*git_diff_line_cb)(
|
||||
const git_diff_delta *delta, /** delta that contains this data */
|
||||
const git_diff_hunk *hunk, /** hunk containing this data */
|
||||
const git_diff_line *line, /** line data */
|
||||
void *payload); /** user reference data */
|
||||
const git_diff_delta *delta, /**< delta that contains this data */
|
||||
const git_diff_hunk *hunk, /**< hunk containing this data */
|
||||
const git_diff_line *line, /**< line data */
|
||||
void *payload); /**< user reference data */
|
||||
|
||||
/**
|
||||
* Flags to control the behavior of diff rename/copy detection.
|
||||
|
@ -19,13 +19,13 @@ GIT_BEGIN_DECL
|
||||
|
||||
/** Generic return codes */
|
||||
typedef enum {
|
||||
GIT_OK = 0, /*< No error */
|
||||
GIT_OK = 0, /**< No error */
|
||||
|
||||
GIT_ERROR = -1, /*< Generic error */
|
||||
GIT_ENOTFOUND = -3, /*< Requested object could not be found */
|
||||
GIT_EEXISTS = -4, /*< Object exists preventing operation */
|
||||
GIT_EAMBIGUOUS = -5, /*< More than one object matches */
|
||||
GIT_EBUFS = -6, /*< Output buffer too short to hold data */
|
||||
GIT_ERROR = -1, /**< Generic error */
|
||||
GIT_ENOTFOUND = -3, /**< Requested object could not be found */
|
||||
GIT_EEXISTS = -4, /**< Object exists preventing operation */
|
||||
GIT_EAMBIGUOUS = -5, /**< More than one object matches */
|
||||
GIT_EBUFS = -6, /**< Output buffer too short to hold data */
|
||||
|
||||
/* GIT_EUSER is a special error that is never generated by libgit2
|
||||
* code. You can return it from a callback (e.g to stop an iteration)
|
||||
@ -33,17 +33,17 @@ typedef enum {
|
||||
*/
|
||||
GIT_EUSER = -7,
|
||||
|
||||
GIT_EBAREREPO = -8, /*< Operation not allowed on bare repository */
|
||||
GIT_EUNBORNBRANCH = -9, /*< HEAD refers to branch with no commits */
|
||||
GIT_EUNMERGED = -10, /*< Merge in progress prevented operation */
|
||||
GIT_ENONFASTFORWARD = -11, /*< Reference was not fast-forwardable */
|
||||
GIT_EINVALIDSPEC = -12, /*< Name/ref spec was not in a valid format */
|
||||
GIT_EMERGECONFLICT = -13, /*< Merge conflicts prevented operation */
|
||||
GIT_ELOCKED = -14, /*< Lock file prevented operation */
|
||||
GIT_EMODIFIED = -15, /*< Reference value does not match expected */
|
||||
GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
|
||||
GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
|
||||
GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */
|
||||
GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */
|
||||
GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */
|
||||
GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */
|
||||
GIT_ELOCKED = -14, /**< Lock file prevented operation */
|
||||
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
|
||||
|
||||
GIT_PASSTHROUGH = -30, /*< Internal only */
|
||||
GIT_ITEROVER = -31, /*< Signals end of iteration with iterator */
|
||||
GIT_PASSTHROUGH = -30, /**< Internal only */
|
||||
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
|
||||
} git_error_code;
|
||||
|
||||
/**
|
||||
|
@ -73,10 +73,13 @@ typedef struct git_index_entry {
|
||||
*/
|
||||
#define GIT_IDXENTRY_NAMEMASK (0x0fff)
|
||||
#define GIT_IDXENTRY_STAGEMASK (0x3000)
|
||||
#define GIT_IDXENTRY_EXTENDED (0x4000)
|
||||
#define GIT_IDXENTRY_VALID (0x8000)
|
||||
#define GIT_IDXENTRY_STAGESHIFT 12
|
||||
|
||||
typedef enum {
|
||||
GIT_IDXENTRY_EXTENDED = (0x4000),
|
||||
GIT_IDXENTRY_VALID = (0x8000),
|
||||
} git_indxentry_flag_t;
|
||||
|
||||
#define GIT_IDXENTRY_STAGE(E) \
|
||||
(((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
|
||||
|
||||
@ -92,36 +95,36 @@ typedef struct git_index_entry {
|
||||
* in-memory only and used by libgit2. Only the flags in
|
||||
* `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk.
|
||||
*
|
||||
* These bitmasks match the three fields in the `git_index_entry`
|
||||
* `flags_extended` value that belong on disk. You can use them to
|
||||
* interpret the data in the `flags_extended`.
|
||||
*/
|
||||
#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13)
|
||||
#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14)
|
||||
/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */
|
||||
#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
|
||||
|
||||
#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
|
||||
|
||||
/**
|
||||
* Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended`
|
||||
*
|
||||
* These bitmasks match the other fields in the `git_index_entry`
|
||||
* `flags_extended` value that are only used in-memory by libgit2. You
|
||||
* Thee first three bitmasks match the three fields in the
|
||||
* `git_index_entry` `flags_extended` value that belong on disk. You
|
||||
* can use them to interpret the data in the `flags_extended`.
|
||||
*
|
||||
* The rest of the bitmasks match the other fields in the `git_index_entry`
|
||||
* `flags_extended` value that are only used in-memory by libgit2.
|
||||
* You can use them to interpret the data in the `flags_extended`.
|
||||
*
|
||||
*/
|
||||
#define GIT_IDXENTRY_UPDATE (1 << 0)
|
||||
#define GIT_IDXENTRY_REMOVE (1 << 1)
|
||||
#define GIT_IDXENTRY_UPTODATE (1 << 2)
|
||||
#define GIT_IDXENTRY_ADDED (1 << 3)
|
||||
typedef enum {
|
||||
|
||||
#define GIT_IDXENTRY_HASHED (1 << 4)
|
||||
#define GIT_IDXENTRY_UNHASHED (1 << 5)
|
||||
#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */
|
||||
#define GIT_IDXENTRY_CONFLICTED (1 << 7)
|
||||
GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
|
||||
GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
|
||||
/** Reserved for future extension */
|
||||
GIT_IDXENTRY_EXTENDED2 = (1 << 15),
|
||||
|
||||
#define GIT_IDXENTRY_UNPACKED (1 << 8)
|
||||
#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
|
||||
GIT_IDXENTRY_EXTENDED_FLAGS = (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE),
|
||||
GIT_IDXENTRY_UPDATE = (1 << 0),
|
||||
GIT_IDXENTRY_REMOVE = (1 << 1),
|
||||
GIT_IDXENTRY_UPTODATE = (1 << 2),
|
||||
GIT_IDXENTRY_ADDED = (1 << 3),
|
||||
|
||||
GIT_IDXENTRY_HASHED = (1 << 4),
|
||||
GIT_IDXENTRY_UNHASHED = (1 << 5),
|
||||
GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
|
||||
GIT_IDXENTRY_CONFLICTED = (1 << 7),
|
||||
|
||||
GIT_IDXENTRY_UNPACKED = (1 << 8),
|
||||
GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9),
|
||||
} git_idxentry_extended_flag_t;
|
||||
|
||||
/** Capabilities of system that affect index actions. */
|
||||
typedef enum {
|
||||
@ -412,10 +415,10 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
|
||||
*
|
||||
* This entry is calculated from the entry's flag attribute like this:
|
||||
*
|
||||
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
|
||||
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
|
||||
*
|
||||
* @param entry The entry
|
||||
* @returns the stage number
|
||||
* @return the stage number
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
|
||||
|
||||
|
@ -268,6 +268,26 @@ typedef enum {
|
||||
GIT_MERGE_ANALYSIS_UNBORN = (1 << 3),
|
||||
} 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
|
||||
* merging them into the HEAD of the repository.
|
||||
@ -280,6 +300,7 @@ typedef enum {
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge_analysis(
|
||||
git_merge_analysis_t *analysis_out,
|
||||
git_merge_preference_t *preference_out,
|
||||
git_repository *repo,
|
||||
const git_merge_head **their_heads,
|
||||
size_t their_heads_len);
|
||||
@ -378,8 +399,8 @@ GIT_EXTERN(int) git_merge_head_from_id(
|
||||
/**
|
||||
* Gets the commit ID that the given `git_merge_head` refers to.
|
||||
*
|
||||
* @param id pointer to commit id to be filled in
|
||||
* @param head the given merge head
|
||||
* @return commit id
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_merge_head_id(
|
||||
const git_merge_head *head);
|
||||
@ -424,8 +445,8 @@ GIT_EXTERN(int) git_merge_file(
|
||||
* @param out The git_merge_file_result to be filled in
|
||||
* @param repo The repository
|
||||
* @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 their_path The index entry for their file (stage level 3)
|
||||
* @param ours The index entry for our file (stage level 2)
|
||||
* @param theirs The index entry for their file (stage level 3)
|
||||
* @param opts The merge file options or NULL
|
||||
* @return 0 on success or error code
|
||||
*/
|
||||
@ -497,8 +518,8 @@ GIT_EXTERN(int) git_merge_commits(
|
||||
* completes, resolve any conflicts and prepare a commit.
|
||||
*
|
||||
* @param repo the repository to merge
|
||||
* @param merge_heads the heads to merge into
|
||||
* @param merge_heads_len the number of heads to merge
|
||||
* @param their_heads the heads to merge into
|
||||
* @param their_heads_len the number of heads to merge
|
||||
* @param merge_opts merge options
|
||||
* @param checkout_opts checkout options
|
||||
* @return 0 on success or error code
|
||||
|
@ -29,12 +29,14 @@ GIT_BEGIN_DECL
|
||||
*
|
||||
* @param message The message to be prettified.
|
||||
*
|
||||
* @param strip_comments Non-zero to remove lines starting with "#", 0 to
|
||||
* leave them in.
|
||||
* @param strip_comments Non-zero to remove comment lines, 0 to 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.
|
||||
*/
|
||||
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
|
||||
|
@ -41,6 +41,11 @@ struct git_remote_head {
|
||||
git_oid oid;
|
||||
git_oid loid;
|
||||
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
|
||||
*
|
||||
* @param repo the repository
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
* @param name The name of the reference
|
||||
* @param id The object id pointed to by the reference.
|
||||
* @param force Overwrite existing references
|
||||
* @param force Overwrite existing references
|
||||
* @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
|
||||
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
|
||||
@ -221,7 +220,6 @@ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo,
|
||||
* @param name The name of the reference
|
||||
* @param id The object id pointed to by the reference.
|
||||
* @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 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
|
||||
@ -415,7 +413,7 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
||||
* This method removes the named reference from the repository without
|
||||
* looking at its old value.
|
||||
*
|
||||
* @param ref The reference to remove
|
||||
* @param name The reference to remove
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
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);
|
||||
*/
|
||||
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
|
||||
#endif
|
||||
|
@ -649,7 +649,7 @@ GIT_EXTERN(int) git_repository_set_head_detached(
|
||||
*
|
||||
* @param repo Repository pointer
|
||||
* @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
|
||||
* branch or an error code
|
||||
*/
|
||||
|
@ -19,9 +19,9 @@ GIT_BEGIN_DECL
|
||||
* Kinds of reset operation
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_RESET_SOFT = 1, /** Move the head to the given 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_SOFT = 1, /**< Move the head to the given 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_t;
|
||||
|
||||
/**
|
||||
|
@ -56,7 +56,7 @@ GIT_EXTERN(int) git_revert_init_options(
|
||||
* @param revert_commit the commit to revert
|
||||
* @param our_commit the commit to revert against (eg, HEAD)
|
||||
* @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.
|
||||
*/
|
||||
int git_revert_commit(
|
||||
@ -71,9 +71,8 @@ int git_revert_commit(
|
||||
* Reverts the given commit, producing changes in the working directory.
|
||||
*
|
||||
* @param repo the repository to revert
|
||||
* @param commits the commits to revert
|
||||
* @param commits_len the number of commits to revert
|
||||
* @param flags merge flags
|
||||
* @param commit the commit to revert
|
||||
* @param given_opts merge flags
|
||||
* @return zero on success, -1 on failure.
|
||||
*/
|
||||
GIT_EXTERN(int) git_revert(
|
||||
|
@ -69,7 +69,7 @@ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo)
|
||||
* Call `git_signature_free()` to free the data.
|
||||
*
|
||||
* @param dest pointer where to store the copy
|
||||
* @param entry signature to duplicate
|
||||
* @param sig signature to duplicate
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
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);
|
||||
* Resolve a submodule url relative to the given repository.
|
||||
*
|
||||
* @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
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
|
@ -151,7 +151,7 @@ GIT_EXTERN(int) git_tree_entry_bypath(
|
||||
* and must be freed explicitly with `git_tree_entry_free()`.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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;
|
||||
|
||||
/** Time in a signature */
|
||||
typedef struct git_time {
|
||||
git_time_t time; /** time in seconds from epoch */
|
||||
int offset; /** timezone offset, in minutes */
|
||||
git_time_t time; /**< time in seconds from epoch */
|
||||
int offset; /**< timezone offset, in minutes */
|
||||
} git_time;
|
||||
|
||||
/** An action signature (e.g. for committers, taggers, etc) */
|
||||
typedef struct git_signature {
|
||||
char *name; /** full name of the author */
|
||||
char *email; /** email of the author */
|
||||
git_time when; /** time when the action happened */
|
||||
char *name; /**< full name of the author */
|
||||
char *email; /**< email of the author */
|
||||
git_time when; /**< time when the action happened */
|
||||
} git_signature;
|
||||
|
||||
/** In-memory representation of a reference. */
|
||||
@ -183,9 +183,9 @@ typedef struct git_status_list git_status_list;
|
||||
|
||||
/** Basic type of any Git reference. */
|
||||
typedef enum {
|
||||
GIT_REF_INVALID = 0, /** Invalid reference */
|
||||
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_INVALID = 0, /**< Invalid reference */
|
||||
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_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC,
|
||||
} git_ref_t;
|
||||
|
||||
@ -314,12 +314,12 @@ typedef enum {
|
||||
* when we don't want any particular ignore rule to be specified.
|
||||
*/
|
||||
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_UNTRACKED = 2, /* dirty if tracked files change */
|
||||
GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */
|
||||
GIT_SUBMODULE_IGNORE_ALL = 4, /* never dirty */
|
||||
GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
|
||||
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
|
||||
GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
|
||||
GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
|
||||
|
||||
GIT_SUBMODULE_IGNORE_DEFAULT = 0
|
||||
} git_submodule_ignore_t;
|
||||
|
6
script/install-deps-linux.sh
Executable file
6
script/install-deps-linux.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
sudo apt-get -qq update &&
|
||||
sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
|
5
script/install-deps-osx.sh
Executable file
5
script/install-deps-osx.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
brew install libssh2 cmake
|
@ -281,7 +281,7 @@ uint32_t git_attr_file__name_hash(const char *name)
|
||||
|
||||
int git_attr_file__lookup_one(
|
||||
git_attr_file *file,
|
||||
const git_attr_path *path,
|
||||
git_attr_path *path,
|
||||
const char *attr,
|
||||
const char **value)
|
||||
{
|
||||
@ -342,14 +342,11 @@ int git_attr_file__load_standalone(git_attr_file **out, const char *path)
|
||||
|
||||
bool git_attr_fnmatch__match(
|
||||
git_attr_fnmatch *match,
|
||||
const git_attr_path *path)
|
||||
git_attr_path *path)
|
||||
{
|
||||
const char *filename;
|
||||
int flags = 0;
|
||||
|
||||
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir)
|
||||
return false;
|
||||
|
||||
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
|
||||
flags |= FNM_CASEFOLD;
|
||||
if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
|
||||
@ -365,12 +362,28 @@ bool git_attr_fnmatch__match(
|
||||
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);
|
||||
}
|
||||
|
||||
bool git_attr_rule__match(
|
||||
git_attr_rule *rule,
|
||||
const git_attr_path *path)
|
||||
git_attr_path *path)
|
||||
{
|
||||
bool matched = git_attr_fnmatch__match(&rule->match, path);
|
||||
|
||||
|
@ -138,7 +138,7 @@ int git_attr_file__clear_rules(
|
||||
|
||||
int git_attr_file__lookup_one(
|
||||
git_attr_file *file,
|
||||
const git_attr_path *path,
|
||||
git_attr_path *path,
|
||||
const char *attr,
|
||||
const char **value);
|
||||
|
||||
@ -162,13 +162,13 @@ extern int git_attr_fnmatch__parse(
|
||||
|
||||
extern bool git_attr_fnmatch__match(
|
||||
git_attr_fnmatch *rule,
|
||||
const git_attr_path *path);
|
||||
git_attr_path *path);
|
||||
|
||||
extern void git_attr_rule__free(git_attr_rule *rule);
|
||||
|
||||
extern bool git_attr_rule__match(
|
||||
git_attr_rule *rule,
|
||||
const git_attr_path *path);
|
||||
git_attr_path *path);
|
||||
|
||||
extern git_attr_assignment *git_attr_rule__lookup_assignment(
|
||||
git_attr_rule *rule, const char *name);
|
||||
|
@ -53,7 +53,7 @@ int git_attr_cache__alloc_file_entry(
|
||||
cachesize++;
|
||||
}
|
||||
|
||||
ce = git_pool_mallocz(pool, cachesize);
|
||||
ce = git_pool_mallocz(pool, (uint32_t)cachesize);
|
||||
GITERR_CHECK_ALLOC(ce);
|
||||
|
||||
if (baselen) {
|
||||
@ -349,14 +349,11 @@ int git_attr_cache__do_init(git_repository *repo)
|
||||
{
|
||||
int ret = 0;
|
||||
git_attr_cache *cache = git_repository_attr_cache(repo);
|
||||
git_config *cfg;
|
||||
git_config *cfg = NULL;
|
||||
|
||||
if (cache)
|
||||
return 0;
|
||||
|
||||
if ((ret = git_repository_config__weakptr(&cfg, repo)) < 0)
|
||||
return ret;
|
||||
|
||||
cache = git__calloc(1, sizeof(git_attr_cache));
|
||||
GITERR_CHECK_ALLOC(cache);
|
||||
|
||||
@ -367,6 +364,9 @@ int git_attr_cache__do_init(git_repository *repo)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
|
||||
goto cancel;
|
||||
|
||||
/* cache config settings for attributes and ignores */
|
||||
ret = attr_cache__lookup_path(
|
||||
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
|
||||
@ -390,11 +390,14 @@ int git_attr_cache__do_init(git_repository *repo)
|
||||
if (cache)
|
||||
goto cancel; /* raced with another thread, free this but no error */
|
||||
|
||||
git_config_free(cfg);
|
||||
|
||||
/* insert default macros */
|
||||
return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
|
||||
|
||||
cancel:
|
||||
attr_cache__free(cache);
|
||||
git_config_free(cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,8 @@ int git_blob_is_binary(const git_blob *blob)
|
||||
assert(blob);
|
||||
|
||||
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;
|
||||
|
||||
return git_buf_text_is_binary(&content);
|
||||
|
313
src/clone.c
313
src/clone.c
@ -22,6 +22,7 @@
|
||||
#include "refs.h"
|
||||
#include "path.h"
|
||||
#include "repository.h"
|
||||
#include "odb.h"
|
||||
|
||||
static int create_branch(
|
||||
git_reference **branch,
|
||||
@ -105,54 +106,6 @@ static int create_tracking_branch(
|
||||
git_reference_name(*branch));
|
||||
}
|
||||
|
||||
struct head_info {
|
||||
git_repository *repo;
|
||||
git_oid remote_head_oid;
|
||||
git_buf branchname;
|
||||
const git_refspec *refspec;
|
||||
bool found;
|
||||
};
|
||||
|
||||
static int reference_matches_remote_head(
|
||||
const char *reference_name,
|
||||
void *payload)
|
||||
{
|
||||
struct head_info *head_info = (struct head_info *)payload;
|
||||
git_oid oid;
|
||||
int error;
|
||||
|
||||
/* TODO: Should we guard against references
|
||||
* which name doesn't start with refs/heads/ ?
|
||||
*/
|
||||
|
||||
error = git_reference_name_to_id(&oid, head_info->repo, reference_name);
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
/* If the reference doesn't exists, it obviously cannot match the
|
||||
* expected oid. */
|
||||
giterr_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) {
|
||||
/* Determine the local reference name from the remote tracking one */
|
||||
error = git_refspec_rtransform(
|
||||
&head_info->branchname, head_info->refspec, reference_name);
|
||||
|
||||
if (!error &&
|
||||
git_buf_len(&head_info->branchname) > 0 &&
|
||||
!(error = git_buf_sets(
|
||||
&head_info->branchname,
|
||||
git_buf_cstr(&head_info->branchname) +
|
||||
strlen(GIT_REFS_HEADS_DIR))))
|
||||
{
|
||||
head_info->found = true;
|
||||
error = GIT_ITEROVER;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int update_head_to_new_branch(
|
||||
git_repository *repo,
|
||||
const git_oid *target,
|
||||
@ -161,7 +114,12 @@ static int update_head_to_new_branch(
|
||||
const char *reflog_message)
|
||||
{
|
||||
git_reference *tracking_branch = NULL;
|
||||
int error = create_tracking_branch(&tracking_branch, repo, target, name,
|
||||
int error;
|
||||
|
||||
if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
|
||||
name += strlen(GIT_REFS_HEADS_DIR);
|
||||
|
||||
error = create_tracking_branch(&tracking_branch, repo, target, name,
|
||||
signature, reflog_message);
|
||||
|
||||
if (!error)
|
||||
@ -171,6 +129,10 @@ static int update_head_to_new_branch(
|
||||
|
||||
git_reference_free(tracking_branch);
|
||||
|
||||
/* if it already existed, then the user's refspec created it for us, ignore it' */
|
||||
if (error == GIT_EEXISTS)
|
||||
error = 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -180,12 +142,13 @@ static int update_head_to_remote(
|
||||
const git_signature *signature,
|
||||
const char *reflog_message)
|
||||
{
|
||||
int error = 0;
|
||||
int error = 0, found_branch = 0;
|
||||
size_t refs_len;
|
||||
git_refspec dummy_spec;
|
||||
git_refspec dummy_spec, *refspec;
|
||||
const git_remote_head *remote_head, **refs;
|
||||
struct head_info head_info;
|
||||
const git_oid *remote_head_id;
|
||||
git_buf remote_master_name = GIT_BUF_INIT;
|
||||
git_buf branch = GIT_BUF_INIT;
|
||||
|
||||
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
|
||||
return error;
|
||||
@ -195,63 +158,45 @@ static int update_head_to_remote(
|
||||
return setup_tracking_config(
|
||||
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
|
||||
|
||||
error = git_remote_default_branch(&branch, remote);
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE);
|
||||
} else {
|
||||
found_branch = 1;
|
||||
}
|
||||
|
||||
/* Get the remote's HEAD. This is always the first ref in the list. */
|
||||
remote_head = refs[0];
|
||||
assert(remote_head);
|
||||
|
||||
memset(&head_info, 0, sizeof(head_info));
|
||||
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
|
||||
head_info.repo = repo;
|
||||
head_info.refspec =
|
||||
git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
|
||||
remote_head_id = &remote_head->oid;
|
||||
refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
|
||||
|
||||
if (head_info.refspec == NULL) {
|
||||
if (refspec == NULL) {
|
||||
memset(&dummy_spec, 0, sizeof(git_refspec));
|
||||
head_info.refspec = &dummy_spec;
|
||||
refspec = &dummy_spec;
|
||||
}
|
||||
|
||||
/* Determine the remote tracking reference name from the local master */
|
||||
if ((error = git_refspec_transform(
|
||||
&remote_master_name,
|
||||
head_info.refspec,
|
||||
GIT_REFS_HEADS_MASTER_FILE)) < 0)
|
||||
refspec,
|
||||
git_buf_cstr(&branch))) < 0)
|
||||
return error;
|
||||
|
||||
/* Check to see if the remote HEAD points to the remote master */
|
||||
error = reference_matches_remote_head(
|
||||
git_buf_cstr(&remote_master_name), &head_info);
|
||||
if (error < 0 && error != GIT_ITEROVER)
|
||||
goto cleanup;
|
||||
|
||||
if (head_info.found) {
|
||||
if (found_branch) {
|
||||
error = update_head_to_new_branch(
|
||||
repo,
|
||||
&head_info.remote_head_oid,
|
||||
git_buf_cstr(&head_info.branchname),
|
||||
signature, reflog_message);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Not master. Check all the other refs. */
|
||||
error = git_reference_foreach_name(
|
||||
repo, reference_matches_remote_head, &head_info);
|
||||
if (error < 0 && error != GIT_ITEROVER)
|
||||
goto cleanup;
|
||||
|
||||
if (head_info.found) {
|
||||
error = update_head_to_new_branch(
|
||||
repo,
|
||||
&head_info.remote_head_oid,
|
||||
git_buf_cstr(&head_info.branchname),
|
||||
remote_head_id,
|
||||
git_buf_cstr(&branch),
|
||||
signature, reflog_message);
|
||||
} else {
|
||||
error = git_repository_set_head_detached(
|
||||
repo, &head_info.remote_head_oid, signature, reflog_message);
|
||||
repo, remote_head_id, signature, reflog_message);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&remote_master_name);
|
||||
git_buf_free(&head_info.branchname);
|
||||
git_buf_free(&branch);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -297,6 +242,15 @@ static int create_and_configure_origin(
|
||||
int error;
|
||||
git_remote *origin = NULL;
|
||||
const char *name;
|
||||
char buf[GIT_PATH_MAX];
|
||||
|
||||
/* If the path exists and is a dir, the url should be the absolute path */
|
||||
if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
|
||||
if (p_realpath(url, buf) == NULL)
|
||||
return -1;
|
||||
|
||||
url = buf;
|
||||
}
|
||||
|
||||
name = options->remote_name ? options->remote_name : "origin";
|
||||
if ((error = git_remote_create(&origin, repo, name, url)) < 0)
|
||||
@ -336,59 +290,86 @@ static bool should_checkout(
|
||||
return !git_repository_head_unborn(repo);
|
||||
}
|
||||
|
||||
int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
|
||||
static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature, const char *reflog_message)
|
||||
{
|
||||
int error = 0, old_fetchhead;
|
||||
git_strarray refspecs;
|
||||
git_buf reflog_message = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
assert(repo && remote);
|
||||
if (branch)
|
||||
error = update_head_to_branch(repo, git_remote_name(remote), branch,
|
||||
signature, reflog_message);
|
||||
/* Point HEAD to the same ref as the remote's head */
|
||||
else
|
||||
error = update_head_to_remote(repo, remote, signature, reflog_message);
|
||||
|
||||
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
|
||||
error = git_checkout_head(repo, co_opts);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
|
||||
{
|
||||
int error;
|
||||
git_buf reflog_message = GIT_BUF_INIT;
|
||||
git_remote *remote;
|
||||
const git_remote_callbacks *callbacks;
|
||||
|
||||
assert(repo && _remote);
|
||||
|
||||
if (!git_repository_is_empty(repo)) {
|
||||
giterr_set(GITERR_INVALID, "the repository is not empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
|
||||
if ((error = git_remote_dup(&remote, _remote)) < 0)
|
||||
return error;
|
||||
|
||||
callbacks = git_remote_get_callbacks(_remote);
|
||||
if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
|
||||
(error = git_remote_set_callbacks(remote, callbacks)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
|
||||
return error;
|
||||
goto cleanup;
|
||||
|
||||
old_fetchhead = git_remote_update_fetchhead(remote);
|
||||
git_remote_set_update_fetchhead(remote, 0);
|
||||
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
|
||||
|
||||
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
|
||||
goto cleanup;
|
||||
|
||||
if (branch)
|
||||
error = update_head_to_branch(repo, git_remote_name(remote), branch,
|
||||
signature, git_buf_cstr(&reflog_message));
|
||||
/* Point HEAD to the same ref as the remote's head */
|
||||
else
|
||||
error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message));
|
||||
|
||||
if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
|
||||
error = git_checkout_head(repo, co_opts);
|
||||
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
|
||||
|
||||
cleanup:
|
||||
git_remote_set_update_fetchhead(remote, old_fetchhead);
|
||||
|
||||
/* Go back to the original refspecs */
|
||||
{
|
||||
int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs);
|
||||
if (!error)
|
||||
error = error_alt;
|
||||
}
|
||||
|
||||
git_strarray_free(&refspecs);
|
||||
git_remote_free(remote);
|
||||
git_buf_free(&reflog_message);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_clone__should_clone_local(const char *url, git_clone_local_t local)
|
||||
{
|
||||
const char *path;
|
||||
int is_url;
|
||||
|
||||
if (local == GIT_CLONE_NO_LOCAL)
|
||||
return false;
|
||||
|
||||
is_url = !git__prefixcmp(url, "file://");
|
||||
|
||||
if (is_url && local != GIT_CLONE_LOCAL && local != GIT_CLONE_LOCAL_NO_LINKS )
|
||||
return false;
|
||||
|
||||
path = url;
|
||||
if (is_url)
|
||||
path = url + strlen("file://");
|
||||
|
||||
if ((git_path_exists(path) && git_path_isdir(path)) && local != GIT_CLONE_NO_LOCAL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int git_clone(
|
||||
git_repository **out,
|
||||
const char *url,
|
||||
@ -423,8 +404,16 @@ int git_clone(
|
||||
return error;
|
||||
|
||||
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
|
||||
error = git_clone_into(
|
||||
repo, origin, &options.checkout_opts, options.checkout_branch, options.signature);
|
||||
if (git_clone__should_clone_local(url, options.local)) {
|
||||
int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
|
||||
error = git_clone_local_into(
|
||||
repo, origin, &options.checkout_opts,
|
||||
options.checkout_branch, link, options.signature);
|
||||
} else {
|
||||
error = git_clone_into(
|
||||
repo, origin, &options.checkout_opts,
|
||||
options.checkout_branch, options.signature);
|
||||
}
|
||||
|
||||
git_remote_free(origin);
|
||||
}
|
||||
@ -451,3 +440,91 @@ int git_clone_init_options(git_clone_options *opts, unsigned int version)
|
||||
opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *repository_base(git_repository *repo)
|
||||
{
|
||||
if (git_repository_is_bare(repo))
|
||||
return git_repository_path(repo);
|
||||
|
||||
return git_repository_workdir(repo);
|
||||
}
|
||||
|
||||
static bool can_link(const char *src, const char *dst, int link)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
return false;
|
||||
#else
|
||||
|
||||
struct stat st_src, st_dst;
|
||||
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
if (p_stat(src, &st_src) < 0)
|
||||
return false;
|
||||
|
||||
if (p_stat(dst, &st_dst) < 0)
|
||||
return false;
|
||||
|
||||
return st_src.st_dev == st_dst.st_dev;
|
||||
#endif
|
||||
}
|
||||
|
||||
int git_clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
|
||||
{
|
||||
int error, flags;
|
||||
git_repository *src;
|
||||
git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
|
||||
git_buf reflog_message = GIT_BUF_INIT;
|
||||
|
||||
assert(repo && remote);
|
||||
|
||||
if (!git_repository_is_empty(repo)) {
|
||||
giterr_set(GITERR_INVALID, "the repository is not empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's figure out what path we should use for the source
|
||||
* repo, if it's not rooted, the path should be relative to
|
||||
* the repository's worktree/gitdir.
|
||||
*/
|
||||
if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
|
||||
return error;
|
||||
|
||||
/* Copy .git/objects/ from the source to the target */
|
||||
if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
|
||||
git_buf_free(&src_path);
|
||||
return error;
|
||||
}
|
||||
|
||||
git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
|
||||
git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
|
||||
if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (can_link(git_repository_path(src), git_repository_path(repo), link))
|
||||
flags |= GIT_CPDIR_LINK_FILES;
|
||||
|
||||
if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
|
||||
flags, GIT_OBJECT_DIR_MODE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
|
||||
|
||||
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
|
||||
goto cleanup;
|
||||
|
||||
error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&reflog_message);
|
||||
git_buf_free(&src_path);
|
||||
git_buf_free(&src_odb);
|
||||
git_buf_free(&dst_odb);
|
||||
git_repository_free(src);
|
||||
return error;
|
||||
}
|
||||
|
12
src/clone.h
Normal file
12
src/clone.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#ifndef INCLUDE_clone_h__
|
||||
#define INCLUDE_clone_h__
|
||||
|
||||
extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
|
||||
|
||||
#endif
|
14
src/config.c
14
src/config.c
@ -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 error;
|
||||
int error = 0;
|
||||
size_t i;
|
||||
file_internal *internal;
|
||||
git_config *config;
|
||||
@ -153,19 +153,19 @@ int git_config_snapshot(git_config **out, git_config *in)
|
||||
git_config_backend *b;
|
||||
|
||||
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) {
|
||||
b->free(b);
|
||||
goto on_error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*out = config;
|
||||
return error;
|
||||
if (error < 0)
|
||||
git_config_free(config);
|
||||
else
|
||||
*out = config;
|
||||
|
||||
on_error:
|
||||
git_config_free(config);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -76,4 +76,10 @@ extern int git_config__get_bool_force(
|
||||
extern int git_config__get_int_force(
|
||||
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
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "repository.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "vector.h"
|
||||
#include "filter.h"
|
||||
#include "repository.h"
|
||||
|
||||
struct map_data {
|
||||
const char *cvar_name;
|
||||
@ -51,6 +51,12 @@ static git_cvar_map _cvar_map_autocrlf[] = {
|
||||
{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
|
||||
*/
|
||||
@ -68,33 +74,39 @@ static struct map_data _cvar_maps[] = {
|
||||
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
|
||||
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_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)
|
||||
{
|
||||
*out = repo->cvar_cache[(int)cvar];
|
||||
|
||||
if (*out == GIT_CVAR_NOT_CACHED) {
|
||||
struct map_data *data = &_cvar_maps[(int)cvar];
|
||||
git_config *config;
|
||||
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;
|
||||
|
||||
repo->cvar_cache[(int)cvar] = *out;
|
||||
|
@ -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)
|
||||
return res;
|
||||
|
||||
git_mutex_init(&b->header.values_mutex);
|
||||
git_array_init(b->readers);
|
||||
reader = git_array_alloc(b->readers);
|
||||
if (!reader) {
|
||||
@ -313,6 +312,7 @@ static int config__refresh(git_config_backend *cfg)
|
||||
goto out;
|
||||
|
||||
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)
|
||||
goto out;
|
||||
@ -327,7 +327,8 @@ static int config__refresh(git_config_backend *cfg)
|
||||
|
||||
out:
|
||||
refcounted_strmap_free(values);
|
||||
git_buf_free(&reader->buffer);
|
||||
if (reader)
|
||||
git_buf_free(&reader->buffer);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -344,8 +345,8 @@ static int config_refresh(git_config_backend *cfg)
|
||||
&reader->buffer, reader->file_path,
|
||||
&reader->file_mtime, &reader->file_size, &updated);
|
||||
|
||||
if (error < 0)
|
||||
return (error == GIT_ENOTFOUND) ? 0 : error;
|
||||
if (error < 0 && error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
|
||||
if (updated)
|
||||
any_updated = 1;
|
||||
@ -373,6 +374,7 @@ static void backend_free(git_config_backend *_backend)
|
||||
|
||||
git__free(backend->file_path);
|
||||
refcounted_strmap_free(backend->header.values);
|
||||
git_mutex_free(&backend->header.values_mutex);
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
@ -684,6 +686,7 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
|
||||
GITERR_CHECK_ALLOC(backend);
|
||||
|
||||
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
|
||||
git_mutex_init(&backend->header.values_mutex);
|
||||
|
||||
backend->file_path = git__strdup(path);
|
||||
GITERR_CHECK_ALLOC(backend->file_path);
|
||||
@ -756,6 +759,7 @@ static void backend_readonly_free(git_config_backend *_backend)
|
||||
return;
|
||||
|
||||
refcounted_strmap_free(backend->header.values);
|
||||
git_mutex_free(&backend->header.values_mutex);
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
@ -782,6 +786,7 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
|
||||
GITERR_CHECK_ALLOC(backend);
|
||||
|
||||
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
|
||||
git_mutex_init(&backend->header.values_mutex);
|
||||
|
||||
backend->snapshot_from = in;
|
||||
|
||||
|
@ -138,6 +138,10 @@ static int crlf_apply_to_odb(
|
||||
if (git_buf_text_gather_stats(&stats, from, false))
|
||||
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 (stats.cr != stats.crlf || stats.lf != stats.crlf) {
|
||||
switch (ca->safe_crlf) {
|
||||
|
59
src/diff.c
59
src/diff.c
@ -390,7 +390,7 @@ static int diff_list_apply_options(
|
||||
git_diff *diff,
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
git_config *cfg;
|
||||
git_config *cfg = NULL;
|
||||
git_repository *repo = diff->repo;
|
||||
git_pool *pool = &diff->pool;
|
||||
int val;
|
||||
@ -415,20 +415,20 @@ static int diff_list_apply_options(
|
||||
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
|
||||
@ -490,8 +490,6 @@ static int diff_list_apply_options(
|
||||
/* strdup prefix from pool so we're not dependent on external data */
|
||||
diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_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)) {
|
||||
const char *tmp_prefix = diff->opts.old_prefix;
|
||||
@ -499,7 +497,10 @@ static int diff_list_apply_options(
|
||||
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)
|
||||
@ -642,7 +643,6 @@ typedef struct {
|
||||
git_iterator *new_iter;
|
||||
const git_index_entry *oitem;
|
||||
const git_index_entry *nitem;
|
||||
git_buf ignore_prefix;
|
||||
} diff_in_progress;
|
||||
|
||||
#define MODE_BITS_MASK 0000777
|
||||
@ -858,24 +858,13 @@ static int handle_unmatched_new_item(
|
||||
/* check if this is a prefix of the other side */
|
||||
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
|
||||
|
||||
/* check if this is contained in an ignored parent directory */
|
||||
if (git_buf_len(&info->ignore_prefix)) {
|
||||
if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
else
|
||||
git_buf_clear(&info->ignore_prefix);
|
||||
}
|
||||
/* update delta_type if this item is ignored */
|
||||
if (git_iterator_current_is_ignored(info->new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
if (nitem->mode == GIT_FILEMODE_TREE) {
|
||||
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 */
|
||||
recurse_into_dir = contains_oitem ||
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
@ -952,27 +941,12 @@ static int handle_unmatched_new_item(
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two checks are effectively reversed --
|
||||
* i.e. when an file contained in an ignored directory is explicitly
|
||||
* ignored, it shows up as an ignored file in the diff list, even though
|
||||
* other untracked files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this seems odd. If the directory is ignored and the file is
|
||||
* untracked, we should skip it consistently, regardless of whether it
|
||||
* happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, reverse the following two if checks
|
||||
* so that individual file ignores are checked before container
|
||||
* directory exclusions are used to skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED &&
|
||||
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 */
|
||||
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)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
@ -1089,7 +1063,6 @@ int git_diff__from_iterators(
|
||||
info.repo = repo;
|
||||
info.old_iter = old_iter;
|
||||
info.new_iter = new_iter;
|
||||
git_buf_init(&info.ignore_prefix, 0);
|
||||
|
||||
/* make iterators have matching icase behavior */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
|
||||
@ -1144,8 +1117,6 @@ cleanup:
|
||||
else
|
||||
git_diff_free(diff);
|
||||
|
||||
git_buf_free(&info.ignore_prefix);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -233,17 +233,17 @@ static int git_diff_driver_load(
|
||||
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 (git_repository_config_snapshot(&cfg, repo) < 0) {
|
||||
giterr_clear();
|
||||
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)
|
||||
goto done;
|
||||
|
||||
@ -397,7 +397,11 @@ void git_diff_driver_update_options(
|
||||
int git_diff_driver_content_is_binary(
|
||||
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);
|
||||
|
||||
|
@ -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;
|
||||
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";
|
||||
char *ptr;
|
||||
char *scan, *end;
|
||||
int error;
|
||||
|
||||
old_data = old ? git_blob_rawcontent(old) : NULL;
|
||||
new_data = new ? git_blob_rawcontent(new) : NULL;
|
||||
|
||||
old_data_len = old ? (size_t)git_blob_rawsize(old) : 0;
|
||||
new_data_len = new ? (size_t)git_blob_rawsize(new) : 0;
|
||||
old_data_len = old ? git_blob_rawsize(old) : 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;
|
||||
inflated_len = new_data_len;
|
||||
inflated_len = (unsigned long)new_data_len;
|
||||
|
||||
if ((error = git_zstream_deflatebuf(
|
||||
&deflate, new_data, new_data_len)) < 0)
|
||||
out, new_data, (size_t)new_data_len)) < 0)
|
||||
goto done;
|
||||
|
||||
if (old && new) {
|
||||
void *delta_data;
|
||||
/* The git_delta function accepts unsigned long only */
|
||||
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,
|
||||
new_data_len, &delta_data_len, deflate.size);
|
||||
if (old && new) {
|
||||
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) {
|
||||
error = git_zstream_deflatebuf(&delta, delta_data, delta_data_len);
|
||||
free(delta_data);
|
||||
error = git_zstream_deflatebuf(
|
||||
&delta, delta_data, (size_t)delta_data_len);
|
||||
|
||||
git__free(delta_data);
|
||||
|
||||
if (error < 0)
|
||||
goto done;
|
||||
@ -326,18 +339,20 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_printf(pi->buf, "%s %" PRIuZ "\n", out_type, inflated_len);
|
||||
git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len);
|
||||
pi->line.num_lines++;
|
||||
|
||||
for (ptr = out->ptr, remain = out->size; remain > 0; ) {
|
||||
size_t chunk_len = (52 < remain) ? 52 : remain;
|
||||
for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) {
|
||||
size_t chunk_len = end - scan;
|
||||
if (chunk_len > 52)
|
||||
chunk_len = 52;
|
||||
|
||||
if (chunk_len <= 26)
|
||||
git_buf_putc(pi->buf, chunk_len + 'A' - 1);
|
||||
git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1);
|
||||
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');
|
||||
|
||||
if (git_buf_oom(pi->buf)) {
|
||||
@ -345,8 +360,7 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
|
||||
goto done;
|
||||
}
|
||||
|
||||
ptr += chunk_len;
|
||||
remain -= chunk_len;
|
||||
scan += chunk_len;
|
||||
pi->line.num_lines++;
|
||||
}
|
||||
|
||||
@ -365,26 +379,33 @@ static int diff_print_patch_file_binary(
|
||||
git_blob *old = NULL, *new = NULL;
|
||||
const git_oid *old_id, *new_id;
|
||||
int error;
|
||||
size_t pre_binary_size;
|
||||
|
||||
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) {
|
||||
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");
|
||||
}
|
||||
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
|
||||
goto noshow;
|
||||
|
||||
pre_binary_size = pi->buf->size;
|
||||
git_buf_printf(pi->buf, "GIT binary patch\n");
|
||||
pi->line.num_lines++;
|
||||
|
||||
old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_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) ||
|
||||
(new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) ||
|
||||
(error = print_binary_hunk(pi, old, new)) < 0 ||
|
||||
if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0)
|
||||
goto done;
|
||||
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 = 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++;
|
||||
|
||||
@ -393,6 +414,12 @@ done:
|
||||
git_blob_free(new);
|
||||
|
||||
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(
|
||||
|
@ -740,9 +740,11 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
return error;
|
||||
|
||||
/* 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);
|
||||
else {
|
||||
} else {
|
||||
mode_t usemode = from_st.st_mode;
|
||||
|
||||
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
|
||||
|
@ -173,6 +173,7 @@ extern int git_futils_cp(
|
||||
* - 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 has exec bits set) for target.
|
||||
* - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
|
||||
@ -181,6 +182,7 @@ typedef enum {
|
||||
GIT_CPDIR_OVERWRITE = (1u << 3),
|
||||
GIT_CPDIR_CHMOD_DIRS = (1u << 4),
|
||||
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
|
||||
GIT_CPDIR_LINK_FILES = (1u << 6),
|
||||
} git_futils_cpdir_flags;
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include "common.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 {
|
||||
GIT_CRLF_GUESS = -1,
|
||||
GIT_CRLF_BINARY = 0,
|
||||
|
@ -5,6 +5,40 @@
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains code originally derrived from OpenBSD fnmatch.c
|
||||
*
|
||||
* Copyright (c) 1989, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Guido van Rossum.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
|
||||
* Compares a filename or pathname to a pattern.
|
||||
|
@ -1,8 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* 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__
|
||||
#define INCLUDE_fnmatch__compat_h__
|
||||
|
@ -76,7 +76,7 @@ static void git__shutdown(void)
|
||||
#if defined(GIT_THREADS) && defined(GIT_WIN32)
|
||||
|
||||
static DWORD _tls_index;
|
||||
static DWORD _mutex = 0;
|
||||
static volatile LONG _mutex = 0;
|
||||
|
||||
static int synchronized_threads_init()
|
||||
{
|
||||
|
67
src/ignore.c
67
src/ignore.c
@ -248,14 +248,15 @@ void git_ignore__free(git_ignores *ignores)
|
||||
}
|
||||
|
||||
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;
|
||||
git_attr_fnmatch *match;
|
||||
|
||||
git_vector_rforeach(&file->rules, j, match) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -264,34 +265,34 @@ static bool ignore_lookup_in_rules(
|
||||
}
|
||||
|
||||
int git_ignore__lookup(
|
||||
git_ignores *ignores, const char *pathname, int *ignored)
|
||||
int *out, git_ignores *ignores, const char *pathname)
|
||||
{
|
||||
unsigned int i;
|
||||
git_attr_file *file;
|
||||
git_attr_path path;
|
||||
|
||||
*out = GIT_IGNORE_NOTFOUND;
|
||||
|
||||
if (git_attr_path__init(
|
||||
&path, pathname, git_repository_workdir(ignores->repo)) < 0)
|
||||
return -1;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* next process files in the path */
|
||||
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;
|
||||
}
|
||||
|
||||
/* last process global ignores */
|
||||
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;
|
||||
}
|
||||
|
||||
*ignored = 0;
|
||||
|
||||
cleanup:
|
||||
git_attr_path__free(&path);
|
||||
return 0;
|
||||
@ -335,8 +336,6 @@ int git_ignore_path_is_ignored(
|
||||
int error;
|
||||
const char *workdir;
|
||||
git_attr_path path;
|
||||
char *tail, *end;
|
||||
bool full_is_dir;
|
||||
git_ignores ignores;
|
||||
unsigned int i;
|
||||
git_attr_file *file;
|
||||
@ -345,55 +344,42 @@ int git_ignore_path_is_ignored(
|
||||
|
||||
workdir = repo ? git_repository_workdir(repo) : NULL;
|
||||
|
||||
if ((error = git_attr_path__init(&path, pathname, workdir)) < 0)
|
||||
return error;
|
||||
memset(&path, 0, sizeof(path));
|
||||
memset(&ignores, 0, sizeof(ignores));
|
||||
|
||||
tail = path.path;
|
||||
end = &path.full.ptr[path.full.size];
|
||||
full_is_dir = path.is_dir;
|
||||
if ((error = git_attr_path__init(&path, pathname, workdir)) < 0 ||
|
||||
(error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
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 */
|
||||
if (ignore_lookup_in_rules(ignores.ign_internal, &path, ignored))
|
||||
if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path))
|
||||
goto cleanup;
|
||||
|
||||
/* next process files in the path */
|
||||
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;
|
||||
}
|
||||
|
||||
/* last process global ignores */
|
||||
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;
|
||||
}
|
||||
|
||||
/* if we found no rules before reaching the end, we're done */
|
||||
if (tail == end)
|
||||
/* move up one directory */
|
||||
if (path.basename == path.path)
|
||||
break;
|
||||
path.basename[-1] = '\0';
|
||||
while (path.basename > path.path && *path.basename != '/')
|
||||
path.basename--;
|
||||
if (path.basename > path.path)
|
||||
path.basename++;
|
||||
path.is_dir = 1;
|
||||
|
||||
/* now add this directory to list of ignores */
|
||||
if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
|
||||
if ((error = git_ignore__pop_dir(&ignores)) < 0)
|
||||
break;
|
||||
|
||||
/* reinstate divider in path */
|
||||
*tail = '/';
|
||||
while (*tail == '/') tail++;
|
||||
}
|
||||
|
||||
*ignored = 0;
|
||||
@ -404,7 +390,6 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int git_ignore__check_pathspec_for_exact_ignores(
|
||||
git_repository *repo,
|
||||
git_vector *vspec,
|
||||
|
@ -42,7 +42,14 @@ extern int git_ignore__pop_dir(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
|
||||
* pathspec that contains an exact match to an ignored file (provided
|
||||
|
14
src/index.c
14
src/index.c
@ -1104,6 +1104,15 @@ int git_index_remove_bypath(git_index *index, const char *path)
|
||||
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)
|
||||
{
|
||||
git_index_entry *entry = NULL;
|
||||
@ -1111,6 +1120,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
|
||||
|
||||
assert(index && source_entry && source_entry->path);
|
||||
|
||||
if (!valid_filemode(source_entry->mode)) {
|
||||
giterr_set(GITERR_INDEX, "invalid filemode");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
|
||||
(ret = index_insert(index, &entry, 1)) < 0)
|
||||
return ret;
|
||||
|
160
src/indexer.c
160
src/indexer.c
@ -34,7 +34,6 @@ struct git_indexer {
|
||||
have_delta :1;
|
||||
struct git_pack_header hdr;
|
||||
struct git_pack_file *pack;
|
||||
git_filebuf pack_file;
|
||||
unsigned int mode;
|
||||
git_off_t off;
|
||||
git_off_t entry_start;
|
||||
@ -67,33 +66,18 @@ const git_oid *git_indexer_hash(const git_indexer *idx)
|
||||
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)
|
||||
{
|
||||
int error;
|
||||
git_map map;
|
||||
|
||||
if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
|
||||
return error;
|
||||
|
||||
memcpy(hdr, map.data, sizeof(*hdr));
|
||||
p_munmap(&map);
|
||||
|
||||
/* Verify we recognize this pack file format. */
|
||||
if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to read in pack header");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
|
||||
giterr_set(GITERR_INDEXER, "Wrong pack signature");
|
||||
return -1;
|
||||
@ -124,9 +108,9 @@ int git_indexer_new(
|
||||
void *progress_payload)
|
||||
{
|
||||
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";
|
||||
int error;
|
||||
int error, fd = -1;
|
||||
|
||||
idx = git__calloc(1, sizeof(git_indexer));
|
||||
GITERR_CHECK_ALLOC(idx);
|
||||
@ -140,19 +124,30 @@ int git_indexer_new(
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_filebuf_open(&idx->pack_file, path.ptr,
|
||||
GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
|
||||
idx->mode);
|
||||
fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode);
|
||||
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)
|
||||
goto cleanup;
|
||||
|
||||
idx->pack->mwf.fd = fd;
|
||||
if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
*out = idx;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
if (fd != -1)
|
||||
p_close(fd);
|
||||
|
||||
git_buf_free(&path);
|
||||
git_filebuf_cleanup(&idx->pack_file);
|
||||
git_buf_free(&tmp_path);
|
||||
git__free(idx);
|
||||
return -1;
|
||||
}
|
||||
@ -429,6 +424,42 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
|
||||
idx->inbuf_len += size - to_expell;
|
||||
}
|
||||
|
||||
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 error = -1;
|
||||
@ -440,22 +471,13 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran
|
||||
|
||||
processed = stats->indexed_objects;
|
||||
|
||||
if ((error = git_filebuf_write(&idx->pack_file, data, size)) < 0)
|
||||
if ((error = append_to_pack(idx, data, size)) < 0)
|
||||
return error;
|
||||
|
||||
hash_partially(idx, data, (int)size);
|
||||
|
||||
/* Make sure we set the new size of the pack */
|
||||
if (idx->opened_pack) {
|
||||
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;
|
||||
}
|
||||
idx->pack->mwf.size += size;
|
||||
|
||||
if (!idx->parsed_header) {
|
||||
unsigned int total_objects;
|
||||
@ -616,17 +638,10 @@ static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
|
||||
* Rewind the packfile by the trailer, as we might need to fix the
|
||||
* 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;
|
||||
git_mwindow_free_all(&idx->pack->mwf);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
return -1;
|
||||
@ -657,7 +673,9 @@ static int inject_object(git_indexer *idx, git_oid *id)
|
||||
|
||||
/* Write out the object header */
|
||||
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;
|
||||
entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
|
||||
|
||||
@ -665,13 +683,16 @@ static int inject_object(git_indexer *idx, git_oid *id)
|
||||
goto cleanup;
|
||||
|
||||
/* 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;
|
||||
entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
|
||||
git_buf_free(&buf);
|
||||
|
||||
/* 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;
|
||||
|
||||
idx->pack->mwf.size += GIT_OID_RAWSZ;
|
||||
@ -703,7 +724,7 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
|
||||
size_t size;
|
||||
git_otype type;
|
||||
git_mwindow *w = NULL;
|
||||
git_off_t curpos;
|
||||
git_off_t curpos = 0;
|
||||
unsigned char *base_info;
|
||||
unsigned int left = 0;
|
||||
git_oid base;
|
||||
@ -817,19 +838,12 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
|
||||
ctx = &idx->trailer;
|
||||
|
||||
git_hash_ctx_init(ctx);
|
||||
git_mwindow_free_all(mwf);
|
||||
|
||||
|
||||
/* Update the header to include the numer of local objects we injected */
|
||||
idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
|
||||
if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
|
||||
giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
|
||||
if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
|
||||
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
|
||||
@ -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
|
||||
* calculation.
|
||||
*/
|
||||
git_mwindow_free_all(mwf);
|
||||
idx->inbuf_len = 0;
|
||||
while (hashed < mwf->size) {
|
||||
ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
|
||||
@ -906,13 +921,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
|
||||
return -1;
|
||||
|
||||
git_hash_final(&trailer_hash, &idx->trailer);
|
||||
if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
|
||||
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;
|
||||
}
|
||||
write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
|
||||
}
|
||||
|
||||
git_vector_sort(&idx->objects);
|
||||
@ -995,14 +1004,18 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
|
||||
|
||||
git_mwindow_free_all(&idx->pack->mwf);
|
||||
/* 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;
|
||||
|
||||
if (index_path(&filename, idx, ".pack") < 0)
|
||||
goto on_error;
|
||||
|
||||
/* And don't forget to rename the packfile to its new place. */
|
||||
if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0)
|
||||
return -1;
|
||||
p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
|
||||
|
||||
git_buf_free(&filename);
|
||||
return 0;
|
||||
@ -1022,7 +1035,7 @@ void git_indexer_free(git_indexer *idx)
|
||||
|
||||
git_vector_free_deep(&idx->objects);
|
||||
|
||||
if (idx->pack) {
|
||||
if (idx->pack && idx->pack->idx_cache) {
|
||||
struct git_pack_entry *pentry;
|
||||
kh_foreach_value(
|
||||
idx->pack->idx_cache, pentry, { git__free(pentry); });
|
||||
@ -1032,6 +1045,5 @@ void git_indexer_free(git_indexer *idx)
|
||||
|
||||
git_vector_free_deep(&idx->deltas);
|
||||
git_packfile_free(idx->pack);
|
||||
git_filebuf_cleanup(&idx->pack_file);
|
||||
git__free(idx);
|
||||
}
|
||||
|
@ -897,6 +897,7 @@ struct fs_iterator_frame {
|
||||
fs_iterator_frame *next;
|
||||
git_vector entries;
|
||||
size_t index;
|
||||
int is_ignored;
|
||||
};
|
||||
|
||||
typedef struct fs_iterator fs_iterator;
|
||||
@ -1290,16 +1291,28 @@ GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
|
||||
|
||||
static int workdir_iterator__enter_dir(fs_iterator *fi)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)fi;
|
||||
fs_iterator_frame *ff = fi->stack;
|
||||
size_t pos;
|
||||
git_path_with_stat *entry;
|
||||
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) {
|
||||
workdir_iterator *wi = (workdir_iterator *)fi;
|
||||
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]);
|
||||
}
|
||||
|
||||
@ -1342,7 +1355,7 @@ static int workdir_iterator__update_entry(fs_iterator *fi)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
/* reset is_ignored since we haven't checked yet */
|
||||
wi->is_ignored = -1;
|
||||
wi->is_ignored = GIT_IGNORE_UNCHECKED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1487,6 +1500,19 @@ int git_iterator_current_parent_tree(
|
||||
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)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)iter;
|
||||
@ -1494,14 +1520,22 @@ bool git_iterator_current_is_ignored(git_iterator *iter)
|
||||
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
return false;
|
||||
|
||||
if (wi->is_ignored != -1)
|
||||
return (bool)(wi->is_ignored != 0);
|
||||
if (wi->is_ignored != GIT_IGNORE_UNCHECKED)
|
||||
return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
|
||||
|
||||
if (git_ignore__lookup(
|
||||
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
|
||||
wi->is_ignored = true;
|
||||
workdir_iterator_update_is_ignored(wi);
|
||||
|
||||
return (bool)wi->is_ignored;
|
||||
return (bool)(wi->is_ignored == GIT_IGNORE_TRUE);
|
||||
}
|
||||
|
||||
bool git_iterator_current_tree_is_ignored(git_iterator *iter)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)iter;
|
||||
|
||||
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
return false;
|
||||
|
||||
return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE);
|
||||
}
|
||||
|
||||
int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
|
||||
@ -1549,10 +1583,8 @@ int git_iterator_advance_over_with_status(
|
||||
return error;
|
||||
|
||||
if (!S_ISDIR(entry->mode)) {
|
||||
if (git_ignore__lookup(
|
||||
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
|
||||
wi->is_ignored = true;
|
||||
if (wi->is_ignored)
|
||||
workdir_iterator_update_is_ignored(wi);
|
||||
if (wi->is_ignored == GIT_IGNORE_TRUE)
|
||||
*status = GIT_ITERATOR_STATUS_IGNORED;
|
||||
return git_iterator_advance(entryptr, iter);
|
||||
}
|
||||
@ -1564,14 +1596,12 @@ int git_iterator_advance_over_with_status(
|
||||
|
||||
/* scan inside directory looking for a non-ignored item */
|
||||
while (entry && !iter->prefixcomp(entry->path, base)) {
|
||||
if (git_ignore__lookup(
|
||||
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
|
||||
wi->is_ignored = true;
|
||||
workdir_iterator_update_is_ignored(wi);
|
||||
|
||||
/* if we found an explicitly ignored item, then update from
|
||||
* EMPTY to IGNORED
|
||||
*/
|
||||
if (wi->is_ignored)
|
||||
if (wi->is_ignored == GIT_IGNORE_TRUE)
|
||||
*status = GIT_ITERATOR_STATUS_IGNORED;
|
||||
else if (S_ISDIR(entry->mode)) {
|
||||
error = git_iterator_advance_into(&entry, iter);
|
||||
@ -1580,7 +1610,7 @@ int git_iterator_advance_over_with_status(
|
||||
continue;
|
||||
else if (error == GIT_ENOTFOUND) {
|
||||
error = 0;
|
||||
wi->is_ignored = true; /* mark empty directories as ignored */
|
||||
wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */
|
||||
} else
|
||||
break; /* real error, stop here */
|
||||
} else {
|
||||
|
@ -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_tree_is_ignored(git_iterator *iter);
|
||||
|
||||
extern int git_iterator_cmp(
|
||||
git_iterator *iter, const char *path_prefix);
|
||||
|
||||
|
@ -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_munmap(git_map *map);
|
||||
extern long git__page_size(void);
|
||||
|
||||
#endif /* INCLUDE_map_h__ */
|
||||
|
61
src/merge.c
61
src/merge.c
@ -2564,8 +2564,42 @@ done:
|
||||
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(
|
||||
git_merge_analysis_t *out,
|
||||
git_merge_analysis_t *analysis_out,
|
||||
git_merge_preference_t *preference_out,
|
||||
git_repository *repo,
|
||||
const git_merge_head **their_heads,
|
||||
size_t their_heads_len)
|
||||
@ -2573,14 +2607,7 @@ int git_merge_analysis(
|
||||
git_merge_head *ancestor_head = NULL, *our_head = NULL;
|
||||
int error = 0;
|
||||
|
||||
assert(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;
|
||||
}
|
||||
assert(analysis_out && preference_out && repo && their_heads);
|
||||
|
||||
if (their_heads_len != 1) {
|
||||
giterr_set(GITERR_MERGE, "Can only merge a single branch");
|
||||
@ -2588,20 +2615,30 @@ int git_merge_analysis(
|
||||
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)
|
||||
goto done;
|
||||
|
||||
/* 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))
|
||||
*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. */
|
||||
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. */
|
||||
else
|
||||
*out = GIT_MERGE_ANALYSIS_NORMAL;
|
||||
*analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
|
||||
|
||||
done:
|
||||
git_merge_head_free(ancestor_head);
|
||||
|
@ -21,7 +21,7 @@ static size_t line_length_without_trailing_spaces(const char *line, size_t len)
|
||||
|
||||
/* Greatly inspired from git.git "stripspace" */
|
||||
/* 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);
|
||||
|
||||
@ -40,7 +40,7 @@ int git_message_prettify(git_buf *message_out, const char *message, int strip_co
|
||||
line_length = message_len - i;
|
||||
}
|
||||
|
||||
if (strip_comments && line_length && message[i] == '#')
|
||||
if (strip_comments && line_length && message[i] == comment_char)
|
||||
continue;
|
||||
|
||||
rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);
|
||||
|
@ -206,6 +206,8 @@ static int gitno_ssl_teardown(gitno_ssl *ssl)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Match host names according to RFC 2818 rules */
|
||||
int gitno__match_host(const char *pattern, const char *host)
|
||||
{
|
||||
@ -256,6 +258,8 @@ static int check_host_name(const char *name, const char *host)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GIT_SSL
|
||||
|
||||
static int verify_server_cert(gitno_ssl *ssl, const char *host)
|
||||
{
|
||||
X509 *cert;
|
||||
|
@ -783,6 +783,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
|
||||
return error;
|
||||
}
|
||||
|
||||
giterr_clear();
|
||||
if ((object = odb_object__alloc(id, &raw)) == NULL)
|
||||
return -1;
|
||||
|
||||
|
292
src/pack.c
292
src/pack.c
@ -83,16 +83,12 @@ static void cache_free(git_pack_cache *cache)
|
||||
}
|
||||
|
||||
git_offmap_free(cache->entries);
|
||||
git_mutex_free(&cache->lock);
|
||||
cache->entries = NULL;
|
||||
}
|
||||
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
}
|
||||
|
||||
static int cache_init(git_pack_cache *cache)
|
||||
{
|
||||
memset(cache, 0, sizeof(*cache));
|
||||
|
||||
cache->entries = git_offmap_alloc();
|
||||
GITERR_CHECK_ALLOC(cache->entries);
|
||||
|
||||
@ -514,72 +510,102 @@ int git_packfile_resolve_header(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int packfile_unpack_delta(
|
||||
git_rawobj *obj,
|
||||
struct git_pack_file *p,
|
||||
git_mwindow **w_curs,
|
||||
git_off_t *curpos,
|
||||
size_t delta_size,
|
||||
git_otype delta_type,
|
||||
git_off_t obj_offset)
|
||||
#define SMALL_STACK_SIZE 64
|
||||
|
||||
/**
|
||||
* Generate the chain of dependencies which we need to get to the
|
||||
* object at `off`. `chain` is used a stack, popping gives the right
|
||||
* order to apply deltas on. If an object is found in the pack's base
|
||||
* cache, we stop calculating there.
|
||||
*/
|
||||
static int pack_dependency_chain(git_dependency_chain *chain_out,
|
||||
git_pack_cache_entry **cached_out, git_off_t *cached_off,
|
||||
struct pack_chain_elem *small_stack, size_t *stack_sz,
|
||||
struct git_pack_file *p, git_off_t obj_offset)
|
||||
{
|
||||
git_off_t base_offset, base_key;
|
||||
git_rawobj base, delta;
|
||||
git_pack_cache_entry *cached = NULL;
|
||||
int error, found_base = 0;
|
||||
git_dependency_chain chain = GIT_ARRAY_INIT;
|
||||
git_mwindow *w_curs = NULL;
|
||||
git_off_t curpos = obj_offset, base_offset;
|
||||
int error = 0, use_heap = 0;
|
||||
size_t size, elem_pos;
|
||||
git_otype type;
|
||||
|
||||
base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
|
||||
git_mwindow_close(w_curs);
|
||||
if (base_offset == 0)
|
||||
return packfile_error("delta offset is zero");
|
||||
if (base_offset < 0) /* must actually be an error code */
|
||||
return (int)base_offset;
|
||||
elem_pos = 0;
|
||||
while (true) {
|
||||
struct pack_chain_elem *elem;
|
||||
git_pack_cache_entry *cached = NULL;
|
||||
|
||||
if (!p->bases.entries && (cache_init(&p->bases) < 0))
|
||||
return -1;
|
||||
/* if we have a base cached, we can stop here instead */
|
||||
if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
|
||||
*cached_out = cached;
|
||||
*cached_off = obj_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
base_key = base_offset; /* git_packfile_unpack modifies base_offset */
|
||||
if ((cached = cache_get(&p->bases, base_offset)) != NULL) {
|
||||
memcpy(&base, &cached->raw, sizeof(git_rawobj));
|
||||
found_base = 1;
|
||||
}
|
||||
/* if we run out of space on the small stack, use the array */
|
||||
if (elem_pos == SMALL_STACK_SIZE) {
|
||||
git_array_init_to_size(chain, elem_pos);
|
||||
GITERR_CHECK_ARRAY(chain);
|
||||
memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
|
||||
chain.size = elem_pos;
|
||||
use_heap = 1;
|
||||
}
|
||||
|
||||
if (!cached) { /* have to inflate it */
|
||||
error = git_packfile_unpack(&base, p, &base_offset);
|
||||
curpos = obj_offset;
|
||||
if (!use_heap) {
|
||||
elem = &small_stack[elem_pos];
|
||||
} else {
|
||||
elem = git_array_alloc(chain);
|
||||
if (!elem) {
|
||||
error = -1;
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
elem->base_key = obj_offset;
|
||||
|
||||
error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
|
||||
git_mwindow_close(&w_curs);
|
||||
|
||||
/*
|
||||
* TODO: git.git tries to load the base from other packfiles
|
||||
* or loose objects.
|
||||
*
|
||||
* We'll need to do this in order to support thin packs.
|
||||
*/
|
||||
if (error < 0)
|
||||
return error;
|
||||
goto on_error;
|
||||
|
||||
elem->offset = curpos;
|
||||
elem->size = size;
|
||||
elem->type = type;
|
||||
elem->base_key = obj_offset;
|
||||
|
||||
if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
|
||||
break;
|
||||
|
||||
base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
|
||||
git_mwindow_close(&w_curs);
|
||||
|
||||
if (base_offset == 0) {
|
||||
error = packfile_error("delta offset is zero");
|
||||
goto on_error;
|
||||
}
|
||||
if (base_offset < 0) { /* must actually be an error code */
|
||||
error = (int)base_offset;
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* we need to pass the pos *after* the delta-base bit */
|
||||
elem->offset = curpos;
|
||||
|
||||
/* go through the loop again, but with the new object */
|
||||
obj_offset = base_offset;
|
||||
elem_pos++;
|
||||
}
|
||||
|
||||
error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
|
||||
git_mwindow_close(w_curs);
|
||||
|
||||
if (error < 0) {
|
||||
if (!found_base)
|
||||
git__free(base.data);
|
||||
return error;
|
||||
}
|
||||
|
||||
obj->type = base.type;
|
||||
error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
|
||||
if (error < 0)
|
||||
goto on_error;
|
||||
|
||||
if (found_base)
|
||||
git_atomic_dec(&cached->refcount);
|
||||
else if (cache_add(&p->bases, &base, base_key) < 0)
|
||||
git__free(base.data);
|
||||
*stack_sz = elem_pos + 1;
|
||||
*chain_out = chain;
|
||||
return error;
|
||||
|
||||
on_error:
|
||||
git__free(delta.data);
|
||||
|
||||
return error; /* error set by git__delta_apply */
|
||||
git_array_clear(chain);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_packfile_unpack(
|
||||
@ -589,48 +615,138 @@ int git_packfile_unpack(
|
||||
{
|
||||
git_mwindow *w_curs = NULL;
|
||||
git_off_t curpos = *obj_offset;
|
||||
int error;
|
||||
|
||||
size_t size = 0;
|
||||
git_otype type;
|
||||
int error, free_base = 0;
|
||||
git_dependency_chain chain = GIT_ARRAY_INIT;
|
||||
struct pack_chain_elem *elem = NULL, *stack;
|
||||
git_pack_cache_entry *cached = NULL;
|
||||
struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
|
||||
size_t stack_size, elem_pos;
|
||||
git_otype base_type;
|
||||
|
||||
/*
|
||||
* TODO: optionally check the CRC on the packfile
|
||||
*/
|
||||
|
||||
error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
obj->data = NULL;
|
||||
obj->len = 0;
|
||||
obj->type = GIT_OBJ_BAD;
|
||||
|
||||
error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
|
||||
git_mwindow_close(&w_curs);
|
||||
/* let's point to the right stack */
|
||||
stack = chain.ptr ? chain.ptr : small_stack;
|
||||
|
||||
elem_pos = stack_size;
|
||||
if (cached) {
|
||||
memcpy(obj, &cached->raw, sizeof(git_rawobj));
|
||||
base_type = obj->type;
|
||||
elem_pos--; /* stack_size includes the base, which isn't actually there */
|
||||
} else {
|
||||
elem = &stack[--elem_pos];
|
||||
base_type = elem->type;
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
switch (type) {
|
||||
case GIT_OBJ_OFS_DELTA:
|
||||
case GIT_OBJ_REF_DELTA:
|
||||
error = packfile_unpack_delta(
|
||||
obj, p, &w_curs, &curpos,
|
||||
size, type, *obj_offset);
|
||||
break;
|
||||
goto cleanup;
|
||||
|
||||
switch (base_type) {
|
||||
case GIT_OBJ_COMMIT:
|
||||
case GIT_OBJ_TREE:
|
||||
case GIT_OBJ_BLOB:
|
||||
case GIT_OBJ_TAG:
|
||||
error = packfile_unpack_compressed(
|
||||
obj, p, &w_curs, &curpos,
|
||||
size, type);
|
||||
if (!cached) {
|
||||
curpos = elem->offset;
|
||||
error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type);
|
||||
git_mwindow_close(&w_curs);
|
||||
base_type = elem->type;
|
||||
}
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
break;
|
||||
|
||||
case GIT_OBJ_OFS_DELTA:
|
||||
case GIT_OBJ_REF_DELTA:
|
||||
error = packfile_error("dependency chain ends in a delta");
|
||||
goto cleanup;
|
||||
default:
|
||||
error = packfile_error("invalid packfile type in header");;
|
||||
break;
|
||||
error = packfile_error("invalid packfile type in header");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*obj_offset = curpos;
|
||||
/*
|
||||
* Finding the object we want a cached base element is
|
||||
* problematic, as we need to make sure we don't accidentally
|
||||
* give the caller the cached object, which it would then feel
|
||||
* free to free, so we need to copy the data.
|
||||
*/
|
||||
if (cached && stack_size == 1) {
|
||||
void *data = obj->data;
|
||||
obj->data = git__malloc(obj->len + 1);
|
||||
GITERR_CHECK_ALLOC(obj->data);
|
||||
memcpy(obj->data, data, obj->len + 1);
|
||||
git_atomic_dec(&cached->refcount);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* we now apply each consecutive delta until we run out */
|
||||
while (elem_pos > 0 && !error) {
|
||||
git_rawobj base, delta;
|
||||
|
||||
/*
|
||||
* We can now try to add the base to the cache, as
|
||||
* long as it's not already the cached one.
|
||||
*/
|
||||
if (!cached)
|
||||
free_base = !!cache_add(&p->bases, obj, elem->base_key);
|
||||
|
||||
elem = &stack[elem_pos - 1];
|
||||
curpos = elem->offset;
|
||||
error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
|
||||
git_mwindow_close(&w_curs);
|
||||
|
||||
if (error < 0)
|
||||
break;
|
||||
|
||||
/* the current object becomes the new base, on which we apply the delta */
|
||||
base = *obj;
|
||||
obj->data = NULL;
|
||||
obj->len = 0;
|
||||
obj->type = GIT_OBJ_BAD;
|
||||
|
||||
error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
|
||||
obj->type = base_type;
|
||||
/*
|
||||
* We usually don't want to free the base at this
|
||||
* point, as we put it into the cache in the previous
|
||||
* iteration. free_base lets us know that we got the
|
||||
* base object directly from the packfile, so we can free it.
|
||||
*/
|
||||
git__free(delta.data);
|
||||
if (free_base) {
|
||||
free_base = 0;
|
||||
git__free(base.data);
|
||||
}
|
||||
|
||||
if (cached) {
|
||||
git_atomic_dec(&cached->refcount);
|
||||
cached = NULL;
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
break;
|
||||
|
||||
elem_pos--;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (error < 0)
|
||||
git__free(obj->data);
|
||||
|
||||
if (elem)
|
||||
*obj_offset = elem->offset;
|
||||
|
||||
git_array_clear(chain);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -660,7 +776,7 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p,
|
||||
st = inflateInit(&obj->zstream);
|
||||
if (st != Z_OK) {
|
||||
git__free(obj);
|
||||
giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
|
||||
giterr_set(GITERR_ZLIB, "failed to init packfile stream");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -691,7 +807,7 @@ ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t
|
||||
written = len - obj->zstream.avail_out;
|
||||
|
||||
if (st != Z_OK && st != Z_STREAM_END) {
|
||||
giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
|
||||
giterr_set(GITERR_ZLIB, "error reading from the zlib stream");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -736,7 +852,7 @@ int packfile_unpack_compressed(
|
||||
st = inflateInit(&stream);
|
||||
if (st != Z_OK) {
|
||||
git__free(buffer);
|
||||
giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
|
||||
giterr_set(GITERR_ZLIB, "failed to init zlib stream on unpack");
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -763,7 +879,7 @@ int packfile_unpack_compressed(
|
||||
|
||||
if ((st != Z_STREAM_END) || stream.total_out != size) {
|
||||
git__free(buffer);
|
||||
giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
|
||||
giterr_set(GITERR_ZLIB, "error inflating zlib stream");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -862,6 +978,7 @@ void git_packfile_free(struct git_pack_file *p)
|
||||
git__free(p->bad_object_sha1);
|
||||
|
||||
git_mutex_free(&p->lock);
|
||||
git_mutex_free(&p->bases.lock);
|
||||
git__free(p);
|
||||
}
|
||||
|
||||
@ -997,6 +1114,11 @@ int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cache_init(&p->bases) < 0) {
|
||||
git__free(p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pack_out = p;
|
||||
|
||||
return 0;
|
||||
|
10
src/pack.h
10
src/pack.h
@ -17,6 +17,7 @@
|
||||
#include "mwindow.h"
|
||||
#include "odb.h"
|
||||
#include "oidmap.h"
|
||||
#include "array.h"
|
||||
|
||||
#define GIT_PACK_FILE_MODE 0444
|
||||
|
||||
@ -60,6 +61,15 @@ typedef struct git_pack_cache_entry {
|
||||
git_rawobj raw;
|
||||
} 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"
|
||||
|
||||
GIT__USE_OFFMAP;
|
||||
|
18
src/path.c
18
src/path.c
@ -1135,3 +1135,21 @@ int git_path_dirload_with_stat(
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
@ -207,6 +207,13 @@ int p_write(git_file fd, const void *buf, size_t cnt)
|
||||
|
||||
#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)
|
||||
{
|
||||
GIT_MMAP_VALIDATE(out, len, prot, flags);
|
||||
|
@ -73,6 +73,7 @@ extern int p_rename(const char *from, const char *to);
|
||||
#define p_rmdir(p) rmdir(p)
|
||||
#define p_chmod(p,m) chmod(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_send(s,b,l,f) send(s,b,l,f)
|
||||
typedef int GIT_SOCKET;
|
||||
|
@ -458,6 +458,7 @@ typedef struct {
|
||||
git_pool pool;
|
||||
git_vector loose;
|
||||
|
||||
git_sortedcache *cache;
|
||||
size_t loose_pos;
|
||||
size_t packed_pos;
|
||||
} refdb_fs_iter;
|
||||
@ -468,6 +469,7 @@ static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
|
||||
|
||||
git_vector_free(&iter->loose);
|
||||
git_pool_clear(&iter->pool);
|
||||
git_sortedcache_free(iter->cache);
|
||||
git__free(iter);
|
||||
}
|
||||
|
||||
@ -539,10 +541,14 @@ static int refdb_fs_backend__iterator_next(
|
||||
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)) {
|
||||
ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
|
||||
error = GIT_ITEROVER;
|
||||
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 */
|
||||
break;
|
||||
|
||||
@ -556,7 +562,6 @@ static int refdb_fs_backend__iterator_next(
|
||||
break;
|
||||
}
|
||||
|
||||
git_sortedcache_runlock(backend->refcache);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -579,10 +584,14 @@ static int refdb_fs_backend__iterator_next_name(
|
||||
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)) {
|
||||
ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
|
||||
error = GIT_ITEROVER;
|
||||
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 */
|
||||
break;
|
||||
|
||||
@ -596,7 +605,6 @@ static int refdb_fs_backend__iterator_next_name(
|
||||
break;
|
||||
}
|
||||
|
||||
git_sortedcache_runlock(backend->refcache);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -927,19 +935,15 @@ static int has_reflog(git_repository *repo, const char *name);
|
||||
/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */
|
||||
static int should_write_reflog(int *write, git_repository *repo, const char *name)
|
||||
{
|
||||
git_config *config;
|
||||
int error, logall, is_bare;
|
||||
int error, logall;
|
||||
|
||||
error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* 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)
|
||||
return error;
|
||||
|
||||
error = git_config_get_bool(&logall, config, "core.logallrefupdates");
|
||||
if (error < 0 && error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
if (logall == GIT_LOGALLREFUPDATES_UNSET)
|
||||
logall = !git_repository_is_bare(repo);
|
||||
|
||||
if (!logall) {
|
||||
*write = 0;
|
||||
|
48
src/refs.c
48
src/refs.c
@ -159,8 +159,7 @@ int git_reference_name_to_id(
|
||||
}
|
||||
|
||||
static int reference_normalize_for_repo(
|
||||
char *out,
|
||||
size_t out_size,
|
||||
git_refname_t out,
|
||||
git_repository *repo,
|
||||
const char *name)
|
||||
{
|
||||
@ -171,7 +170,7 @@ static int reference_normalize_for_repo(
|
||||
precompose)
|
||||
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(
|
||||
@ -180,7 +179,7 @@ int git_reference_lookup_resolved(
|
||||
const char *name,
|
||||
int max_nesting)
|
||||
{
|
||||
char scan_name[GIT_REFNAME_MAX];
|
||||
git_refname_t scan_name;
|
||||
git_ref_t scan_type;
|
||||
int error = 0, nesting;
|
||||
git_reference *ref = NULL;
|
||||
@ -197,8 +196,7 @@ int git_reference_lookup_resolved(
|
||||
|
||||
scan_type = GIT_REF_SYMBOLIC;
|
||||
|
||||
if ((error = reference_normalize_for_repo(
|
||||
scan_name, sizeof(scan_name), repo, name)) < 0)
|
||||
if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
@ -354,7 +352,7 @@ static int reference__create(
|
||||
const git_oid *old_id,
|
||||
const char *old_target)
|
||||
{
|
||||
char normalized[GIT_REFNAME_MAX];
|
||||
git_refname_t normalized;
|
||||
git_refdb *refdb;
|
||||
git_reference *ref = NULL;
|
||||
int error = 0;
|
||||
@ -365,7 +363,7 @@ static int reference__create(
|
||||
if (ref_out)
|
||||
*ref_out = NULL;
|
||||
|
||||
error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name);
|
||||
error = reference_normalize_for_repo(normalized, repo, name);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
@ -388,15 +386,14 @@ static int reference__create(
|
||||
return -1;
|
||||
}
|
||||
|
||||
ref = git_reference__alloc(name, oid, NULL);
|
||||
ref = git_reference__alloc(normalized, oid, NULL);
|
||||
} else {
|
||||
char normalized_target[GIT_REFNAME_MAX];
|
||||
git_refname_t normalized_target;
|
||||
|
||||
if ((error = git_reference__normalize_name_lax(
|
||||
normalized_target, sizeof(normalized_target), symbolic)) < 0)
|
||||
if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
|
||||
return error;
|
||||
|
||||
ref = git_reference__alloc_symbolic(name, normalized_target);
|
||||
ref = git_reference__alloc_symbolic(normalized, normalized_target);
|
||||
}
|
||||
|
||||
GITERR_CHECK_ALLOC(ref);
|
||||
@ -569,18 +566,14 @@ int git_reference_symbolic_set_target(
|
||||
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
|
||||
const git_signature *signature, const char *message)
|
||||
{
|
||||
unsigned int normalization_flags;
|
||||
char normalized[GIT_REFNAME_MAX];
|
||||
git_refname_t normalized;
|
||||
bool should_head_be_updated = false;
|
||||
int error = 0;
|
||||
|
||||
assert(ref && new_name && signature);
|
||||
|
||||
normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
|
||||
GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
|
||||
|
||||
if ((error = git_reference_normalize_name(
|
||||
normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
|
||||
if ((error = reference_normalize_for_repo(
|
||||
normalized, git_reference_owner(ref), new_name)) < 0)
|
||||
return error;
|
||||
|
||||
|
||||
@ -590,12 +583,12 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
|
||||
|
||||
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;
|
||||
|
||||
/* Update HEAD it was pointing to the reference being renamed */
|
||||
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");
|
||||
return error;
|
||||
}
|
||||
@ -1018,17 +1011,6 @@ cleanup:
|
||||
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)
|
||||
|
||||
int git_reference_cmp(
|
||||
|
@ -51,6 +51,8 @@
|
||||
|
||||
#define GIT_REFNAME_MAX 1024
|
||||
|
||||
typedef char git_refname_t[GIT_REFNAME_MAX];
|
||||
|
||||
struct git_reference {
|
||||
git_refdb *db;
|
||||
git_ref_t type;
|
||||
@ -66,7 +68,6 @@ struct git_reference {
|
||||
|
||||
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__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);
|
||||
|
222
src/remote.c
222
src/remote.c
@ -403,6 +403,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
|
||||
if (!optional_setting_found) {
|
||||
error = GIT_ENOTFOUND;
|
||||
giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -1304,13 +1305,14 @@ static int rename_remote_config_section(
|
||||
if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
|
||||
goto cleanup;
|
||||
if (new_name &&
|
||||
(git_buf_printf(&new_section_name, "remote.%s", new_name) < 0))
|
||||
goto cleanup;
|
||||
|
||||
error = git_config_rename_section(
|
||||
repo,
|
||||
git_buf_cstr(&old_section_name),
|
||||
git_buf_cstr(&new_section_name));
|
||||
new_name ? git_buf_cstr(&new_section_name) : NULL);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&old_section_name);
|
||||
@ -1743,3 +1745,217 @@ int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
|
||||
opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
|
||||
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(
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
|
||||
git_repository *repo;
|
||||
git_config *config;
|
||||
|
||||
if (repo_ptr)
|
||||
*repo_ptr = NULL;
|
||||
@ -458,23 +457,24 @@ int git_repository_open_ext(
|
||||
repo->path_repository = git_buf_detach(&path);
|
||||
GITERR_CHECK_ALLOC(repo->path_repository);
|
||||
|
||||
if ((error = git_repository_config_snapshot(&config, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
|
||||
repo->is_bare = 1;
|
||||
else if ((error = load_config_data(repo, config)) < 0 ||
|
||||
(error = load_workdir(repo, config, &parent)) < 0)
|
||||
{
|
||||
else {
|
||||
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_repository_free(repo);
|
||||
return error;
|
||||
}
|
||||
|
||||
git_config_free(config);
|
||||
if (!error)
|
||||
*repo_ptr = repo;
|
||||
git_buf_free(&parent);
|
||||
*repo_ptr = repo;
|
||||
return 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_open(git_repository **repo_out, const char *path)
|
||||
@ -1190,6 +1190,7 @@ static int repo_init_structure(
|
||||
bool external_tpl =
|
||||
((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
|
||||
mode_t dmode = pick_dir_mode(opts);
|
||||
bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
|
||||
|
||||
/* Hide the ".git" directory */
|
||||
#ifdef GIT_WIN32
|
||||
@ -1230,10 +1231,12 @@ static int repo_init_structure(
|
||||
default_template = true;
|
||||
}
|
||||
|
||||
if (tdir)
|
||||
error = git_futils_cp_r(tdir, repo_dir,
|
||||
GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
|
||||
GIT_CPDIR_SIMPLE_TO_MODE, dmode);
|
||||
if (tdir) {
|
||||
uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE;
|
||||
if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
|
||||
cpflags |= GIT_CPDIR_CHMOD_DIRS;
|
||||
error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
|
||||
}
|
||||
|
||||
git_buf_free(&template_buf);
|
||||
git_config_free(cfg);
|
||||
@ -1254,9 +1257,14 @@ static int repo_init_structure(
|
||||
* - only create files if no external template was specified
|
||||
*/
|
||||
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(
|
||||
tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
|
||||
tpl->path, repo_dir, dmode, mkdir_flags);
|
||||
}
|
||||
else if (!external_tpl) {
|
||||
const char *content = tpl->content;
|
||||
|
||||
|
@ -39,6 +39,7 @@ typedef enum {
|
||||
GIT_CVAR_ABBREV, /* core.abbrev */
|
||||
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
|
||||
GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
|
||||
GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
|
||||
GIT_CVAR_CACHE_MAX
|
||||
} git_cvar_cached;
|
||||
|
||||
@ -92,6 +93,9 @@ typedef enum {
|
||||
GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
|
||||
/* core.safecrlf */
|
||||
GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE,
|
||||
/* core.logallrefupdates */
|
||||
GIT_LOGALLREFUPDATES_UNSET = 2,
|
||||
GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
|
||||
} git_cvar_value;
|
||||
|
||||
/* internal repository init flags */
|
||||
|
14
src/tag.c
14
src/tag.c
@ -363,20 +363,22 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
|
||||
}
|
||||
|
||||
/* write the buffer */
|
||||
if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
|
||||
return -1;
|
||||
if ((error = git_odb_open_wstream(
|
||||
&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);
|
||||
|
||||
if (error < 0) {
|
||||
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_buf_free(&ref_name);
|
||||
|
@ -40,17 +40,29 @@ typedef struct {
|
||||
have_refs : 1;
|
||||
} 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)
|
||||
{
|
||||
const char peeled[] = "^{}";
|
||||
git_oid head_oid;
|
||||
git_reference *ref, *resolved;
|
||||
git_remote_head *head;
|
||||
git_oid obj_id;
|
||||
git_object *obj = NULL, *target = NULL;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
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) {
|
||||
git_reference_free(ref);
|
||||
if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
|
||||
/* This is actually okay. Empty repos often have a HEAD that
|
||||
* points to a nonexistent "refs/heads/master". */
|
||||
@ -60,17 +72,25 @@ static int add_ref(transport_local *t, const char *name)
|
||||
return error;
|
||||
}
|
||||
|
||||
git_oid_cpy(&obj_id, git_reference_target(resolved));
|
||||
git_reference_free(resolved);
|
||||
|
||||
head = git__calloc(1, sizeof(git_remote_head));
|
||||
GITERR_CHECK_ALLOC(head);
|
||||
|
||||
head->name = git__strdup(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) {
|
||||
git__free(head->name);
|
||||
git__free(head);
|
||||
free_head(head);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -103,8 +123,7 @@ static int add_ref(transport_local *t, const char *name)
|
||||
git_oid_cpy(&head->oid, git_object_id(target));
|
||||
|
||||
if ((error = git_vector_insert(&t->refs, head)) < 0) {
|
||||
git__free(head->name);
|
||||
git__free(head);
|
||||
free_head(head);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,27 +175,9 @@ on_error:
|
||||
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
|
||||
* 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(
|
||||
git_transport *transport,
|
||||
@ -203,7 +204,7 @@ static int local_connect(
|
||||
t->flags = flags;
|
||||
|
||||
/* '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);
|
||||
return error;
|
||||
}
|
||||
@ -367,7 +368,7 @@ static int local_push(
|
||||
size_t j;
|
||||
|
||||
/* '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);
|
||||
return error;
|
||||
}
|
||||
@ -626,10 +627,8 @@ static void local_free(git_transport *transport)
|
||||
size_t i;
|
||||
git_remote_head *head;
|
||||
|
||||
git_vector_foreach(&t->refs, i, head) {
|
||||
git__free(head->name);
|
||||
git__free(head);
|
||||
}
|
||||
git_vector_foreach(&t->refs, i, head)
|
||||
free_head(head);
|
||||
|
||||
git_vector_free(&t->refs);
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "git2.h"
|
||||
#include "smart.h"
|
||||
#include "refs.h"
|
||||
#include "refspec.h"
|
||||
|
||||
static int git_smart__recv_cb(gitno_buffer *buf)
|
||||
{
|
||||
@ -63,7 +64,7 @@ static int git_smart__set_callbacks(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_smart__update_heads(transport_smart *t)
|
||||
int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
|
||||
{
|
||||
size_t i;
|
||||
git_pkt *pkt;
|
||||
@ -74,6 +75,25 @@ int git_smart__update_heads(transport_smart *t)
|
||||
if (pkt->type != GIT_PKT_REF)
|
||||
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)
|
||||
return -1;
|
||||
}
|
||||
@ -81,6 +101,19 @@ int git_smart__update_heads(transport_smart *t)
|
||||
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(
|
||||
git_transport *transport,
|
||||
const char *url,
|
||||
@ -94,6 +127,7 @@ static int git_smart__connect(
|
||||
int error;
|
||||
git_pkt *pkt;
|
||||
git_pkt_ref *first;
|
||||
git_vector symrefs;
|
||||
git_smart_service_t service;
|
||||
|
||||
if (git_smart__reset_stream(t, true) < 0)
|
||||
@ -147,8 +181,11 @@ static int git_smart__connect(
|
||||
|
||||
first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
|
||||
|
||||
if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
|
||||
return error;
|
||||
|
||||
/* Detect capabilities */
|
||||
if (git_smart__detect_caps(first, &t->caps) < 0)
|
||||
if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
|
||||
return -1;
|
||||
|
||||
/* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
|
||||
@ -159,7 +196,9 @@ static int git_smart__connect(
|
||||
}
|
||||
|
||||
/* Keep a list of heads for _ls */
|
||||
git_smart__update_heads(t);
|
||||
git_smart__update_heads(t, &symrefs);
|
||||
|
||||
free_symrefs(&symrefs);
|
||||
|
||||
if (t->rpc && git_smart__reset_stream(t, false) < 0)
|
||||
return -1;
|
||||
@ -272,6 +311,18 @@ static int git_smart__close(git_transport *transport)
|
||||
unsigned int i;
|
||||
git_pkt *p;
|
||||
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);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#define GIT_CAP_DELETE_REFS "delete-refs"
|
||||
#define GIT_CAP_REPORT_STATUS "report-status"
|
||||
#define GIT_CAP_THIN_PACK "thin-pack"
|
||||
#define GIT_CAP_SYMREF "symref"
|
||||
|
||||
enum git_pkt_type {
|
||||
GIT_PKT_CMD,
|
||||
@ -154,7 +155,7 @@ typedef struct {
|
||||
|
||||
/* smart_protocol.c */
|
||||
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__negotiate_fetch(
|
||||
@ -174,7 +175,7 @@ int git_smart__download_pack(
|
||||
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
|
||||
int git_smart__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 */
|
||||
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)
|
||||
if (pkt->type == GIT_PKT_REF) {
|
||||
git_pkt_ref *p = (git_pkt_ref *) pkt;
|
||||
git__free(p->head.name);
|
||||
git__free(p->head.symref_target);
|
||||
}
|
||||
|
||||
if (pkt->type == GIT_PKT_OK) {
|
||||
|
@ -26,17 +26,16 @@ int git_smart__store_refs(transport_smart *t, int flushes)
|
||||
int error, flush = 0, recvd;
|
||||
const char *line_end = NULL;
|
||||
git_pkt *pkt = NULL;
|
||||
git_pkt_ref *ref;
|
||||
size_t i;
|
||||
|
||||
/* Clear existing refs in case git_remote_connect() is called again
|
||||
* after git_remote_disconnect().
|
||||
*/
|
||||
git_vector_foreach(refs, i, ref) {
|
||||
git__free(ref->head.name);
|
||||
git__free(ref);
|
||||
git_vector_foreach(refs, i, pkt) {
|
||||
git_pkt_free(pkt);
|
||||
}
|
||||
git_vector_clear(refs);
|
||||
pkt = NULL;
|
||||
|
||||
do {
|
||||
if (buf->offset > 0)
|
||||
@ -78,7 +77,52 @@ int git_smart__store_refs(transport_smart *t, int flushes)
|
||||
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;
|
||||
|
||||
@ -141,6 +185,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
|
||||
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 */
|
||||
ptr = strchr(ptr, ' ');
|
||||
}
|
||||
@ -969,7 +1022,7 @@ int git_smart__push(git_transport *transport, git_push *push)
|
||||
if (error < 0)
|
||||
goto done;
|
||||
|
||||
error = git_smart__update_heads(t);
|
||||
error = git_smart__update_heads(t, NULL);
|
||||
}
|
||||
|
||||
done:
|
||||
|
@ -10,8 +10,14 @@
|
||||
|
||||
#include "map.h"
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.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 mprot = 0;
|
||||
|
@ -133,6 +133,13 @@ GIT_INLINE(int) git__is_uint32(size_t p)
|
||||
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 */
|
||||
#ifdef _MSC_VER /* use built-in method in MSVC */
|
||||
# define git__rotl(v, s) (uint32_t)_rotl(v, s)
|
||||
|
@ -23,6 +23,11 @@ static DWORD get_page_size(void)
|
||||
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)
|
||||
{
|
||||
HANDLE fh = (HANDLE)_get_osfhandle(fd);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#if defined(__MINGW32__)
|
||||
|
||||
/* use a 64-bit file offset type */
|
||||
# undef lseek
|
||||
# define lseek _lseeki64
|
||||
# undef stat
|
||||
# define stat _stati64
|
||||
|
@ -19,6 +19,12 @@
|
||||
# define EAFNOSUPPORT (INT_MAX-1)
|
||||
#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_UNUSED(old);
|
||||
|
@ -19,6 +19,15 @@
|
||||
# define FILE_NAME_NORMALIZED 0
|
||||
#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 */
|
||||
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
|
||||
|
||||
@ -317,7 +326,7 @@ int p_open(const char *path, int flags, ...)
|
||||
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)
|
||||
@ -327,7 +336,7 @@ int p_creat(const char *path, mode_t mode)
|
||||
if (utf8_to_16_with_errno(buf, path) < 0)
|
||||
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)
|
||||
@ -569,7 +578,7 @@ int p_mkstemp(char *tmp_path)
|
||||
return -1;
|
||||
#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)
|
||||
|
@ -518,3 +518,16 @@ void cl_fake_home(void)
|
||||
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
|
||||
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
|
||||
void cl_fake_home(void);
|
||||
void cl_fake_home_cleanup(void *);
|
||||
|
||||
void cl_sandbox_set_search_path_defaults(void);
|
||||
|
||||
#endif
|
||||
|
105
tests/clone/local.c
Normal file
105
tests/clone/local.c
Normal file
@ -0,0 +1,105 @@
|
||||
#include "clar_libgit2.h"
|
||||
|
||||
#include "git2/clone.h"
|
||||
#include "clone.h"
|
||||
#include "buffer.h"
|
||||
#include "path.h"
|
||||
#include "posix.h"
|
||||
#include "fileops.h"
|
||||
|
||||
void test_clone_local__should_clone_local(void)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
const char *path;
|
||||
|
||||
/* we use a fixture path because it needs to exist for us to want to clone */
|
||||
|
||||
cl_git_pass(git_buf_printf(&buf, "file://%s", cl_fixture("testrepo.git")));
|
||||
cl_assert_equal_i(false, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
|
||||
cl_assert_equal_i(true, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
|
||||
cl_assert_equal_i(true, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
|
||||
cl_assert_equal_i(false, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
|
||||
git_buf_free(&buf);
|
||||
|
||||
path = cl_fixture("testrepo.git");
|
||||
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_AUTO));
|
||||
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL));
|
||||
cl_assert_equal_i(true, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS));
|
||||
cl_assert_equal_i(false, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL));
|
||||
}
|
||||
|
||||
void test_clone_local__hardlinks(void)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_remote *remote;
|
||||
git_signature *sig;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
struct stat st;
|
||||
|
||||
|
||||
/*
|
||||
* In this first clone, we just copy over, since the temp dir
|
||||
* will often be in a different filesystem, so we cannot
|
||||
* link. It also allows us to control the number of links
|
||||
*/
|
||||
cl_git_pass(git_repository_init(&repo, "./clone.git", true));
|
||||
cl_git_pass(git_remote_create(&remote, repo, "origin", cl_fixture("testrepo.git")));
|
||||
cl_git_pass(git_signature_now(&sig, "foo", "bar"));
|
||||
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, false, sig));
|
||||
|
||||
git_remote_free(remote);
|
||||
git_repository_free(repo);
|
||||
|
||||
/* This second clone is in the same filesystem, so we can hardlink */
|
||||
|
||||
cl_git_pass(git_repository_init(&repo, "./clone2.git", true));
|
||||
cl_git_pass(git_buf_puts(&buf, cl_git_path_url("clone.git")));
|
||||
cl_git_pass(git_remote_create(&remote, repo, "origin", buf.ptr));
|
||||
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, true, sig));
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
|
||||
|
||||
cl_git_pass(p_stat(buf.ptr, &st));
|
||||
cl_assert_equal_i(2, st.st_nlink);
|
||||
#endif
|
||||
|
||||
git_remote_free(remote);
|
||||
git_repository_free(repo);
|
||||
git_buf_clear(&buf);
|
||||
|
||||
cl_git_pass(git_repository_init(&repo, "./clone3.git", true));
|
||||
cl_git_pass(git_buf_puts(&buf, cl_git_path_url("clone.git")));
|
||||
cl_git_pass(git_remote_create(&remote, repo, "origin", buf.ptr));
|
||||
cl_git_pass(git_clone_local_into(repo, remote, NULL, NULL, false, sig));
|
||||
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
|
||||
|
||||
cl_git_pass(p_stat(buf.ptr, &st));
|
||||
cl_assert_equal_i(1, st.st_nlink);
|
||||
|
||||
git_remote_free(remote);
|
||||
git_repository_free(repo);
|
||||
|
||||
/* this one should automatically use links */
|
||||
cl_git_pass(git_clone(&repo, "./clone.git", "./clone4.git", NULL));
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
git_buf_clear(&buf);
|
||||
cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
|
||||
|
||||
cl_git_pass(p_stat(buf.ptr, &st));
|
||||
cl_assert_equal_i(3, st.st_nlink);
|
||||
#endif
|
||||
|
||||
git_buf_free(&buf);
|
||||
git_signature_free(sig);
|
||||
git_repository_free(repo);
|
||||
|
||||
cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
|
||||
cl_git_pass(git_futils_rmdir_r("./clone2.git", NULL, GIT_RMDIR_REMOVE_FILES));
|
||||
cl_git_pass(git_futils_rmdir_r("./clone3.git", NULL, GIT_RMDIR_REMOVE_FILES));
|
||||
cl_git_pass(git_futils_rmdir_r("./clone4.git", NULL, GIT_RMDIR_REMOVE_FILES));
|
||||
}
|
@ -35,3 +35,31 @@ void assert_config_entry_value(
|
||||
|
||||
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(
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
const char *expected_value);
|
||||
|
||||
extern int count_config_entries_match(
|
||||
git_repository *repo,
|
||||
const char *pattern);
|
||||
|
@ -2,25 +2,10 @@
|
||||
#include "buffer.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)
|
||||
{
|
||||
int i;
|
||||
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_path_prettify(&path, "home", NULL));
|
||||
cl_git_pass(git_libgit2_opts(
|
||||
@ -41,18 +26,7 @@ void test_config_global__initialize(void)
|
||||
|
||||
void test_config_global__cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
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));
|
||||
cl_sandbox_set_search_path_defaults();
|
||||
}
|
||||
|
||||
void test_config_global__open_global(void)
|
||||
|
@ -47,6 +47,8 @@ void test_config_include__homedir(void)
|
||||
cl_assert_equal_s(str, "huzzah");
|
||||
|
||||
git_config_free(cfg);
|
||||
|
||||
cl_sandbox_set_search_path_defaults();
|
||||
}
|
||||
|
||||
void test_config_include__refresh(void)
|
||||
|
@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void)
|
||||
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)
|
||||
{
|
||||
struct stat st;
|
||||
@ -122,5 +132,21 @@ void test_core_copy__tree(void)
|
||||
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
|
||||
cl_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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
@ -69,9 +69,7 @@ void test_core_env__cleanup(void)
|
||||
(void)p_rmdir(*val);
|
||||
}
|
||||
|
||||
/* reset search paths to default */
|
||||
reset_global_search_path();
|
||||
reset_system_search_path();
|
||||
cl_sandbox_set_search_path_defaults();
|
||||
}
|
||||
|
||||
static void setenv_and_check(const char *name, const char *value)
|
||||
@ -124,12 +122,12 @@ void test_core_env__0(void)
|
||||
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
|
||||
|
||||
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_setenv("HOME", env_save[0]);
|
||||
reset_global_search_path();
|
||||
set_global_search_path_from_env();
|
||||
|
||||
cl_assert_equal_i(
|
||||
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
|
||||
@ -138,7 +136,7 @@ void test_core_env__0(void)
|
||||
setenv_and_check("HOMEDRIVE", NULL);
|
||||
setenv_and_check("HOMEPATH", NULL);
|
||||
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));
|
||||
|
||||
@ -148,7 +146,7 @@ void test_core_env__0(void)
|
||||
|
||||
if (root >= 0) {
|
||||
setenv_and_check("USERPROFILE", NULL);
|
||||
reset_global_search_path();
|
||||
set_global_search_path_from_env();
|
||||
|
||||
cl_assert_equal_i(
|
||||
GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
|
||||
@ -158,7 +156,7 @@ void test_core_env__0(void)
|
||||
setenv_and_check("HOMEDRIVE", path.ptr);
|
||||
path.ptr[root] = old;
|
||||
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));
|
||||
}
|
||||
@ -185,7 +183,7 @@ void test_core_env__1(void)
|
||||
cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist"));
|
||||
cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist"));
|
||||
#endif
|
||||
reset_global_search_path();
|
||||
set_global_search_path_from_env();
|
||||
|
||||
cl_assert_equal_i(
|
||||
GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
|
||||
@ -195,8 +193,8 @@ void test_core_env__1(void)
|
||||
cl_git_pass(cl_setenv("HOMEPATH", NULL));
|
||||
cl_git_pass(cl_setenv("USERPROFILE", NULL));
|
||||
#endif
|
||||
reset_global_search_path();
|
||||
reset_system_search_path();
|
||||
set_global_search_path_from_env();
|
||||
set_system_search_path_from_env();
|
||||
|
||||
cl_assert_equal_i(
|
||||
GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
|
||||
@ -206,7 +204,7 @@ void test_core_env__1(void)
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
cl_git_pass(cl_setenv("PROGRAMFILES", NULL));
|
||||
reset_system_search_path();
|
||||
set_system_search_path_from_env();
|
||||
|
||||
cl_assert_equal_i(
|
||||
GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile"));
|
||||
|
@ -647,7 +647,7 @@ static void workdir_iterator_test(
|
||||
|
||||
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[] = {
|
||||
|
@ -1580,3 +1580,117 @@ void test_diff_workdir__can_update_index(void)
|
||||
|
||||
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)
|
||||
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
|
||||
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.size = strlen(in.ptr);
|
||||
|
||||
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
|
||||
cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER);
|
||||
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);
|
||||
@ -196,3 +196,44 @@ void test_filter_crlf__no_safecrlf(void)
|
||||
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)
|
||||
cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF));
|
||||
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)
|
||||
|
||||
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[])
|
||||
int main(int argc, char *argv[])
|
||||
#endif
|
||||
{
|
||||
const char *sandbox_path;
|
||||
int res;
|
||||
|
||||
clar_test_init(argc, argv);
|
||||
|
||||
git_threads_init();
|
||||
|
||||
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);
|
||||
cl_sandbox_set_search_path_defaults();
|
||||
|
||||
/* Run the test suite */
|
||||
res = clar_test_run();
|
||||
|
@ -36,72 +36,105 @@ void test_merge_workdir_analysis__cleanup(void)
|
||||
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_reference *their_ref;
|
||||
git_merge_head *their_head;
|
||||
git_merge_analysis_t analysis;
|
||||
|
||||
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_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_merge_head_free(their_head);
|
||||
git_reference_free(their_ref);
|
||||
|
||||
return analysis;
|
||||
}
|
||||
|
||||
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);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (analysis & GIT_MERGE_ANALYSIS_NORMAL));
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, analysis);
|
||||
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
|
||||
}
|
||||
|
||||
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);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, analysis);
|
||||
analysis_from_branch(&merge_analysis, &merge_pref, UPTODATE_BRANCH);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
|
||||
}
|
||||
|
||||
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);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, analysis);
|
||||
analysis_from_branch(&merge_analysis, &merge_pref, PREVIOUS_BRANCH);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
|
||||
}
|
||||
|
||||
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_joinpath(&master, git_repository_path(repo), "refs/heads/master");
|
||||
p_unlink(git_buf_cstr(&master));
|
||||
|
||||
analysis = analysis_from_branch(NOFASTFORWARD_BRANCH);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (analysis & GIT_MERGE_ANALYSIS_UNBORN));
|
||||
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
|
||||
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (merge_analysis & GIT_MERGE_ANALYSIS_UNBORN));
|
||||
|
||||
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)
|
||||
git_strarray_free(&refnames);
|
||||
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");
|
||||
}
|
||||
|
50
tests/network/remote/defaultbranch.c
Normal file
50
tests/network/remote/defaultbranch.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "buffer.h"
|
||||
#include "refspec.h"
|
||||
#include "remote.h"
|
||||
|
||||
static git_remote *g_remote;
|
||||
static git_repository *g_repo_a, *g_repo_b;
|
||||
|
||||
void test_network_remote_defaultbranch__initialize(void)
|
||||
{
|
||||
g_repo_a = cl_git_sandbox_init("testrepo.git");
|
||||
cl_git_pass(git_repository_init(&g_repo_b, "repo-b.git", true));
|
||||
cl_git_pass(git_remote_create(&g_remote, g_repo_b, "origin", git_repository_path(g_repo_a)));
|
||||
}
|
||||
|
||||
void test_network_remote_defaultbranch__cleanup(void)
|
||||
{
|
||||
git_remote_free(g_remote);
|
||||
git_repository_free(g_repo_b);
|
||||
|
||||
cl_git_sandbox_cleanup();
|
||||
cl_fixture_cleanup("repo-b.git");
|
||||
}
|
||||
|
||||
static void assert_default_branch(const char *should)
|
||||
{
|
||||
git_buf name = GIT_BUF_INIT;
|
||||
|
||||
cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH));
|
||||
cl_git_pass(git_remote_default_branch(&name, g_remote));
|
||||
cl_assert_equal_s(should, name.ptr);
|
||||
git_buf_free(&name);
|
||||
}
|
||||
|
||||
void test_network_remote_defaultbranch__master(void)
|
||||
{
|
||||
assert_default_branch("refs/heads/master");
|
||||
}
|
||||
|
||||
void test_network_remote_defaultbranch__master_does_not_win(void)
|
||||
{
|
||||
cl_git_pass(git_repository_set_head(g_repo_a, "refs/heads/not-good", NULL, NULL));
|
||||
assert_default_branch("refs/heads/not-good");
|
||||
}
|
||||
|
||||
void test_network_remote_defaultbranch__master_on_detached(void)
|
||||
{
|
||||
cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
|
||||
assert_default_branch("refs/heads/master");
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user