summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Rogers <alan@github.com>2014-06-04 15:36:28 -0700
committerAlan Rogers <alan@github.com>2014-06-04 15:36:28 -0700
commitdc49e1b5b368b4b7d5b8ae8a0a12027f86395cad (patch)
tree6335b980e9ff7688d758e411088838b6adb75baa
parent54c02d212d70b439f402c74c6b1f6c835daa43fc (diff)
parent90befde4a1938641dfdb9a7bdb9f361d1de5c26f (diff)
downloadlibgit2-dc49e1b5b368b4b7d5b8ae8a0a12027f86395cad.tar.gz
Merge remote-tracking branch 'origin/development' into fix-git-status-list-new-unreadable-folder
Conflicts: include/git2/diff.h
-rw-r--r--.travis.yml16
-rw-r--r--PROJECTS.md6
-rw-r--r--README.md2
-rw-r--r--examples/log.c49
-rw-r--r--examples/status.c5
-rw-r--r--include/git2/blob.h2
-rw-r--r--include/git2/checkout.h24
-rw-r--r--include/git2/cherrypick.h2
-rw-r--r--include/git2/clone.h37
-rw-r--r--include/git2/diff.h60
-rw-r--r--include/git2/errors.h32
-rw-r--r--include/git2/index.h61
-rw-r--r--include/git2/merge.h31
-rw-r--r--include/git2/message.h8
-rw-r--r--include/git2/net.h5
-rw-r--r--include/git2/reflog.h2
-rw-r--r--include/git2/refs.h4
-rw-r--r--include/git2/remote.h31
-rw-r--r--include/git2/repository.h2
-rw-r--r--include/git2/reset.h6
-rw-r--r--include/git2/revert.h7
-rw-r--r--include/git2/signature.h2
-rw-r--r--include/git2/submodule.h2
-rw-r--r--include/git2/tree.h2
-rw-r--r--include/git2/types.h26
-rwxr-xr-xscript/install-deps-linux.sh6
-rwxr-xr-xscript/install-deps-osx.sh5
-rw-r--r--src/attr_file.c25
-rw-r--r--src/attr_file.h6
-rw-r--r--src/attrcache.c13
-rw-r--r--src/blob.c3
-rw-r--r--src/clone.c311
-rw-r--r--src/clone.h12
-rw-r--r--src/config.c14
-rw-r--r--src/config.h6
-rw-r--r--src/config_cache.c50
-rw-r--r--src/config_file.c13
-rw-r--r--src/crlf.c4
-rw-r--r--src/diff.c59
-rw-r--r--src/diff_driver.c16
-rw-r--r--src/diff_print.c87
-rw-r--r--src/fileops.c6
-rw-r--r--src/fileops.h2
-rw-r--r--src/filter.h4
-rw-r--r--src/fnmatch.c34
-rw-r--r--src/fnmatch.h27
-rw-r--r--src/global.c2
-rw-r--r--src/ignore.c69
-rw-r--r--src/ignore.h9
-rw-r--r--src/index.c14
-rw-r--r--src/indexer.c158
-rw-r--r--src/iterator.c66
-rw-r--r--src/iterator.h2
-rw-r--r--src/map.h1
-rw-r--r--src/merge.c61
-rw-r--r--src/message.c4
-rw-r--r--src/netops.c4
-rw-r--r--src/odb.c1
-rw-r--r--src/pack.c290
-rw-r--r--src/pack.h10
-rw-r--r--src/path.c18
-rw-r--r--src/path.h3
-rw-r--r--src/posix.c7
-rw-r--r--src/posix.h1
-rw-r--r--src/refdb_fs.c40
-rw-r--r--src/refs.c48
-rw-r--r--src/refs.h3
-rw-r--r--src/remote.c222
-rw-r--r--src/repository.c44
-rw-r--r--src/repository.h4
-rw-r--r--src/tag.c14
-rw-r--r--src/transports/local.c63
-rw-r--r--src/transports/smart.c57
-rw-r--r--src/transports/smart.h5
-rw-r--r--src/transports/smart_pkt.c1
-rw-r--r--src/transports/smart_protocol.c65
-rw-r--r--src/unix/map.c6
-rw-r--r--src/util.h7
-rw-r--r--src/win32/map.c5
-rw-r--r--src/win32/mingw-compat.h1
-rw-r--r--src/win32/posix.h6
-rw-r--r--src/win32/posix_w32.c15
-rw-r--r--tests/clar_libgit2.c13
-rw-r--r--tests/clar_libgit2.h2
-rw-r--r--tests/clone/local.c105
-rw-r--r--tests/config/config_helpers.c28
-rw-r--r--tests/config/config_helpers.h4
-rw-r--r--tests/config/global.c28
-rw-r--r--tests/config/include.c2
-rw-r--r--tests/core/copy.c26
-rw-r--r--tests/core/env.c26
-rw-r--r--tests/diff/iterator.c2
-rw-r--r--tests/diff/workdir.c114
-rw-r--r--tests/filter/crlf.c47
-rw-r--r--tests/index/crlf.c18
-rw-r--r--tests/index/filemodes.c17
-rw-r--r--tests/main.c6
-rw-r--r--tests/merge/workdir/analysis.c77
-rw-r--r--tests/network/fetchlocal.c26
-rw-r--r--tests/network/remote/defaultbranch.c50
-rw-r--r--tests/network/remote/delete.c61
-rw-r--r--tests/network/remote/remotes.c9
-rw-r--r--tests/object/commit/commitstagedfile.c2
-rw-r--r--tests/object/message.c14
-rw-r--r--tests/odb/foreach.c2
-rw-r--r--tests/online/clone.c33
-rw-r--r--tests/online/fetch.c18
-rw-r--r--tests/pack/packbuilder.c2
-rw-r--r--tests/refs/branches/create.c3
-rw-r--r--tests/refs/branches/move.c17
-rw-r--r--tests/refs/iterator.c33
-rw-r--r--tests/repo/config.c38
-rw-r--r--tests/repo/open.c5
-rw-r--r--tests/repo/pathspec.c2
-rw-r--r--tests/status/ignore.c107
115 files changed, 2544 insertions, 846 deletions
diff --git a/.travis.yml b/.travis.yml
index fcae726dd..bab02bb44 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,10 @@
language: c
+os:
+ - linux
+ - osx
+
compiler:
- gcc
- clang
@@ -17,17 +21,21 @@ env:
matrix:
fast_finish: true
+ exclude:
+ - os: osx
+ compiler: gcc
include:
- compiler: i586-mingw32msvc-gcc
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF"
+ os: linux
- compiler: gcc
env: COVERITY=1
+ os: linux
allow_failures:
- env: COVERITY=1
install:
- - sudo apt-get -qq update
- - sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
+ - ./script/install-deps-${TRAVIS_OS_NAME}.sh
# Run the Build script and tests
script:
@@ -35,8 +43,8 @@ script:
# Run Tests
after_success:
- - sudo apt-get -qq install valgrind
- - valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install valgrind; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi
# Only watch the development branch
branches:
diff --git a/PROJECTS.md b/PROJECTS.md
index 9472fb43c..d17214471 100644
--- a/PROJECTS.md
+++ b/PROJECTS.md
@@ -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
diff --git a/README.md b/README.md
index 8dd073430..b60e8a234 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/examples/log.c b/examples/log.c
index 471c5ff96..d5f75a297 100644
--- a/examples/log.c
+++ b/examples/log.c
@@ -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))
diff --git a/examples/status.c b/examples/status.c
index 9c99744cb..a59f34454 100644
--- a/examples/status.c
+++ b/examples/status.c
@@ -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
/**
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 1b6583309..c24ff7e7f 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -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
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 494f67456..ad44173e6 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -43,17 +43,17 @@ GIT_BEGIN_DECL
* In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE`
* both of which only make modifications that will not lose changes.
*
- * | target == baseline | target != baseline |
- * ---------------------|-----------------------|----------------------|
- * workdir == baseline | no action | create, update, or |
- * | | delete file |
- * ---------------------|-----------------------|----------------------|
- * workdir exists and | no action | conflict (notify |
- * is != baseline | notify dirty MODIFIED | and cancel checkout) |
- * ---------------------|-----------------------|----------------------|
- * workdir missing, | create if SAFE_CREATE | create file |
- * baseline present | notify dirty DELETED | |
- * ---------------------|-----------------------|----------------------|
+ * | target == baseline | target != baseline |
+ * ---------------------|-----------------------|----------------------|
+ * workdir == baseline | no action | create, update, or |
+ * | | delete file |
+ * ---------------------|-----------------------|----------------------|
+ * workdir exists and | no action | conflict (notify |
+ * is != baseline | notify dirty MODIFIED | and cancel checkout) |
+ * ---------------------|-----------------------|----------------------|
+ * workdir missing, | create if SAFE_CREATE | create file |
+ * baseline present | notify dirty DELETED | |
+ * ---------------------|-----------------------|----------------------|
*
* The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE
* will cause a file to be checked out if it is missing from the working
@@ -106,7 +106,7 @@ GIT_BEGIN_DECL
* target contains that file.
*/
typedef enum {
- GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */
+ GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
/** Allow safe updates that cannot overwrite uncommitted data */
GIT_CHECKOUT_SAFE = (1u << 0),
diff --git a/include/git2/cherrypick.h b/include/git2/cherrypick.h
index e998d325f..9eccb0af9 100644
--- a/include/git2/cherrypick.h
+++ b/include/git2/cherrypick.h
@@ -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(
diff --git a/include/git2/clone.h b/include/git2/clone.h
index 985c04bf6..b2c944a78 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -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
diff --git a/include/git2/diff.h b/include/git2/diff.h
index db2233f82..f126453f4 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -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.
diff --git a/include/git2/errors.h b/include/git2/errors.h
index e22f0d86d..c914653fc 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -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;
/**
diff --git a/include/git2/index.h b/include/git2/index.h
index cdb87282c..0b4476b4e 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -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`.
+ * 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_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)
+typedef enum {
-#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
+ GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
+ GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
+ /** Reserved for future extension */
+ GIT_IDXENTRY_EXTENDED2 = (1 << 15),
-/**
- * Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended`
- *
- * These bitmasks match the other fields in the `git_index_entry`
- * `flags_extended` value that are only used in-memory by libgit2. You
- * can use them to interpret the data in the `flags_extended`.
- */
-#define GIT_IDXENTRY_UPDATE (1 << 0)
-#define GIT_IDXENTRY_REMOVE (1 << 1)
-#define GIT_IDXENTRY_UPTODATE (1 << 2)
-#define GIT_IDXENTRY_ADDED (1 << 3)
+ 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),
-#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_HASHED = (1 << 4),
+ GIT_IDXENTRY_UNHASHED = (1 << 5),
+ GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
+ GIT_IDXENTRY_CONFLICTED = (1 << 7),
-#define GIT_IDXENTRY_UNPACKED (1 << 8)
-#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
+ GIT_IDXENTRY_UNPACKED = (1 << 8),
+ GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9),
+} git_idxentry_extended_flag_t;
/** Capabilities of system that affect index actions. */
typedef enum {
@@ -412,10 +415,10 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
*
* This entry is calculated from the entry's flag attribute like this:
*
- * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
+ * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
*
* @param entry The entry
- * @returns the stage number
+ * @return the stage number
*/
GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
diff --git a/include/git2/merge.h b/include/git2/merge.h
index abbc3a5bb..9eb14ccb1 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -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
diff --git a/include/git2/message.h b/include/git2/message.h
index bcdb72f6a..d78b1dce5 100644
--- a/include/git2/message.h
+++ b/include/git2/message.h
@@ -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
diff --git a/include/git2/net.h b/include/git2/net.h
index e70ba1f71..a727696a2 100644
--- a/include/git2/net.h
+++ b/include/git2/net.h
@@ -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;
};
/**
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index df06e1b8e..ac42a231c 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -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);
diff --git a/include/git2/refs.h b/include/git2/refs.h
index ae2d379d9..e5bb15c7c 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -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);
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 3633501e2..28771ac42 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -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
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 037cb3f96..6a8ff4545 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -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
*/
diff --git a/include/git2/reset.h b/include/git2/reset.h
index 1759cc036..b8c580339 100644
--- a/include/git2/reset.h
+++ b/include/git2/reset.h
@@ -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;
/**
diff --git a/include/git2/revert.h b/include/git2/revert.h
index da37fbe7b..fc1767c93 100644
--- a/include/git2/revert.h
+++ b/include/git2/revert.h
@@ -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(
diff --git a/include/git2/signature.h b/include/git2/signature.h
index a1dd1ec7a..feb1b4073 100644
--- a/include/git2/signature.h
+++ b/include/git2/signature.h
@@ -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);
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 28e235725..864d1c58c 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -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
*/
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 6669652ae..56922d40b 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -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);
diff --git a/include/git2/types.h b/include/git2/types.h
index 46dfd8b75..3019c4677 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -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;
diff --git a/script/install-deps-linux.sh b/script/install-deps-linux.sh
new file mode 100755
index 000000000..347922b89
--- /dev/null
+++ b/script/install-deps-linux.sh
@@ -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
diff --git a/script/install-deps-osx.sh b/script/install-deps-osx.sh
new file mode 100755
index 000000000..c2e0162d8
--- /dev/null
+++ b/script/install-deps-osx.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+set -x
+
+brew install libssh2 cmake
diff --git a/src/attr_file.c b/src/attr_file.c
index 156a23d91..3e95a2134 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -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);
diff --git a/src/attr_file.h b/src/attr_file.h
index e50aec07c..87cde7e35 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -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);
diff --git a/src/attrcache.c b/src/attrcache.c
index f1bc70467..b4579bfc0 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -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;
}
diff --git a/src/blob.c b/src/blob.c
index ab7dec67f..30d5b705b 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -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);
diff --git a/src/clone.c b/src/clone.c
index c6be00f0e..6c4fb6727 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -22,6 +22,7 @@
#include "refs.h"
#include "path.h"
#include "repository.h"
+#include "odb.h"
static int create_branch(
git_reference **branch,
@@ -105,54 +106,6 @@ static int create_tracking_branch(
git_reference_name(*branch));
}
-struct head_info {
- git_repository *repo;
- git_oid remote_head_oid;
- git_buf branchname;
- const git_refspec *refspec;
- bool found;
-};
-
-static int reference_matches_remote_head(
- const char *reference_name,
- void *payload)
-{
- struct head_info *head_info = (struct head_info *)payload;
- git_oid oid;
- int error;
-
- /* TODO: Should we guard against references
- * which name doesn't start with refs/heads/ ?
- */
-
- error = git_reference_name_to_id(&oid, head_info->repo, reference_name);
- if (error == GIT_ENOTFOUND) {
- /* If the reference doesn't exists, it obviously cannot match the
- * expected oid. */
- giterr_clear();
- return 0;
- }
-
- if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) {
- /* Determine the local reference name from the remote tracking one */
- error = git_refspec_rtransform(
- &head_info->branchname, head_info->refspec, reference_name);
-
- if (!error &&
- git_buf_len(&head_info->branchname) > 0 &&
- !(error = git_buf_sets(
- &head_info->branchname,
- git_buf_cstr(&head_info->branchname) +
- strlen(GIT_REFS_HEADS_DIR))))
- {
- head_info->found = true;
- error = GIT_ITEROVER;
- }
- }
-
- return error;
-}
-
static int update_head_to_new_branch(
git_repository *repo,
const git_oid *target,
@@ -161,7 +114,12 @@ static int update_head_to_new_branch(
const char *reflog_message)
{
git_reference *tracking_branch = NULL;
- int error = create_tracking_branch(&tracking_branch, repo, target, name,
+ int error;
+
+ if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
+ name += strlen(GIT_REFS_HEADS_DIR);
+
+ error = create_tracking_branch(&tracking_branch, repo, target, name,
signature, reflog_message);
if (!error)
@@ -171,6 +129,10 @@ static int update_head_to_new_branch(
git_reference_free(tracking_branch);
+ /* if it already existed, then the user's refspec created it for us, ignore it' */
+ if (error == GIT_EEXISTS)
+ error = 0;
+
return error;
}
@@ -180,12 +142,13 @@ static int update_head_to_remote(
const git_signature *signature,
const char *reflog_message)
{
- int error = 0;
+ int error = 0, found_branch = 0;
size_t refs_len;
- git_refspec dummy_spec;
+ git_refspec dummy_spec, *refspec;
const git_remote_head *remote_head, **refs;
- struct head_info head_info;
+ const git_oid *remote_head_id;
git_buf remote_master_name = GIT_BUF_INIT;
+ git_buf branch = GIT_BUF_INIT;
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
return error;
@@ -195,63 +158,45 @@ static int update_head_to_remote(
return setup_tracking_config(
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
+ error = git_remote_default_branch(&branch, remote);
+ if (error == GIT_ENOTFOUND) {
+ git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE);
+ } else {
+ found_branch = 1;
+ }
+
/* Get the remote's HEAD. This is always the first ref in the list. */
remote_head = refs[0];
assert(remote_head);
- memset(&head_info, 0, sizeof(head_info));
- git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
- head_info.repo = repo;
- head_info.refspec =
- git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
+ remote_head_id = &remote_head->oid;
+ refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
- if (head_info.refspec == NULL) {
+ if (refspec == NULL) {
memset(&dummy_spec, 0, sizeof(git_refspec));
- head_info.refspec = &dummy_spec;
+ refspec = &dummy_spec;
}
/* Determine the remote tracking reference name from the local master */
if ((error = git_refspec_transform(
&remote_master_name,
- head_info.refspec,
- GIT_REFS_HEADS_MASTER_FILE)) < 0)
+ refspec,
+ git_buf_cstr(&branch))) < 0)
return error;
- /* Check to see if the remote HEAD points to the remote master */
- error = reference_matches_remote_head(
- git_buf_cstr(&remote_master_name), &head_info);
- if (error < 0 && error != GIT_ITEROVER)
- goto cleanup;
-
- if (head_info.found) {
- 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) {
+ if (found_branch) {
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;
+
+ 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 = 0, old_fetchhead;
- git_strarray refspecs;
+ int error;
git_buf reflog_message = GIT_BUF_INIT;
+ git_remote *remote;
+ const git_remote_callbacks *callbacks;
- assert(repo && remote);
+ assert(repo && _remote);
if (!git_repository_is_empty(repo)) {
giterr_set(GITERR_INVALID, "the repository is not empty");
return -1;
}
-
- if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
+ if ((error = git_remote_dup(&remote, _remote)) < 0)
return error;
+ callbacks = git_remote_get_callbacks(_remote);
+ if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
+ (error = git_remote_set_callbacks(remote, callbacks)) < 0)
+ goto cleanup;
+
if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
- return error;
+ goto cleanup;
- old_fetchhead = git_remote_update_fetchhead(remote);
git_remote_set_update_fetchhead(remote, 0);
git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
goto cleanup;
- if (branch)
- error = update_head_to_branch(repo, git_remote_name(remote), branch,
- signature, git_buf_cstr(&reflog_message));
- /* Point HEAD to the same ref as the remote's head */
- else
- error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message));
-
- if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
- error = git_checkout_head(repo, co_opts);
+ error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
cleanup:
- git_remote_set_update_fetchhead(remote, old_fetchhead);
-
- /* Go back to the original refspecs */
- {
- int error_alt = git_remote_set_fetch_refspecs(remote, &refspecs);
- if (!error)
- error = error_alt;
- }
-
- git_strarray_free(&refspecs);
+ git_remote_free(remote);
git_buf_free(&reflog_message);
return error;
}
+int git_clone__should_clone_local(const char *url, git_clone_local_t local)
+{
+ const char *path;
+ int is_url;
+
+ if (local == GIT_CLONE_NO_LOCAL)
+ return false;
+
+ is_url = !git__prefixcmp(url, "file://");
+
+ if (is_url && local != GIT_CLONE_LOCAL && local != GIT_CLONE_LOCAL_NO_LINKS )
+ return false;
+
+ path = url;
+ if (is_url)
+ path = url + strlen("file://");
+
+ if ((git_path_exists(path) && git_path_isdir(path)) && local != GIT_CLONE_NO_LOCAL)
+ return true;
+
+ return false;
+}
+
int git_clone(
git_repository **out,
const char *url,
@@ -423,8 +404,16 @@ int git_clone(
return error;
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
- error = git_clone_into(
- repo, origin, &options.checkout_opts, options.checkout_branch, options.signature);
+ if (git_clone__should_clone_local(url, options.local)) {
+ int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
+ error = git_clone_local_into(
+ repo, origin, &options.checkout_opts,
+ options.checkout_branch, link, options.signature);
+ } else {
+ error = git_clone_into(
+ repo, origin, &options.checkout_opts,
+ options.checkout_branch, options.signature);
+ }
git_remote_free(origin);
}
@@ -451,3 +440,91 @@ int git_clone_init_options(git_clone_options *opts, unsigned int version)
opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
return 0;
}
+
+static const char *repository_base(git_repository *repo)
+{
+ if (git_repository_is_bare(repo))
+ return git_repository_path(repo);
+
+ return git_repository_workdir(repo);
+}
+
+static bool can_link(const char *src, const char *dst, int link)
+{
+#ifdef GIT_WIN32
+ return false;
+#else
+
+ struct stat st_src, st_dst;
+
+ if (!link)
+ return false;
+
+ if (p_stat(src, &st_src) < 0)
+ return false;
+
+ if (p_stat(dst, &st_dst) < 0)
+ return false;
+
+ return st_src.st_dev == st_dst.st_dev;
+#endif
+}
+
+int git_clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
+{
+ int error, flags;
+ git_repository *src;
+ git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
+ git_buf reflog_message = GIT_BUF_INIT;
+
+ assert(repo && remote);
+
+ if (!git_repository_is_empty(repo)) {
+ giterr_set(GITERR_INVALID, "the repository is not empty");
+ return -1;
+ }
+
+ /*
+ * Let's figure out what path we should use for the source
+ * repo, if it's not rooted, the path should be relative to
+ * the repository's worktree/gitdir.
+ */
+ if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
+ return error;
+
+ /* Copy .git/objects/ from the source to the target */
+ if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
+ git_buf_free(&src_path);
+ return error;
+ }
+
+ git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
+ git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
+ if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ flags = 0;
+ if (can_link(git_repository_path(src), git_repository_path(repo), link))
+ flags |= GIT_CPDIR_LINK_FILES;
+
+ if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
+ flags, GIT_OBJECT_DIR_MODE)) < 0)
+ goto cleanup;
+
+ git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+
+ if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
+ goto cleanup;
+
+ error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));
+
+cleanup:
+ git_buf_free(&reflog_message);
+ git_buf_free(&src_path);
+ git_buf_free(&src_odb);
+ git_buf_free(&dst_odb);
+ git_repository_free(src);
+ return error;
+}
diff --git a/src/clone.h b/src/clone.h
new file mode 100644
index 000000000..14ca5d44c
--- /dev/null
+++ b/src/clone.h
@@ -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
diff --git a/src/config.c b/src/config.c
index 757ff0387..8a0fb653c 100644
--- a/src/config.c
+++ b/src/config.c
@@ -139,7 +139,7 @@ int git_config_open_ondisk(git_config **out, const char *path)
int git_config_snapshot(git_config **out, git_config *in)
{
- int error;
+ int error = 0;
size_t i;
file_internal *internal;
git_config *config;
@@ -153,19 +153,19 @@ int git_config_snapshot(git_config **out, git_config *in)
git_config_backend *b;
if ((error = internal->file->snapshot(&b, internal->file)) < 0)
- goto on_error;
+ break;
if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
b->free(b);
- goto on_error;
+ break;
}
}
- *out = config;
- return error;
+ if (error < 0)
+ git_config_free(config);
+ else
+ *out = config;
-on_error:
- git_config_free(config);
return error;
}
diff --git a/src/config.h b/src/config.h
index 00b6063e7..b0dcb49ac 100644
--- a/src/config.h
+++ b/src/config.h
@@ -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
diff --git a/src/config_cache.c b/src/config_cache.c
index 4bcbf02bf..45c39ce17 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -7,11 +7,11 @@
#include "common.h"
#include "fileops.h"
+#include "repository.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
#include "filter.h"
-#include "repository.h"
struct map_data {
const char *cvar_name;
@@ -51,6 +51,12 @@ static git_cvar_map _cvar_map_autocrlf[] = {
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
};
+static git_cvar_map _cvar_map_safecrlf[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_SAFE_CRLF_FALSE},
+ {GIT_CVAR_TRUE, NULL, GIT_SAFE_CRLF_FAIL},
+ {GIT_CVAR_STRING, "warn", GIT_SAFE_CRLF_WARN}
+};
+
/*
* Generic map for integer values
*/
@@ -68,33 +74,39 @@ static struct map_data _cvar_maps[] = {
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
- {"core.safecrlf", NULL, 0, GIT_SAFE_CRLF_DEFAULT},
+ {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
+ {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
};
+int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
+{
+ int error = 0;
+ struct map_data *data = &_cvar_maps[(int)cvar];
+ const git_config_entry *entry;
+
+ git_config__lookup_entry(&entry, config, data->cvar_name, false);
+
+ if (!entry)
+ *out = data->default_value;
+ else if (data->maps)
+ error = git_config_lookup_map_value(
+ out, data->maps, data->map_count, entry->value);
+ else
+ error = git_config_parse_bool(out, entry->value);
+
+ return error;
+}
+
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
{
*out = repo->cvar_cache[(int)cvar];
if (*out == GIT_CVAR_NOT_CACHED) {
- struct map_data *data = &_cvar_maps[(int)cvar];
- git_config *config;
int error;
- const git_config_entry *entry;
-
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
- return error;
-
- git_config__lookup_entry(&entry, config, data->cvar_name, false);
-
- if (!entry)
- *out = data->default_value;
- else if (data->maps)
- error = git_config_lookup_map_value(
- out, data->maps, data->map_count, entry->value);
- else
- error = git_config_parse_bool(out, entry->value);
+ git_config *config;
- if (error < 0)
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
+ (error = git_config__cvar(out, config, cvar)) < 0)
return error;
repo->cvar_cache[(int)cvar] = *out;
diff --git a/src/config_file.c b/src/config_file.c
index b00d0827d..56271144b 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -270,7 +270,6 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
return res;
- git_mutex_init(&b->header.values_mutex);
git_array_init(b->readers);
reader = git_array_alloc(b->readers);
if (!reader) {
@@ -313,6 +312,7 @@ static int config__refresh(git_config_backend *cfg)
goto out;
reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
+ GITERR_CHECK_ALLOC(reader);
if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0)
goto out;
@@ -327,7 +327,8 @@ static int config__refresh(git_config_backend *cfg)
out:
refcounted_strmap_free(values);
- git_buf_free(&reader->buffer);
+ if (reader)
+ git_buf_free(&reader->buffer);
return error;
}
@@ -344,8 +345,8 @@ static int config_refresh(git_config_backend *cfg)
&reader->buffer, reader->file_path,
&reader->file_mtime, &reader->file_size, &updated);
- if (error < 0)
- return (error == GIT_ENOTFOUND) ? 0 : error;
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
if (updated)
any_updated = 1;
@@ -373,6 +374,7 @@ static void backend_free(git_config_backend *_backend)
git__free(backend->file_path);
refcounted_strmap_free(backend->header.values);
+ git_mutex_free(&backend->header.values_mutex);
git__free(backend);
}
@@ -684,6 +686,7 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
GITERR_CHECK_ALLOC(backend);
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
+ git_mutex_init(&backend->header.values_mutex);
backend->file_path = git__strdup(path);
GITERR_CHECK_ALLOC(backend->file_path);
@@ -756,6 +759,7 @@ static void backend_readonly_free(git_config_backend *_backend)
return;
refcounted_strmap_free(backend->header.values);
+ git_mutex_free(&backend->header.values_mutex);
git__free(backend);
}
@@ -782,6 +786,7 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
GITERR_CHECK_ALLOC(backend);
backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
+ git_mutex_init(&backend->header.values_mutex);
backend->snapshot_from = in;
diff --git a/src/crlf.c b/src/crlf.c
index dad3ecebc..821e04eb2 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -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) {
diff --git a/src/diff.c b/src/diff.c
index b55d123c1..2691d7ca0 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -390,7 +390,7 @@ static int diff_list_apply_options(
git_diff *diff,
const git_diff_options *opts)
{
- git_config *cfg;
+ git_config *cfg = NULL;
git_repository *repo = diff->repo;
git_pool *pool = &diff->pool;
int val;
@@ -415,20 +415,20 @@ static int diff_list_apply_options(
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* load config values that affect diff behavior */
- if ((val = git_repository_config__weakptr(&cfg, repo)) < 0)
+ if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
return val;
- if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val)
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_SYMLINKS) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
- if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val)
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_IGNORESTAT) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT;
if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
- !git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val)
+ !git_config__cvar(&val, cfg, GIT_CVAR_FILEMODE) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
- if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val)
+ if (!git_config__cvar(&val, cfg, GIT_CVAR_TRUSTCTIME) && val)
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
@@ -490,8 +490,6 @@ static int diff_list_apply_options(
/* strdup prefix from pool so we're not dependent on external data */
diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix);
diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix);
- if (!diff->opts.old_prefix || !diff->opts.new_prefix)
- return -1;
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
const char *tmp_prefix = diff->opts.old_prefix;
@@ -499,7 +497,10 @@ static int diff_list_apply_options(
diff->opts.new_prefix = tmp_prefix;
}
- return 0;
+ git_config_free(cfg);
+
+ /* check strdup results for error */
+ return (!diff->opts.old_prefix || !diff->opts.new_prefix) ? -1 : 0;
}
static void diff_list_free(git_diff *diff)
@@ -642,7 +643,6 @@ typedef struct {
git_iterator *new_iter;
const git_index_entry *oitem;
const git_index_entry *nitem;
- git_buf ignore_prefix;
} diff_in_progress;
#define MODE_BITS_MASK 0000777
@@ -858,24 +858,13 @@ static int handle_unmatched_new_item(
/* check if this is a prefix of the other side */
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
- /* check if this is contained in an ignored parent directory */
- if (git_buf_len(&info->ignore_prefix)) {
- if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0)
- delta_type = GIT_DELTA_IGNORED;
- else
- git_buf_clear(&info->ignore_prefix);
- }
+ /* update delta_type if this item is ignored */
+ if (git_iterator_current_is_ignored(info->new_iter))
+ delta_type = GIT_DELTA_IGNORED;
if (nitem->mode == GIT_FILEMODE_TREE) {
bool recurse_into_dir = contains_oitem;
- /* if not already inside an ignored dir, check if this is ignored */
- if (delta_type != GIT_DELTA_IGNORED &&
- git_iterator_current_is_ignored(info->new_iter)) {
- delta_type = GIT_DELTA_IGNORED;
- git_buf_sets(&info->ignore_prefix, nitem->path);
- }
-
/* check if user requests recursion into this type of dir */
recurse_into_dir = contains_oitem ||
(delta_type == GIT_DELTA_UNTRACKED &&
@@ -952,27 +941,12 @@ static int handle_unmatched_new_item(
}
}
- /* In core git, the next two checks are effectively reversed --
- * i.e. when an file contained in an ignored directory is explicitly
- * ignored, it shows up as an ignored file in the diff list, even though
- * other untracked files in the same directory are skipped completely.
- *
- * To me, this seems odd. If the directory is ignored and the file is
- * untracked, we should skip it consistently, regardless of whether it
- * happens to match a pattern in the ignore file.
- *
- * To match the core git behavior, reverse the following two if checks
- * so that individual file ignores are checked before container
- * directory exclusions are used to skip the file.
- */
else if (delta_type == GIT_DELTA_IGNORED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
+ git_iterator_current_tree_is_ignored(info->new_iter))
/* item contained in ignored directory, so skip over it */
return git_iterator_advance(&info->nitem, info->new_iter);
- else if (git_iterator_current_is_ignored(info->new_iter))
- delta_type = GIT_DELTA_IGNORED;
-
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
delta_type = GIT_DELTA_ADDED;
@@ -1089,7 +1063,6 @@ int git_diff__from_iterators(
info.repo = repo;
info.old_iter = old_iter;
info.new_iter = new_iter;
- git_buf_init(&info.ignore_prefix, 0);
/* make iterators have matching icase behavior */
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
@@ -1144,8 +1117,6 @@ cleanup:
else
git_diff_free(diff);
- git_buf_free(&info.ignore_prefix);
-
return error;
}
diff --git a/src/diff_driver.c b/src/diff_driver.c
index fc1354f36..c3c5f365b 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -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);
diff --git a/src/diff_print.c b/src/diff_print.c
index 964c49540..32b109bb2 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -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(
diff --git a/src/fileops.c b/src/fileops.c
index 13b8f6a39..bebbae4f9 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -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)
diff --git a/src/fileops.h b/src/fileops.h
index 62227abae..4f5700a99 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -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;
/**
diff --git a/src/filter.h b/src/filter.h
index d0ace0f9a..5a366108b 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -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,
diff --git a/src/fnmatch.c b/src/fnmatch.c
index d8a83a8ed..d7899e3e6 100644
--- a/src/fnmatch.c
+++ b/src/fnmatch.c
@@ -6,6 +6,40 @@
*/
/*
+ * This file contains code originally derrived from OpenBSD fnmatch.c
+ *
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern.
*/
diff --git a/src/fnmatch.h b/src/fnmatch.h
index 920e7de4d..88af45939 100644
--- a/src/fnmatch.h
+++ b/src/fnmatch.h
@@ -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__
diff --git a/src/global.c b/src/global.c
index 4dfdcf438..7da31853e 100644
--- a/src/global.c
+++ b/src/global.c
@@ -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()
{
diff --git a/src/ignore.c b/src/ignore.c
index f373c9482..78f01ac44 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -248,14 +248,15 @@ void git_ignore__free(git_ignores *ignores)
}
static bool ignore_lookup_in_rules(
- git_attr_file *file, git_attr_path *path, int *ignored)
+ int *ignored, git_attr_file *file, git_attr_path *path)
{
size_t j;
git_attr_fnmatch *match;
git_vector_rforeach(&file->rules, j, match) {
if (git_attr_fnmatch__match(match, path)) {
- *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
+ *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ?
+ GIT_IGNORE_TRUE : GIT_IGNORE_FALSE;
return true;
}
}
@@ -264,34 +265,34 @@ static bool ignore_lookup_in_rules(
}
int git_ignore__lookup(
- git_ignores *ignores, const char *pathname, int *ignored)
+ int *out, git_ignores *ignores, const char *pathname)
{
unsigned int i;
git_attr_file *file;
git_attr_path path;
+ *out = GIT_IGNORE_NOTFOUND;
+
if (git_attr_path__init(
&path, pathname, git_repository_workdir(ignores->repo)) < 0)
return -1;
/* first process builtins - success means path was found */
- if (ignore_lookup_in_rules(ignores->ign_internal, &path, ignored))
+ if (ignore_lookup_in_rules(out, ignores->ign_internal, &path))
goto cleanup;
/* next process files in the path */
git_vector_foreach(&ignores->ign_path, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(out, file, &path))
goto cleanup;
}
/* last process global ignores */
git_vector_foreach(&ignores->ign_global, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(out, file, &path))
goto cleanup;
}
- *ignored = 0;
-
cleanup:
git_attr_path__free(&path);
return 0;
@@ -335,8 +336,6 @@ int git_ignore_path_is_ignored(
int error;
const char *workdir;
git_attr_path path;
- char *tail, *end;
- bool full_is_dir;
git_ignores ignores;
unsigned int i;
git_attr_file *file;
@@ -345,55 +344,42 @@ int git_ignore_path_is_ignored(
workdir = repo ? git_repository_workdir(repo) : NULL;
- if ((error = git_attr_path__init(&path, pathname, workdir)) < 0)
- return error;
+ memset(&path, 0, sizeof(path));
+ memset(&ignores, 0, sizeof(ignores));
- tail = path.path;
- end = &path.full.ptr[path.full.size];
- full_is_dir = path.is_dir;
+ if ((error = git_attr_path__init(&path, pathname, workdir)) < 0 ||
+ (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
+ goto cleanup;
while (1) {
- /* advance to next component of path */
- path.basename = tail;
-
- while (tail < end && *tail != '/') tail++;
- *tail = '\0';
-
- path.full.size = (tail - path.full.ptr);
- path.is_dir = (tail == end) ? full_is_dir : true;
-
- /* initialize ignores the first time through */
- if (path.basename == path.path &&
- (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
- break;
-
/* first process builtins - success means path was found */
- if (ignore_lookup_in_rules(ignores.ign_internal, &path, ignored))
+ if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path))
goto cleanup;
/* next process files in the path */
git_vector_foreach(&ignores.ign_path, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(ignored, file, &path))
goto cleanup;
}
/* last process global ignores */
git_vector_foreach(&ignores.ign_global, i, file) {
- if (ignore_lookup_in_rules(file, &path, ignored))
+ if (ignore_lookup_in_rules(ignored, file, &path))
goto cleanup;
}
- /* if we found no rules before reaching the end, we're done */
- if (tail == end)
+ /* move up one directory */
+ if (path.basename == path.path)
break;
-
- /* now add this directory to list of ignores */
- if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
+ path.basename[-1] = '\0';
+ while (path.basename > path.path && *path.basename != '/')
+ path.basename--;
+ if (path.basename > path.path)
+ path.basename++;
+ path.is_dir = 1;
+
+ 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,
diff --git a/src/ignore.h b/src/ignore.h
index ff9369000..77668c661 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -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
diff --git a/src/index.c b/src/index.c
index 8a7f29279..b63a0bec6 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1104,6 +1104,15 @@ int git_index_remove_bypath(git_index *index, const char *path)
return 0;
}
+static bool valid_filemode(const int filemode)
+{
+ return (filemode == GIT_FILEMODE_BLOB ||
+ filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
+ filemode == GIT_FILEMODE_LINK ||
+ filemode == GIT_FILEMODE_COMMIT);
+}
+
+
int git_index_add(git_index *index, const git_index_entry *source_entry)
{
git_index_entry *entry = NULL;
@@ -1111,6 +1120,11 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
assert(index && source_entry && source_entry->path);
+ if (!valid_filemode(source_entry->mode)) {
+ giterr_set(GITERR_INDEX, "invalid filemode");
+ return -1;
+ }
+
if ((ret = index_entry_dup(&entry, source_entry)) < 0 ||
(ret = index_insert(index, &entry, 1)) < 0)
return ret;
diff --git a/src/indexer.c b/src/indexer.c
index 3c8415c7c..25c3d0537 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -34,7 +34,6 @@ struct git_indexer {
have_delta :1;
struct git_pack_header hdr;
struct git_pack_file *pack;
- git_filebuf pack_file;
unsigned int mode;
git_off_t off;
git_off_t entry_start;
@@ -67,33 +66,18 @@ const git_oid *git_indexer_hash(const git_indexer *idx)
return &idx->hash;
}
-static int open_pack(struct git_pack_file **out, const char *filename)
-{
- struct git_pack_file *pack;
-
- if (git_packfile_alloc(&pack, filename) < 0)
- return -1;
-
- if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
- giterr_set(GITERR_OS, "Failed to open packfile.");
- git_packfile_free(pack);
- return -1;
- }
-
- *out = pack;
- return 0;
-}
-
static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
{
int error;
+ git_map map;
- /* 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");
+ 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 (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
giterr_set(GITERR_INDEXER, "Wrong pack signature");
return -1;
@@ -124,9 +108,9 @@ int git_indexer_new(
void *progress_payload)
{
git_indexer *idx;
- git_buf path = GIT_BUF_INIT;
+ git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT;
static const char suff[] = "/pack";
- int error;
+ int error, fd = -1;
idx = git__calloc(1, sizeof(git_indexer));
GITERR_CHECK_ALLOC(idx);
@@ -140,19 +124,30 @@ int git_indexer_new(
if (error < 0)
goto cleanup;
- error = git_filebuf_open(&idx->pack_file, path.ptr,
- GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
- idx->mode);
+ fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode);
git_buf_free(&path);
+ if (fd < 0)
+ goto cleanup;
+
+ error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path));
+ git_buf_free(&tmp_path);
+
if (error < 0)
goto cleanup;
+ idx->pack->mwf.fd = fd;
+ if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
+ goto cleanup;
+
*out = idx;
return 0;
cleanup:
+ if (fd != -1)
+ p_close(fd);
+
git_buf_free(&path);
- git_filebuf_cleanup(&idx->pack_file);
+ git_buf_free(&tmp_path);
git__free(idx);
return -1;
}
@@ -429,6 +424,42 @@ static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
idx->inbuf_len += size - to_expell;
}
+static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size)
+{
+ git_file fd = idx->pack->mwf.fd;
+ long page_size = git__page_size();
+ git_off_t page_start, page_offset;
+ unsigned char *map_data;
+ git_map map;
+ int error;
+
+ /* the offset needs to be at the beginning of the a page boundary */
+ page_start = (offset / page_size) * page_size;
+ page_offset = offset - page_start;
+
+ if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
+ return error;
+
+ map_data = (unsigned char *)map.data;
+ memcpy(map_data + page_offset, data, size);
+ p_munmap(&map);
+
+ return 0;
+}
+
+static int append_to_pack(git_indexer *idx, const void *data, size_t size)
+{
+ git_off_t current_size = idx->pack->mwf.size;
+
+ /* add the extra space we need at the end */
+ if (p_ftruncate(idx->pack->mwf.fd, current_size + size) < 0) {
+ giterr_system_set(errno);
+ return -1;
+ }
+
+ return write_at(idx, data, idx->pack->mwf.size, size);
+}
+
int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats)
{
int error = -1;
@@ -440,22 +471,13 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran
processed = stats->indexed_objects;
- if ((error = git_filebuf_write(&idx->pack_file, data, size)) < 0)
+ if ((error = append_to_pack(idx, data, size)) < 0)
return error;
hash_partially(idx, data, (int)size);
/* Make sure we set the new size of the pack */
- if (idx->opened_pack) {
- idx->pack->mwf.size += size;
- } else {
- if ((error = open_pack(&idx->pack, idx->pack_file.path_lock)) < 0)
- return error;
- idx->opened_pack = 1;
- mwf = &idx->pack->mwf;
- if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
- return error;
- }
+ idx->pack->mwf.size += size;
if (!idx->parsed_header) {
unsigned int total_objects;
@@ -616,17 +638,10 @@ static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
* Rewind the packfile by the trailer, as we might need to fix the
* packfile by injecting objects at the tail and must overwrite it.
*/
-static git_off_t seek_back_trailer(git_indexer *idx)
+static void seek_back_trailer(git_indexer *idx)
{
- git_off_t off;
-
- if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
- return -1;
-
idx->pack->mwf.size -= GIT_OID_RAWSZ;
git_mwindow_free_all(&idx->pack->mwf);
-
- return off;
}
static int inject_object(git_indexer *idx, git_oid *id)
@@ -642,7 +657,8 @@ static int inject_object(git_indexer *idx, git_oid *id)
size_t len, hdr_len;
int error;
- entry_start = seek_back_trailer(idx);
+ seek_back_trailer(idx);
+ entry_start = idx->pack->mwf.size;
if (git_odb_read(&obj, idx->odb, id) < 0)
return -1;
@@ -657,7 +673,9 @@ static int inject_object(git_indexer *idx, git_oid *id)
/* Write out the object header */
hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
- git_filebuf_write(&idx->pack_file, hdr, hdr_len);
+ if ((error = append_to_pack(idx, hdr, hdr_len)) < 0)
+ goto cleanup;
+
idx->pack->mwf.size += hdr_len;
entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
@@ -665,13 +683,16 @@ static int inject_object(git_indexer *idx, git_oid *id)
goto cleanup;
/* And then the compressed object */
- git_filebuf_write(&idx->pack_file, buf.ptr, buf.size);
+ if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0)
+ goto cleanup;
+
idx->pack->mwf.size += buf.size;
entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
git_buf_free(&buf);
/* Write a fake trailer so the pack functions play ball */
- if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
+
+ if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0)
goto cleanup;
idx->pack->mwf.size += GIT_OID_RAWSZ;
@@ -703,7 +724,7 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
size_t size;
git_otype type;
git_mwindow *w = NULL;
- git_off_t curpos;
+ git_off_t curpos = 0;
unsigned char *base_info;
unsigned int left = 0;
git_oid base;
@@ -817,19 +838,12 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
ctx = &idx->trailer;
git_hash_ctx_init(ctx);
- git_mwindow_free_all(mwf);
+
/* Update the header to include the numer of local objects we injected */
idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
- if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
- giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
+ if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
return -1;
- }
-
- if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
- giterr_set(GITERR_OS, "failed to update the pack header");
- return -1;
- }
/*
* We now use the same technique as before to determine the
@@ -837,6 +851,7 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta
* hash_partially() keep the existing trailer out of the
* calculation.
*/
+ git_mwindow_free_all(mwf);
idx->inbuf_len = 0;
while (hashed < mwf->size) {
ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
@@ -906,13 +921,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
return -1;
git_hash_final(&trailer_hash, &idx->trailer);
- if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
- return -1;
-
- if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
- giterr_set(GITERR_OS, "failed to update pack trailer");
- return -1;
- }
+ write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ);
}
git_vector_sort(&idx->objects);
@@ -995,14 +1004,18 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
git_mwindow_free_all(&idx->pack->mwf);
/* We need to close the descriptor here so Windows doesn't choke on commit_at */
- p_close(idx->pack->mwf.fd);
+ if (p_close(idx->pack->mwf.fd) < 0) {
+ giterr_set(GITERR_OS, "failed to close packfile");
+ goto on_error;
+ }
+
idx->pack->mwf.fd = -1;
if (index_path(&filename, idx, ".pack") < 0)
goto on_error;
+
/* And don't forget to rename the packfile to its new place. */
- if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0)
- return -1;
+ p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
git_buf_free(&filename);
return 0;
@@ -1022,7 +1035,7 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->objects);
- if (idx->pack) {
+ if (idx->pack && idx->pack->idx_cache) {
struct git_pack_entry *pentry;
kh_foreach_value(
idx->pack->idx_cache, pentry, { git__free(pentry); });
@@ -1032,6 +1045,5 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->deltas);
git_packfile_free(idx->pack);
- git_filebuf_cleanup(&idx->pack_file);
git__free(idx);
}
diff --git a/src/iterator.c b/src/iterator.c
index 4f8087c8d..c664f17cd 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -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 == GIT_IGNORE_TRUE);
+}
+
+bool git_iterator_current_tree_is_ignored(git_iterator *iter)
+{
+ workdir_iterator *wi = (workdir_iterator *)iter;
+
+ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
+ return false;
- return (bool)wi->is_ignored;
+ return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE);
}
int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
@@ -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 {
diff --git a/src/iterator.h b/src/iterator.h
index f67830212..d88ad5191 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -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);
diff --git a/src/map.h b/src/map.h
index da3d1e19a..722eb7a30 100644
--- a/src/map.h
+++ b/src/map.h
@@ -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__ */
diff --git a/src/merge.c b/src/merge.c
index 6a8e5874f..a279d31d4 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -2564,8 +2564,42 @@ done:
return error;
}
+static int merge_preference(git_merge_preference_t *out, git_repository *repo)
+{
+ git_config *config;
+ const char *value;
+ int bool_value, error = 0;
+
+ *out = GIT_MERGE_PREFERENCE_NONE;
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ goto done;
+
+ if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if (git_config_parse_bool(&bool_value, value) == 0) {
+ if (!bool_value)
+ *out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD;
+ } else {
+ if (strcasecmp(value, "only") == 0)
+ *out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY;
+ }
+
+done:
+ git_config_free(config);
+ return error;
+}
+
int git_merge_analysis(
- git_merge_analysis_t *out,
+ git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
git_repository *repo,
const git_merge_head **their_heads,
size_t their_heads_len)
@@ -2573,14 +2607,7 @@ int git_merge_analysis(
git_merge_head *ancestor_head = NULL, *our_head = NULL;
int error = 0;
- assert(out && repo && their_heads);
-
- *out = GIT_MERGE_ANALYSIS_NONE;
-
- if (git_repository_head_unborn(repo)) {
- *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
- goto done;
- }
+ assert(analysis_out && preference_out && repo && their_heads);
if (their_heads_len != 1) {
giterr_set(GITERR_MERGE, "Can only merge a single branch");
@@ -2588,20 +2615,30 @@ int git_merge_analysis(
goto done;
}
+ *analysis_out = GIT_MERGE_ANALYSIS_NONE;
+
+ if ((error = merge_preference(preference_out, repo)) < 0)
+ goto done;
+
+ if (git_repository_head_unborn(repo)) {
+ *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
+ goto done;
+ }
+
if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
goto done;
/* We're up-to-date if we're trying to merge our own common ancestor. */
if (ancestor_head && git_oid_equal(&ancestor_head->oid, &their_heads[0]->oid))
- *out = GIT_MERGE_ANALYSIS_UP_TO_DATE;
+ *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE;
/* We're fastforwardable if we're our own common ancestor. */
else if (ancestor_head && git_oid_equal(&ancestor_head->oid, &our_head->oid))
- *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
+ *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
/* Otherwise, just a normal merge is possible. */
else
- *out = GIT_MERGE_ANALYSIS_NORMAL;
+ *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
done:
git_merge_head_free(ancestor_head);
diff --git a/src/message.c b/src/message.c
index 07b2569ad..6c5a2379f 100644
--- a/src/message.c
+++ b/src/message.c
@@ -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);
diff --git a/src/netops.c b/src/netops.c
index 24092c17f..a0193a022 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -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;
diff --git a/src/odb.c b/src/odb.c
index 20a3f6c6e..a4fc02686 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -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;
diff --git a/src/pack.c b/src/pack.c
index de038a45c..ace7abb58 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -83,16 +83,12 @@ static void cache_free(git_pack_cache *cache)
}
git_offmap_free(cache->entries);
- git_mutex_free(&cache->lock);
+ cache->entries = NULL;
}
-
- memset(cache, 0, sizeof(*cache));
}
static int cache_init(git_pack_cache *cache)
{
- memset(cache, 0, sizeof(*cache));
-
cache->entries = git_offmap_alloc();
GITERR_CHECK_ALLOC(cache->entries);
@@ -514,72 +510,102 @@ int git_packfile_resolve_header(
return error;
}
-static int packfile_unpack_delta(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_mwindow **w_curs,
- git_off_t *curpos,
- size_t delta_size,
- git_otype delta_type,
- git_off_t obj_offset)
+#define SMALL_STACK_SIZE 64
+
+/**
+ * Generate the chain of dependencies which we need to get to the
+ * object at `off`. `chain` is used a stack, popping gives the right
+ * order to apply deltas on. If an object is found in the pack's base
+ * cache, we stop calculating there.
+ */
+static int pack_dependency_chain(git_dependency_chain *chain_out,
+ git_pack_cache_entry **cached_out, git_off_t *cached_off,
+ struct pack_chain_elem *small_stack, size_t *stack_sz,
+ struct git_pack_file *p, git_off_t obj_offset)
{
- git_off_t base_offset, base_key;
- git_rawobj base, delta;
- git_pack_cache_entry *cached = NULL;
- int error, found_base = 0;
+ git_dependency_chain chain = GIT_ARRAY_INIT;
+ git_mwindow *w_curs = NULL;
+ git_off_t curpos = obj_offset, base_offset;
+ int error = 0, use_heap = 0;
+ size_t size, elem_pos;
+ git_otype type;
- base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
- git_mwindow_close(w_curs);
- if (base_offset == 0)
- return packfile_error("delta offset is zero");
- if (base_offset < 0) /* must actually be an error code */
- return (int)base_offset;
+ elem_pos = 0;
+ while (true) {
+ struct pack_chain_elem *elem;
+ git_pack_cache_entry *cached = NULL;
- if (!p->bases.entries && (cache_init(&p->bases) < 0))
- return -1;
+ /* if we have a base cached, we can stop here instead */
+ if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
+ *cached_out = cached;
+ *cached_off = obj_offset;
+ break;
+ }
- base_key = base_offset; /* git_packfile_unpack modifies base_offset */
- if ((cached = cache_get(&p->bases, base_offset)) != NULL) {
- memcpy(&base, &cached->raw, sizeof(git_rawobj));
- found_base = 1;
- }
+ /* if we run out of space on the small stack, use the array */
+ if (elem_pos == SMALL_STACK_SIZE) {
+ git_array_init_to_size(chain, elem_pos);
+ GITERR_CHECK_ARRAY(chain);
+ memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
+ chain.size = elem_pos;
+ use_heap = 1;
+ }
+
+ curpos = obj_offset;
+ if (!use_heap) {
+ elem = &small_stack[elem_pos];
+ } else {
+ elem = git_array_alloc(chain);
+ if (!elem) {
+ error = -1;
+ goto on_error;
+ }
+ }
- if (!cached) { /* have to inflate it */
- error = git_packfile_unpack(&base, p, &base_offset);
+ elem->base_key = obj_offset;
+
+ error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+ git_mwindow_close(&w_curs);
- /*
- * TODO: git.git tries to load the base from other packfiles
- * or loose objects.
- *
- * We'll need to do this in order to support thin packs.
- */
if (error < 0)
- return error;
- }
+ goto on_error;
- error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
- git_mwindow_close(w_curs);
+ elem->offset = curpos;
+ elem->size = size;
+ elem->type = type;
+ elem->base_key = obj_offset;
- if (error < 0) {
- if (!found_base)
- git__free(base.data);
- return error;
- }
+ if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
+ break;
- obj->type = base.type;
- error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
- if (error < 0)
- goto on_error;
+ base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+ git_mwindow_close(&w_curs);
- if (found_base)
- git_atomic_dec(&cached->refcount);
- else if (cache_add(&p->bases, &base, base_key) < 0)
- git__free(base.data);
+ 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;
+ }
-on_error:
- git__free(delta.data);
+ /* 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;
- return error; /* error set by git__delta_apply */
+on_error:
+ 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;
- if (error < 0)
- return error;
+ 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;
+ }
- 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;
+ if (error < 0)
+ 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;
+ }
+
+ /*
+ * 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;
}
- *obj_offset = curpos;
+ /* 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;
diff --git a/src/pack.h b/src/pack.h
index 58f81e2f0..610e70c18 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -17,6 +17,7 @@
#include "mwindow.h"
#include "odb.h"
#include "oidmap.h"
+#include "array.h"
#define GIT_PACK_FILE_MODE 0444
@@ -60,6 +61,15 @@ typedef struct git_pack_cache_entry {
git_rawobj raw;
} git_pack_cache_entry;
+struct pack_chain_elem {
+ git_off_t base_key;
+ git_off_t offset;
+ size_t size;
+ git_otype type;
+};
+
+typedef git_array_t(struct pack_chain_elem) git_dependency_chain;
+
#include "offmap.h"
GIT__USE_OFFMAP;
diff --git a/src/path.c b/src/path.c
index 0c1013fa0..377f396d3 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1135,3 +1135,21 @@ int git_path_dirload_with_stat(
return error;
}
+
+int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
+{
+ int error;
+
+ /* If url_or_path begins with file:// treat it as a URL */
+ if (!git__prefixcmp(url_or_path, "file://")) {
+ if ((error = git_path_fromurl(local_path_out, url_or_path)) < 0) {
+ return error;
+ }
+ } else { /* We assume url_or_path is already a path */
+ if ((error = git_buf_sets(local_path_out, url_or_path)) < 0) {
+ return error;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/path.h b/src/path.h
index 3213c5104..3e6efe3de 100644
--- a/src/path.h
+++ b/src/path.h
@@ -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
diff --git a/src/posix.c b/src/posix.c
index 7484ac0d8..7aeb0e6c1 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -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);
diff --git a/src/posix.h b/src/posix.h
index f85b1aebd..965cd98d5 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -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;
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index f9bd4eab5..0e36ca8ac 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -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;
-
- /* Defaults to the opposite of the repo being bare */
- is_bare = git_repository_is_bare(repo);
- logall = !is_bare;
+ int error, logall;
- if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES);
+ if (error < 0)
return error;
- error = git_config_get_bool(&logall, config, "core.logallrefupdates");
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
+ /* Defaults to the opposite of the repo being bare */
+ if (logall == GIT_LOGALLREFUPDATES_UNSET)
+ logall = !git_repository_is_bare(repo);
if (!logall) {
*write = 0;
diff --git a/src/refs.c b/src/refs.c
index 9428f617d..1603876da 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -159,8 +159,7 @@ int git_reference_name_to_id(
}
static int reference_normalize_for_repo(
- char *out,
- size_t out_size,
+ git_refname_t out,
git_repository *repo,
const char *name)
{
@@ -171,7 +170,7 @@ static int reference_normalize_for_repo(
precompose)
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
- return git_reference_normalize_name(out, out_size, name, flags);
+ return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
}
int git_reference_lookup_resolved(
@@ -180,7 +179,7 @@ int git_reference_lookup_resolved(
const char *name,
int max_nesting)
{
- char scan_name[GIT_REFNAME_MAX];
+ git_refname_t scan_name;
git_ref_t scan_type;
int error = 0, nesting;
git_reference *ref = NULL;
@@ -197,8 +196,7 @@ int git_reference_lookup_resolved(
scan_type = GIT_REF_SYMBOLIC;
- if ((error = reference_normalize_for_repo(
- scan_name, sizeof(scan_name), repo, name)) < 0)
+ if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
return error;
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
@@ -354,7 +352,7 @@ static int reference__create(
const git_oid *old_id,
const char *old_target)
{
- char normalized[GIT_REFNAME_MAX];
+ git_refname_t normalized;
git_refdb *refdb;
git_reference *ref = NULL;
int error = 0;
@@ -365,7 +363,7 @@ static int reference__create(
if (ref_out)
*ref_out = NULL;
- error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name);
+ error = reference_normalize_for_repo(normalized, repo, name);
if (error < 0)
return error;
@@ -388,15 +386,14 @@ static int reference__create(
return -1;
}
- ref = git_reference__alloc(name, oid, NULL);
+ ref = git_reference__alloc(normalized, oid, NULL);
} else {
- char normalized_target[GIT_REFNAME_MAX];
+ git_refname_t normalized_target;
- if ((error = git_reference__normalize_name_lax(
- normalized_target, sizeof(normalized_target), symbolic)) < 0)
+ if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
return error;
- ref = git_reference__alloc_symbolic(name, normalized_target);
+ ref = git_reference__alloc_symbolic(normalized, normalized_target);
}
GITERR_CHECK_ALLOC(ref);
@@ -569,18 +566,14 @@ int git_reference_symbolic_set_target(
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
const git_signature *signature, const char *message)
{
- unsigned int normalization_flags;
- char normalized[GIT_REFNAME_MAX];
+ git_refname_t normalized;
bool should_head_be_updated = false;
int error = 0;
assert(ref && new_name && signature);
- normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
- GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
-
- if ((error = git_reference_normalize_name(
- normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
+ if ((error = reference_normalize_for_repo(
+ normalized, git_reference_owner(ref), new_name)) < 0)
return error;
@@ -590,12 +583,12 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
should_head_be_updated = (error > 0);
- if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, signature, message)) < 0)
+ if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
return error;
/* Update HEAD it was pointing to the reference being renamed */
if (should_head_be_updated &&
- (error = git_repository_set_head(ref->db->repo, new_name, signature, message)) < 0) {
+ (error = git_repository_set_head(ref->db->repo, normalized, signature, message)) < 0) {
giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
return error;
}
@@ -1018,17 +1011,6 @@ cleanup:
return error;
}
-int git_reference__normalize_name_lax(
- char *buffer_out,
- size_t out_size,
- const char *name)
-{
- return git_reference_normalize_name(
- buffer_out,
- out_size,
- name,
- GIT_REF_FORMAT_ALLOW_ONELEVEL);
-}
#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
int git_reference_cmp(
diff --git a/src/refs.h b/src/refs.h
index d57d67026..f75a4bf7e 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -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);
diff --git a/src/remote.c b/src/remote.c
index 31ee97795..0c82433d1 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -403,6 +403,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
if (!optional_setting_found) {
error = GIT_ENOTFOUND;
+ giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
goto cleanup;
}
@@ -1304,13 +1305,14 @@ static int rename_remote_config_section(
if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
goto cleanup;
- if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
- goto cleanup;
+ if (new_name &&
+ (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0))
+ goto cleanup;
error = git_config_rename_section(
repo,
git_buf_cstr(&old_section_name),
- git_buf_cstr(&new_section_name));
+ new_name ? git_buf_cstr(&new_section_name) : NULL);
cleanup:
git_buf_free(&old_section_name);
@@ -1743,3 +1745,217 @@ int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
return 0;
}
+
+/* asserts a branch.<foo>.remote format */
+static const char *name_offset(size_t *len_out, const char *name)
+{
+ size_t prefix_len;
+ const char *dot;
+
+ prefix_len = strlen("remote.");
+ dot = strchr(name + prefix_len, '.');
+
+ assert(dot);
+
+ *len_out = dot - name - prefix_len;
+ return name + prefix_len;
+}
+
+static int remove_branch_config_related_entries(
+ git_repository *repo,
+ const char *remote_name)
+{
+ int error;
+ git_config *config;
+ git_config_entry *entry;
+ git_config_iterator *iter;
+ git_buf buf = GIT_BUF_INIT;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
+ return error;
+
+ /* find any branches with us as upstream and remove that config */
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ const char *branch;
+ size_t branch_len;
+
+ if (strcmp(remote_name, entry->value))
+ continue;
+
+ branch = name_offset(&branch_len, entry->name);
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
+ break;
+
+ if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
+ break;
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
+ break;
+
+ if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
+ break;
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_buf_free(&buf);
+ git_config_iterator_free(iter);
+ return error;
+}
+
+static int remove_refs(git_repository *repo, const git_refspec *spec)
+{
+ git_reference_iterator *iter = NULL;
+ git_vector refs;
+ const char *name;
+ char *dup;
+ int error;
+ size_t i;
+
+ if ((error = git_vector_init(&refs, 8, NULL)) < 0)
+ return error;
+
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ goto cleanup;
+
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ if (!git_refspec_dst_matches(spec, name))
+ continue;
+
+ dup = git__strdup(name);
+ if (!dup) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_vector_insert(&refs, dup)) < 0)
+ goto cleanup;
+ }
+ if (error == GIT_ITEROVER)
+ error = 0;
+ if (error < 0)
+ goto cleanup;
+
+ git_vector_foreach(&refs, i, name) {
+ if ((error = git_reference_remove(repo, name)) < 0)
+ break;
+ }
+
+cleanup:
+ git_reference_iterator_free(iter);
+ git_vector_foreach(&refs, i, dup) {
+ git__free(dup);
+ }
+ git_vector_free(&refs);
+ return error;
+}
+
+static int remove_remote_tracking(git_repository *repo, const char *remote_name)
+{
+ git_remote *remote;
+ int error;
+ size_t i, count;
+
+ /* we want to use what's on the config, regardless of changes to the instance in memory */
+ if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
+ return error;
+
+ count = git_remote_refspec_count(remote);
+ for (i = 0; i < count; i++) {
+ const git_refspec *refspec = git_remote_get_refspec(remote, i);
+
+ /* shouldn't ever actually happen */
+ if (refspec == NULL)
+ continue;
+
+ if ((error = remove_refs(repo, refspec)) < 0)
+ break;
+ }
+
+ git_remote_free(remote);
+ return error;
+}
+
+int git_remote_delete(git_remote *remote)
+{
+ int error;
+ git_repository *repo;
+
+ assert(remote);
+
+ if (!remote->name) {
+ giterr_set(GITERR_INVALID, "Can't delete an anonymous remote.");
+ return -1;
+ }
+
+ repo = git_remote_owner(remote);
+
+ if ((error = remove_branch_config_related_entries(repo,
+ git_remote_name(remote))) < 0)
+ return error;
+
+ if ((error = remove_remote_tracking(repo, git_remote_name(remote))) < 0)
+ return error;
+
+ if ((error = rename_remote_config_section(
+ repo, git_remote_name(remote), NULL)) < 0)
+ return error;
+
+ git_remote_free(remote);
+
+ return 0;
+}
+
+int git_remote_default_branch(git_buf *out, git_remote *remote)
+{
+ const git_remote_head **heads;
+ const git_remote_head *guess = NULL;
+ const git_oid *head_id;
+ size_t heads_len, i;
+ int error;
+
+ if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
+ return error;
+
+ if (heads_len == 0)
+ return GIT_ENOTFOUND;
+
+ git_buf_sanitize(out);
+ /* the first one must be HEAD so if that has the symref info, we're done */
+ if (heads[0]->symref_target)
+ return git_buf_puts(out, heads[0]->symref_target);
+
+ /*
+ * If there's no symref information, we have to look over them
+ * and guess. We return the first match unless the master
+ * branch is a candidate. Then we return the master branch.
+ */
+ head_id = &heads[0]->oid;
+
+ for (i = 1; i < heads_len; i++) {
+ if (git_oid_cmp(head_id, &heads[i]->oid))
+ continue;
+
+ if (!guess) {
+ guess = heads[i];
+ continue;
+ }
+
+ if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
+ guess = heads[i];
+ break;
+ }
+ }
+
+ if (!guess)
+ return GIT_ENOTFOUND;
+
+ return git_buf_puts(out, guess->name);
+}
diff --git a/src/repository.c b/src/repository.c
index 7d055e28e..e8d50aed3 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -443,7 +443,6 @@ int git_repository_open_ext(
int error;
git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
git_repository *repo;
- git_config *config;
if (repo_ptr)
*repo_ptr = NULL;
@@ -458,23 +457,24 @@ int git_repository_open_ext(
repo->path_repository = git_buf_detach(&path);
GITERR_CHECK_ALLOC(repo->path_repository);
- if ((error = git_repository_config_snapshot(&config, repo)) < 0)
- return error;
-
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
repo->is_bare = 1;
- else if ((error = load_config_data(repo, config)) < 0 ||
- (error = load_workdir(repo, config, &parent)) < 0)
- {
+ else {
+ git_config *config = NULL;
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0 ||
+ (error = load_config_data(repo, config)) < 0 ||
+ (error = load_workdir(repo, config, &parent)) < 0)
+ git_repository_free(repo);
+
git_config_free(config);
- git_repository_free(repo);
- return error;
}
- git_config_free(config);
+ if (!error)
+ *repo_ptr = repo;
git_buf_free(&parent);
- *repo_ptr = repo;
- return 0;
+
+ return error;
}
int git_repository_open(git_repository **repo_out, const char *path)
@@ -1190,6 +1190,7 @@ static int repo_init_structure(
bool external_tpl =
((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
mode_t dmode = pick_dir_mode(opts);
+ bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
/* Hide the ".git" directory */
#ifdef GIT_WIN32
@@ -1230,10 +1231,12 @@ static int repo_init_structure(
default_template = true;
}
- if (tdir)
- error = git_futils_cp_r(tdir, repo_dir,
- GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
- GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+ if (tdir) {
+ uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE;
+ if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
+ cpflags |= GIT_CPDIR_CHMOD_DIRS;
+ error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
+ }
git_buf_free(&template_buf);
git_config_free(cfg);
@@ -1254,9 +1257,14 @@ static int repo_init_structure(
* - only create files if no external template was specified
*/
for (tpl = repo_template; !error && tpl->path; ++tpl) {
- if (!tpl->content)
+ if (!tpl->content) {
+ uint32_t mkdir_flags = GIT_MKDIR_PATH;
+ if (chmod)
+ mkdir_flags |= GIT_MKDIR_CHMOD;
+
error = git_futils_mkdir(
- tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
+ tpl->path, repo_dir, dmode, mkdir_flags);
+ }
else if (!external_tpl) {
const char *content = tpl->content;
diff --git a/src/repository.h b/src/repository.h
index 27eec9dd8..aba16a016 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -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 */
diff --git a/src/tag.c b/src/tag.c
index 1a4ee1e1c..d7b531d34 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -363,20 +363,22 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
}
/* write the buffer */
- if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
- return -1;
+ if ((error = git_odb_open_wstream(
+ &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0)
+ return error;
- git_odb_stream_write(stream, buffer, strlen(buffer));
+ if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
+ error = git_odb_stream_finalize_write(oid, stream);
- error = git_odb_stream_finalize_write(oid, stream);
git_odb_stream_free(stream);
if (error < 0) {
git_buf_free(&ref_name);
- return -1;
+ return error;
}
- error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL);
+ error = git_reference_create(
+ &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL);
git_reference_free(new_ref);
git_buf_free(&ref_name);
diff --git a/src/transports/local.c b/src/transports/local.c
index 2c17e6271..f859f0b70 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -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);
diff --git a/src/transports/smart.c b/src/transports/smart.c
index 69eaf9b78..a5c3e82dc 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -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);
diff --git a/src/transports/smart.h b/src/transports/smart.h
index a2b6b2a71..f1fc29520 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -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);
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index e9376ae6f..b5f9d6dbe 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -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) {
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 5dd6bab24..a52aacc60 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -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:
diff --git a/src/unix/map.c b/src/unix/map.c
index e62ab3e76..3d0cbbaf8 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -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;
diff --git a/src/util.h b/src/util.h
index 6fb2dc0f4..ca676c039 100644
--- a/src/util.h
+++ b/src/util.h
@@ -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)
diff --git a/src/win32/map.c b/src/win32/map.c
index 902ea3994..ef83f882e 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -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);
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 8f51d6f5a..059e39cbc 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -10,6 +10,7 @@
#if defined(__MINGW32__)
/* use a 64-bit file offset type */
+# undef lseek
# define lseek _lseeki64
# undef stat
# define stat _stati64
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 7f9d57cc3..2cbea1807 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -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);
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 0d070f6b5..34938431a 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -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)
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
index b2730f4d1..0a4c3e8e5 100644
--- a/tests/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -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);
+}
+
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
index f84d9e353..da37bd655 100644
--- a/tests/clar_libgit2.h
+++ b/tests/clar_libgit2.h
@@ -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
diff --git a/tests/clone/local.c b/tests/clone/local.c
new file mode 100644
index 000000000..a4406c1cc
--- /dev/null
+++ b/tests/clone/local.c
@@ -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));
+}
diff --git a/tests/config/config_helpers.c b/tests/config/config_helpers.c
index 53bd945a0..35da720e0 100644
--- a/tests/config/config_helpers.c
+++ b/tests/config/config_helpers.c
@@ -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;
+}
diff --git a/tests/config/config_helpers.h b/tests/config/config_helpers.h
index b887b3d38..440645730 100644
--- a/tests/config/config_helpers.h
+++ b/tests/config/config_helpers.h
@@ -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);
diff --git a/tests/config/global.c b/tests/config/global.c
index 006b34628..fc471f90d 100644
--- a/tests/config/global.c
+++ b/tests/config/global.c
@@ -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)
diff --git a/tests/config/include.c b/tests/config/include.c
index 535573808..58bc690ff 100644
--- a/tests/config/include.c
+++ b/tests/config/include.c
@@ -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)
diff --git a/tests/core/copy.c b/tests/core/copy.c
index c0c59c056..04b2dfab5 100644
--- a/tests/core/copy.c
+++ b/tests/core/copy.c
@@ -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));
}
diff --git a/tests/core/env.c b/tests/core/env.c
index df1d92a02..293b786db 100644
--- a/tests/core/env.c
+++ b/tests/core/env.c
@@ -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"));
diff --git a/tests/diff/iterator.c b/tests/diff/iterator.c
index cdc64eb1d..a2df1c7a7 100644
--- a/tests/diff/iterator.c
+++ b/tests/diff/iterator.c
@@ -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[] = {
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
index a6d48abc6..963be9481 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -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);
+}
diff --git a/tests/filter/crlf.c b/tests/filter/crlf.c
index 66c267e31..a31dac965 100644
--- a/tests/filter/crlf.c
+++ b/tests/filter/crlf.c
@@ -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);
+}
diff --git a/tests/index/crlf.c b/tests/index/crlf.c
index cf69c6226..7babd5939 100644
--- a/tests/index/crlf.c
+++ b/tests/index/crlf.c
@@ -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"));
+}
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
index 013932696..58d7935a0 100644
--- a/tests/index/filemodes.c
+++ b/tests/index/filemodes.c
@@ -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);
+}
diff --git a/tests/main.c b/tests/main.c
index ffbbcbf48..3de4f9801 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -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();
diff --git a/tests/merge/workdir/analysis.c b/tests/merge/workdir/analysis.c
index 0e937857f..85918abe4 100644
--- a/tests/merge/workdir/analysis.c
+++ b/tests/merge/workdir/analysis.c
@@ -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));
+}
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
index 4c39394bb..0d23bef48 100644
--- a/tests/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -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");
+}
diff --git a/tests/network/remote/defaultbranch.c b/tests/network/remote/defaultbranch.c
new file mode 100644
index 000000000..fa3a329db
--- /dev/null
+++ b/tests/network/remote/defaultbranch.c
@@ -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");
+}
diff --git a/tests/network/remote/delete.c b/tests/network/remote/delete.c
new file mode 100644
index 000000000..db55b0768
--- /dev/null
+++ b/tests/network/remote/delete.c
@@ -0,0 +1,61 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+#include "repository.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+
+void test_network_remote_delete__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+}
+
+void test_network_remote_delete__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_delete__cannot_delete_an_anonymous_remote(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, _repo, "git://github.com/libgit2/libgit2", NULL));
+
+ cl_git_fail(git_remote_delete(remote));
+
+ git_remote_free(remote);
+ git_remote_free(_remote);
+}
+
+void test_network_remote_delete__remove_remote_tracking_branches(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_remote_delete(_remote));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, _repo, "refs/remotes/test/master"));
+}
+
+void test_network_remote_delete__remove_remote_configuration_settings(void)
+{
+ cl_assert(count_config_entries_match(_repo, "remote\\.test\\.+") > 0);
+
+ cl_git_pass(git_remote_delete(_remote));
+
+ cl_assert_equal_i(0, count_config_entries_match(_repo, "remote\\.test\\.+"));
+}
+
+void test_network_remote_delete__remove_branch_upstream_configuration_settings(void)
+{
+ assert_config_entry_existence(_repo, "branch.mergeless.remote", true);
+ assert_config_entry_existence(_repo, "branch.master.remote", true);
+
+ cl_git_pass(git_remote_delete(_remote));
+
+ assert_config_entry_existence(_repo, "branch.mergeless.remote", false);
+ assert_config_entry_existence(_repo, "branch.mergeless.merge", false);
+ assert_config_entry_existence(_repo, "branch.master.remote", false);
+ assert_config_entry_existence(_repo, "branch.master.merge", false);
+}
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
index 306ccaee5..333b52a5b 100644
--- a/tests/network/remote/remotes.c
+++ b/tests/network/remote/remotes.c
@@ -60,6 +60,15 @@ void test_network_remote_remotes__pushurl(void)
cl_assert(git_remote_pushurl(_remote) == NULL);
}
+void test_network_remote_remotes__error_when_not_found(void)
+{
+ git_remote *r;
+ cl_git_fail_with(git_remote_load(&r, _repo, "does-not-exist"), GIT_ENOTFOUND);
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_CONFIG);
+}
+
void test_network_remote_remotes__error_when_no_push_available(void)
{
git_remote *r;
diff --git a/tests/object/commit/commitstagedfile.c b/tests/object/commit/commitstagedfile.c
index 9758ea9a2..5b48519b8 100644
--- a/tests/object/commit/commitstagedfile.c
+++ b/tests/object/commit/commitstagedfile.c
@@ -112,7 +112,7 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
memset(&buffer, 0, sizeof(git_buf));
- cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0));
+ cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0, '#'));
cl_git_pass(git_commit_create_v(
&commit_oid,
diff --git a/tests/object/message.c b/tests/object/message.c
index ab5565341..40d8e7297 100644
--- a/tests/object/message.c
+++ b/tests/object/message.c
@@ -6,7 +6,7 @@ static void assert_message_prettifying(char *expected_output, char *input, int s
{
git_buf prettified_message = GIT_BUF_INIT;
- git_message_prettify(&prettified_message, input, strip_comments);
+ git_message_prettify(&prettified_message, input, strip_comments, '#');
cl_assert_equal_s(expected_output, git_buf_cstr(&prettified_message));
git_buf_free(&prettified_message);
@@ -175,25 +175,25 @@ void test_object_message__message_prettify(void)
git_buf buffer;
memset(&buffer, 0, sizeof(buffer));
- cl_git_pass(git_message_prettify(&buffer, "", 0));
+ cl_git_pass(git_message_prettify(&buffer, "", 0, '#'));
cl_assert_equal_s(buffer.ptr, "");
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "", 1));
+ cl_git_pass(git_message_prettify(&buffer, "", 1, '#'));
cl_assert_equal_s(buffer.ptr, "");
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "Short", 0));
+ cl_git_pass(git_message_prettify(&buffer, "Short", 0, '#'));
cl_assert_equal_s("Short\n", buffer.ptr);
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "Short", 1));
+ cl_git_pass(git_message_prettify(&buffer, "Short", 1, '#'));
cl_assert_equal_s("Short\n", buffer.ptr);
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0));
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0, '#'));
cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n# with some comments still in\n");
git_buf_free(&buffer);
- cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1));
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1, '#'));
cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n");
git_buf_free(&buffer);
}
diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c
index ab3808b00..56daf7574 100644
--- a/tests/odb/foreach.c
+++ b/tests/odb/foreach.c
@@ -93,8 +93,8 @@ void test_odb_foreach__files_in_objects_dir(void)
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
cl_git_pass(git_buf_printf(&buf, "%s/objects/somefile", git_repository_path(repo)));
-
cl_git_mkfile(buf.ptr, "");
+ git_buf_free(&buf);
cl_git_pass(git_repository_odb(&odb, repo));
cl_git_pass(git_odb_foreach(odb, foreach_cb, &nobj));
diff --git a/tests/online/clone.c b/tests/online/clone.c
index 6e0e63950..e269771c0 100644
--- a/tests/online/clone.c
+++ b/tests/online/clone.c
@@ -164,6 +164,39 @@ void test_online_clone__clone_into(void)
git_buf_free(&path);
}
+void test_online_clone__clone_mirror(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_remote *remote;
+ git_reference *head;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ bool fetch_progress_cb_was_called = false;
+
+ cl_git_pass(git_repository_init(&g_repo, "./foo.git", true));
+ cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
+
+ callbacks.transfer_progress = &fetch_progress;
+ callbacks.payload = &fetch_progress_cb_was_called;
+ git_remote_set_callbacks(remote, &callbacks);
+
+ git_remote_clear_refspecs(remote);
+ cl_git_pass(git_remote_add_fetch(remote, "+refs/*:refs/*"));
+
+ cl_git_pass(git_clone_into(g_repo, remote, NULL, NULL, NULL));
+
+ cl_git_pass(git_reference_lookup(&head, g_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));
+
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+ git_remote_free(remote);
+ git_reference_free(head);
+ git_buf_free(&path);
+ cl_fixture_cleanup("./foo.git");
+}
+
static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
{
int *callcount = (int*)payload;
diff --git a/tests/online/fetch.c b/tests/online/fetch.c
index c54ec5673..f03a6faa6 100644
--- a/tests/online/fetch.c
+++ b/tests/online/fetch.c
@@ -184,3 +184,21 @@ void test_online_fetch__ls_disconnected(void)
git_remote_free(remote);
}
+
+void test_online_fetch__remote_symrefs(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ git_remote_disconnect(remote);
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_s("HEAD", refs[0]->name);
+ cl_assert_equal_s("refs/heads/master", refs[0]->symref_target);
+
+ git_remote_free(remote);
+}
diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c
index 53db81828..1059424ed 100644
--- a/tests/pack/packbuilder.c
+++ b/tests/pack/packbuilder.c
@@ -17,6 +17,7 @@ static git_transfer_progress _stats;
void test_pack_packbuilder__initialize(void)
{
_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(p_chdir("testrepo.git"));
cl_git_pass(git_revwalk_new(&_revwalker, _repo));
cl_git_pass(git_packbuilder_new(&_packbuilder, _repo));
cl_git_pass(git_vector_init(&_commits, 0, NULL));
@@ -46,6 +47,7 @@ void test_pack_packbuilder__cleanup(void)
git_indexer_free(_indexer);
_indexer = NULL;
+ p_chdir("..");
cl_git_sandbox_cleanup();
_repo = NULL;
}
diff --git a/tests/refs/branches/create.c b/tests/refs/branches/create.c
index 864640ab3..3a4f33b6e 100644
--- a/tests/refs/branches/create.c
+++ b/tests/refs/branches/create.c
@@ -179,11 +179,14 @@ void test_refs_branches_create__can_create_branch_with_unicode(void)
expected[0] = nfd;
for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ const char *name;
cl_git_pass(git_branch_create(
&branch, repo, names[i], target, 0, NULL, NULL));
cl_git_pass(git_oid_cmp(
git_reference_target(branch), git_commit_id(target)));
+ cl_git_pass(git_branch_name(&name, branch));
+ cl_assert_equal_s(expected[i], name);
assert_branch_matches_name(expected[i], names[i]);
if (fs_decompose_unicode && alt[i])
assert_branch_matches_name(expected[i], alt[i]);
diff --git a/tests/refs/branches/move.c b/tests/refs/branches/move.c
index 6c6dbbe21..f136b00d6 100644
--- a/tests/refs/branches/move.c
+++ b/tests/refs/branches/move.c
@@ -241,3 +241,20 @@ void test_refs_branches_move__default_reflog_message(void)
git_reflog_free(log);
git_signature_free(sig);
}
+
+void test_refs_branches_move__can_move_with_unicode(void)
+{
+ git_reference *original_ref, *new_ref;
+ const char *new_branch_name = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+ cl_git_pass(git_branch_move(&new_ref, original_ref, new_branch_name, 0, NULL, NULL));
+
+ if (cl_repo_get_bool(repo, "core.precomposeunicode"))
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR "\xC3\x85\x73\x74\x72\xC3\xB6\x6D", git_reference_name(new_ref));
+ else
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D", git_reference_name(new_ref));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c
index a29b0cf8b..c77451309 100644
--- a/tests/refs/iterator.c
+++ b/tests/refs/iterator.c
@@ -186,3 +186,36 @@ void test_refs_iterator__foreach_name_can_cancel(void)
-333);
cl_assert_equal_i(0, cancel_after);
}
+
+void test_refs_iterator__concurrent_delete(void)
+{
+ git_reference_iterator *iter;
+ size_t full_count = 0, concurrent_count = 0;
+ const char *name;
+ int error;
+
+ git_repository_free(repo);
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ full_count++;
+ }
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ cl_git_pass(git_reference_remove(repo, name));
+ concurrent_count++;
+ }
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_assert_equal_i(full_count, concurrent_count);
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
diff --git a/tests/repo/config.c b/tests/repo/config.c
index 2e7be37aa..93dedd576 100644
--- a/tests/repo/config.c
+++ b/tests/repo/config.c
@@ -8,7 +8,8 @@ static git_buf path = GIT_BUF_INIT;
void test_repo_config__initialize(void)
{
cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
git_buf_clear(&path);
@@ -18,15 +19,19 @@ void test_repo_config__initialize(void)
void test_repo_config__cleanup(void)
{
- cl_git_pass(git_path_prettify(&path, "alternate", NULL));
- cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_sandbox_set_search_path_defaults();
+
git_buf_free(&path);
+
+ cl_git_pass(
+ git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("alternate"));
cl_fixture_cleanup("empty_standard_repo");
+
}
-void test_repo_config__open_missing_global(void)
+void test_repo_config__can_open_global_when_there_is_no_file(void)
{
git_repository *repo;
git_config *config, *global;
@@ -40,23 +45,23 @@ void test_repo_config__open_missing_global(void)
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
+ cl_git_pass(git_config_open_level(
+ &global, config, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_set_string(global, "test.set", "42"));
git_config_free(global);
git_config_free(config);
git_repository_free(repo);
-
- git_sysdir_global_shutdown();
}
-void test_repo_config__open_missing_global_with_separators(void)
+void test_repo_config__can_open_missing_global_with_separators(void)
{
git_repository *repo;
git_config *config, *global;
- cl_git_pass(git_buf_printf(&path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
+ cl_git_pass(git_buf_printf(
+ &path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
@@ -69,20 +74,19 @@ void test_repo_config__open_missing_global_with_separators(void)
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
+ cl_git_pass(git_config_open_level(
+ &global, config, GIT_CONFIG_LEVEL_GLOBAL));
cl_git_pass(git_config_set_string(global, "test.set", "42"));
git_config_free(global);
git_config_free(config);
git_repository_free(repo);
-
- git_sysdir_global_shutdown();
}
#include "repository.h"
-void test_repo_config__read_no_configs(void)
+void test_repo_config__read_with_no_configs_at_all(void)
{
git_repository *repo;
int val;
@@ -106,9 +110,9 @@ void test_repo_config__read_no_configs(void)
cl_assert_equal_i(GIT_ABBREV_DEFAULT, val);
git_repository_free(repo);
- git_sysdir_global_shutdown();
+ /* with no local config, just system */
- /* with just system */
+ cl_sandbox_set_search_path_defaults();
cl_must_pass(p_mkdir("alternate/1", 0777));
cl_git_pass(git_buf_joinpath(&path, path.ptr, "1"));
@@ -123,7 +127,7 @@ void test_repo_config__read_no_configs(void)
cl_assert_equal_i(10, val);
git_repository_free(repo);
- /* with xdg + system */
+ /* with just xdg + system */
cl_must_pass(p_mkdir("alternate/2", 0777));
path.ptr[path.size - 1] = '2';
@@ -204,6 +208,4 @@ void test_repo_config__read_no_configs(void)
cl_assert(!git_path_exists("empty_standard_repo/.git/config"));
cl_assert(!git_path_exists("alternate/3/.gitconfig"));
-
- git_sysdir_global_shutdown();
}
diff --git a/tests/repo/open.c b/tests/repo/open.c
index 190adff1c..637c785d5 100644
--- a/tests/repo/open.c
+++ b/tests/repo/open.c
@@ -298,7 +298,8 @@ void test_repo_open__no_config(void)
git_config *config;
cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
/* remove local config */
cl_git_pass(git_futils_rmdir_r(
@@ -325,7 +326,7 @@ void test_repo_open__no_config(void)
git_repository_free(repo);
cl_fixture_cleanup("empty_standard_repo");
- git_sysdir_global_shutdown();
+ cl_sandbox_set_search_path_defaults();
}
void test_repo_open__force_bare(void)
diff --git a/tests/repo/pathspec.c b/tests/repo/pathspec.c
index 334066b67..5b86662bc 100644
--- a/tests/repo/pathspec.c
+++ b/tests/repo/pathspec.c
@@ -167,7 +167,7 @@ void test_repo_pathspec__workdir4(void)
cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m));
- cl_assert_equal_s("θΏ™", git_pathspec_match_list_entry(m, 12));
+ cl_assert_equal_s("\xE8\xBF\x99", git_pathspec_match_list_entry(m, 12));
git_pathspec_match_list_free(m);
git_pathspec_free(ps);
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index caa1f1927..a4e766fdf 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -681,3 +681,110 @@ void test_status_ignore__issue_1766_negated_ignores(void)
}
}
+static void add_one_to_index(const char *file)
+{
+ git_index *index;
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, file));
+ git_index_free(index);
+}
+
+/* Some further broken scenarios that have been reported */
+void test_status_ignore__more_breakage(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked",
+ "empty_standard_repo/d1/pfx-d2/d3/d4/d5/untracked",
+ "empty_standard_repo/d1/pfx-d2/d3/d4/untracked",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "/d1/pfx-*\n"
+ "!/d1/pfx-d2/\n"
+ "/d1/pfx-d2/*\n"
+ "!/d1/pfx-d2/d3/\n"
+ "/d1/pfx-d2/d3/*\n"
+ "!/d1/pfx-d2/d3/d4/\n");
+ add_one_to_index("d1/pfx-d2/d3/d4/d5/tracked");
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *files[] = {
+ ".gitignore",
+ "d1/pfx-d2/d3/d4/d5/tracked",
+ "d1/pfx-d2/d3/d4/d5/untracked",
+ "d1/pfx-d2/d3/d4/untracked",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = files;
+ counts.expected_statuses = statuses;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+
+ refute_is_ignored("d1/pfx-d2/d3/d4/d5/tracked");
+ refute_is_ignored("d1/pfx-d2/d3/d4/d5/untracked");
+ refute_is_ignored("d1/pfx-d2/d3/d4/untracked");
+}
+
+void test_status_ignore__negative_ignores_inside_ignores(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/top/mid/btm/tracked",
+ "empty_standard_repo/top/mid/btm/untracked",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "top\n!top/mid/btm\n");
+ add_one_to_index("top/mid/btm/tracked");
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *files[] = {
+ ".gitignore", "top/mid/btm/tracked", "top/mid/btm/untracked",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_INDEX_NEW, GIT_STATUS_WT_NEW,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 3;
+ counts.expected_paths = files;
+ counts.expected_statuses = statuses;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+
+ refute_is_ignored("top/mid/btm/tracked");
+ refute_is_ignored("top/mid/btm/untracked");
+}