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

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

View File

@ -3,6 +3,10 @@
language: c
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:

View File

@ -39,12 +39,6 @@ These are good small projects to get started with libgit2.
the data is available, you would just need to add the code into the
`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

View File

@ -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

View File

@ -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))

View File

@ -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
/**

View File

@ -210,7 +210,7 @@ GIT_EXTERN(int) git_blob_create_frombuffer(
*
* The heuristic used to guess if a file is binary is taken from core git:
* 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

View File

@ -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),

View File

@ -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(

View File

@ -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

View File

@ -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.

View File

@ -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;
/**

View File

@ -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 {
@ -415,7 +418,7 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
* (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);

View File

@ -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

View File

@ -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

View File

@ -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;
};
/**

View File

@ -69,7 +69,7 @@ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const g
*
* @param repo the repository
* @param 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);

View File

@ -178,7 +178,6 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
* @param name The name of the reference
* @param 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);

View File

@ -610,6 +610,37 @@ GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value);
*/
GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
/**
* 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

View File

@ -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
*/

View File

@ -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;
/**

View File

@ -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(

View File

@ -69,7 +69,7 @@ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo)
* Call `git_signature_free()` to free the data.
*
* @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);

View File

@ -283,7 +283,7 @@ GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
* Resolve a submodule url relative to the given repository.
*
* @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
*/

View File

@ -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);

View File

@ -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
View File

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

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

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

View File

@ -281,7 +281,7 @@ uint32_t git_attr_file__name_hash(const char *name)
int git_attr_file__lookup_one(
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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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))) {
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);
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
View File

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

View File

@ -139,7 +139,7 @@ int git_config_open_ondisk(git_config **out, const char *path)
int git_config_snapshot(git_config **out, git_config *in)
{
int 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;
on_error:
if (error < 0)
git_config_free(config);
else
*out = config;
return error;
}

View File

@ -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

View File

@ -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,22 +74,16 @@ 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_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
{
*out = repo->cvar_cache[(int)cvar];
if (*out == GIT_CVAR_NOT_CACHED) {
int error = 0;
struct map_data *data = &_cvar_maps[(int)cvar];
git_config *config;
int error;
const git_config_entry *entry;
if ((error = git_repository_config__weakptr(&config, repo)) < 0)
return error;
git_config__lookup_entry(&entry, config, data->cvar_name, false);
if (!entry)
@ -94,7 +94,19 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
else
error = git_config_parse_bool(out, entry->value);
if (error < 0)
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) {
int error;
git_config *config;
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;

View File

@ -270,7 +270,6 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
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,6 +327,7 @@ static int config__refresh(git_config_backend *cfg)
out:
refcounted_strmap_free(values);
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;

View File

@ -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) {

View File

@ -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)
/* update delta_type if this item is ignored */
if (git_iterator_current_is_ignored(info->new_iter))
delta_type = GIT_DELTA_IGNORED;
else
git_buf_clear(&info->ignore_prefix);
}
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;
}

View File

@ -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);

View File

@ -287,33 +287,46 @@ static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
{
git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL;
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(

View 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)

View File

@ -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;
/**

View File

@ -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,

View File

@ -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.

View File

@ -1,8 +1,29 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* 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__

View File

@ -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()
{

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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;
}
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);
}

View File

@ -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 {

View File

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

View File

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

View File

@ -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);

View File

@ -21,7 +21,7 @@ static size_t line_length_without_trailing_spaces(const char *line, size_t len)
/* Greatly inspired from git.git "stripspace" */
/* 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);

View File

@ -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;

View File

@ -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;

View File

@ -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)
{
git_off_t base_offset, base_key;
git_rawobj base, delta;
git_pack_cache_entry *cached = NULL;
int error, found_base = 0;
#define SMALL_STACK_SIZE 64
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;
if (!p->bases.entries && (cache_init(&p->bases) < 0))
return -1;
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 (!cached) { /* have to inflate it */
error = git_packfile_unpack(&base, p, &base_offset);
/*
* 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.
/**
* 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.
*/
if (error < 0)
return error;
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_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;
elem_pos = 0;
while (true) {
struct pack_chain_elem *elem;
git_pack_cache_entry *cached = NULL;
/* 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;
}
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;
/* 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;
}
obj->type = base.type;
error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
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);
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);
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++;
}
*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;

View File

@ -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;

View File

@ -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;
}

View File

@ -438,4 +438,7 @@ extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
extern bool git_path_does_fs_decompose_unicode(const char *root);
/* 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

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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(

View File

@ -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);

View File

@ -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)
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);
}

View File

@ -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 ||
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_config_free(config);
git_repository_free(repo);
return error;
}
git_config_free(config);
git_buf_free(&parent);
}
if (!error)
*repo_ptr = repo;
return 0;
git_buf_free(&parent);
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;

View File

@ -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 */

View File

@ -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;
git_odb_stream_write(stream, buffer, strlen(buffer));
if ((error = git_odb_open_wstream(
&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
return error;
if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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:

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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)

View File

@ -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);
}

View File

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

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

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

View File

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

View File

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

View File

@ -2,25 +2,10 @@
#include "buffer.h"
#include "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)

View File

@ -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)

View File

@ -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));
}

View File

@ -40,12 +40,12 @@ void test_core_env__initialize(void)
}
}
static void reset_global_search_path(void)
static void set_global_search_path_from_env(void)
{
cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL));
}
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"));

View File

@ -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[] = {

View File

@ -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);
}

View File

@ -103,12 +103,12 @@ void test_filter_crlf__with_safecrlf(void)
cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in));
cl_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);
}

View File

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

View File

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

View File

@ -6,16 +6,12 @@ int __cdecl main(int argc, char *argv[])
int main(int argc, char *argv[])
#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();

View File

@ -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));
}

View File

@ -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");
}

View File

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

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