summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <vicent@github.com>2014-05-02 09:50:15 -0700
committerVicent Marti <vicent@github.com>2014-05-02 09:50:15 -0700
commit272b462db7cfb50c1ab69e1edda214b21f242ae5 (patch)
tree13c7a7ef6e7ef1e6d90949e60a3d4677ef9096e4
parent9862ef8ef8ffd95a74be8082acab9fea0de85edb (diff)
parent99dfa470398b9c4e06e5a5ee61868d3b9e21b26e (diff)
downloadlibgit2-272b462db7cfb50c1ab69e1edda214b21f242ae5.tar.gz
Merge pull request #2308 from libgit2/rb/diff-update-index-stat-cache
Reduce excessive OID calculation for diff and stat
-rw-r--r--include/git2/blame.h19
-rw-r--r--include/git2/checkout.h11
-rw-r--r--include/git2/cherrypick.h11
-rw-r--r--include/git2/clone.h19
-rw-r--r--include/git2/diff.h80
-rw-r--r--include/git2/index.h2
-rw-r--r--include/git2/merge.h6
-rw-r--r--include/git2/push.h4
-rw-r--r--include/git2/remote.h9
-rw-r--r--include/git2/repository.h9
-rw-r--r--include/git2/revert.h11
-rw-r--r--include/git2/status.h15
-rw-r--r--include/git2/sys/config.h9
-rw-r--r--include/git2/sys/diff.h28
-rw-r--r--include/git2/sys/odb_backend.h9
-rw-r--r--include/git2/sys/refdb_backend.h9
-rw-r--r--include/git2/transport.h9
-rw-r--r--src/blame.c13
-rw-r--r--src/checkout.c17
-rw-r--r--src/cherrypick.c14
-rw-r--r--src/clone.c13
-rw-r--r--src/common.h5
-rw-r--r--src/config.c31
-rw-r--r--src/diff.c210
-rw-r--r--src/diff.h6
-rw-r--r--src/diff_tform.c8
-rw-r--r--src/index.c6
-rw-r--r--src/iterator.c8
-rw-r--r--src/iterator.h1
-rw-r--r--src/merge.c40
-rw-r--r--src/odb.c13
-rw-r--r--src/push.c13
-rw-r--r--src/refdb.c13
-rw-r--r--src/remote.c13
-rw-r--r--src/repository.c15
-rw-r--r--src/revert.c13
-rw-r--r--src/status.c70
-rw-r--r--src/sysdir.c16
-rw-r--r--src/transport.c13
-rw-r--r--tests/attr/ignore.c5
-rw-r--r--tests/attr/repo.c5
-rw-r--r--tests/clar_libgit2.c33
-rw-r--r--tests/clar_libgit2.h9
-rw-r--r--tests/config/global.c35
-rw-r--r--tests/diff/blob.c2
-rw-r--r--tests/diff/tree.c2
-rw-r--r--tests/diff/workdir.c98
-rw-r--r--tests/refs/branches/create.c9
-rw-r--r--tests/refs/branches/delete.c7
-rw-r--r--tests/refs/branches/ishead.c24
-rw-r--r--tests/status/ignore.c5
-rw-r--r--tests/status/worktree.c78
-rw-r--r--tests/structinit/structinit.c4
53 files changed, 655 insertions, 472 deletions
diff --git a/include/git2/blame.h b/include/git2/blame.h
index b7fa9aeda..7f0de1731 100644
--- a/include/git2/blame.h
+++ b/include/git2/blame.h
@@ -83,17 +83,16 @@ typedef struct git_blame_options {
#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
/**
-* Initializes a `git_blame_options` with default values. Equivalent to
-* creating an instance with GIT_BLAME_OPTIONS_INIT.
-*
-* @param opts the `git_blame_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_BLAME_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_blame_options` with default values. Equivalent to
+ * creating an instance with GIT_BLAME_OPTIONS_INIT.
+ *
+ * @param opts The `git_blame_options` struct to initialize
+ * @param version Version of struct; pass `GIT_BLAME_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_blame_init_options(
- git_blame_options* opts,
- int version);
+ git_blame_options *opts,
+ unsigned int version);
/**
* Structure that represents a blame hunk.
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index 69addb7d9..494f67456 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -270,14 +270,13 @@ typedef struct git_checkout_options {
* Initializes a `git_checkout_options` with default values. Equivalent to
* creating an instance with GIT_CHECKOUT_OPTIONS_INIT.
*
-* @param opts the `git_checkout_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_CHECKOUT_OPTIONS_VERSION` here.
+* @param opts the `git_checkout_options` struct to initialize.
+* @param version Version of struct; pass `GIT_CHECKOUT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_checkout_init_opts(
- git_checkout_options* opts,
- int version);
+GIT_EXTERN(int) git_checkout_init_options(
+ git_checkout_options *opts,
+ unsigned int version);
/**
* Updates files in the index and the working tree to match the content of
diff --git a/include/git2/cherrypick.h b/include/git2/cherrypick.h
index 7c48e6659..e998d325f 100644
--- a/include/git2/cherrypick.h
+++ b/include/git2/cherrypick.h
@@ -37,14 +37,13 @@ typedef struct {
* Initializes a `git_cherry_pick_options` with default values. Equivalent to
* creating an instance with GIT_CHERRY_PICK_OPTIONS_INIT.
*
- * @param opts the `git_cherry_pick_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_CHERRY_PICK_OPTIONS_VERSION` here.
+ * @param opts the `git_cherry_pick_options` struct to initialize
+ * @param version Version of struct; pass `GIT_CHERRY_PICK_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_cherry_pick_init_opts(
- git_cherry_pick_options* opts,
- int version);
+GIT_EXTERN(int) git_cherry_pick_init_options(
+ git_cherry_pick_options *opts,
+ unsigned int version);
/**
* Cherry-picks the given commit against the given "our" commit, producing an
diff --git a/include/git2/clone.h b/include/git2/clone.h
index 20be1a105..985c04bf6 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -66,17 +66,16 @@ typedef struct git_clone_options {
#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
/**
-* Initializes a `git_clone_options` with default values. Equivalent to
-* creating an instance with GIT_CLONE_OPTIONS_INIT.
-*
-* @param opts the `git_clone_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_CLONE_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_clone_options` with default values. Equivalent to
+ * creating an instance with GIT_CLONE_OPTIONS_INIT.
+ *
+ * @param opts The `git_clone_options` struct to initialize
+ * @param version Version of struct; pass `GIT_CLONE_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_clone_init_options(
- git_clone_options* opts,
- int version);
+ git_clone_options *opts,
+ unsigned int version);
/**
* Clone a remote repository.
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 273f471b6..b40cc6135 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -145,6 +145,13 @@ typedef enum {
*/
GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
+ /** When diff finds a file in the working directory with stat
+ * information different from the index, but the OID ends up being the
+ * same, write the correct stat information into the index. Note:
+ * without this flag, diff will always leave the index untouched.
+ */
+ GIT_DIFF_UPDATE_INDEX = (1u << 15),
+
/*
* Options controlling how output will be generated
*/
@@ -381,17 +388,16 @@ typedef struct {
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3}
/**
-* Initializes a `git_diff_options` with default values. Equivalent to
-* creating an instance with GIT_DIFF_OPTIONS_INIT.
-*
-* @param opts the `git_diff_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_DIFF_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_diff_options` with default values. Equivalent to
+ * creating an instance with GIT_DIFF_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_options` struct to initialize
+ * @param version Version of struct; pass `GIT_DIFF_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_diff_init_options(
- git_diff_options* opts,
- int version);
+ git_diff_options *opts,
+ unsigned int version);
/**
* When iterating over a diff, callback that will be made per file.
@@ -622,17 +628,16 @@ typedef struct {
#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
/**
-* Initializes a `git_diff_find_options` with default values. Equivalent to
-* creating an instance with GIT_DIFF_FIND_OPTIONS_INIT.
-*
-* @param opts the `git_diff_find_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_DIFF_FIND_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_diff_find_options` with default values. Equivalent to
+ * creating an instance with GIT_DIFF_FIND_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_find_options` struct to initialize
+ * @param version Version of struct; pass `GIT_DIFF_FIND_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_diff_find_init_options(
- git_diff_find_options* opts,
- int version);
+ git_diff_find_options *opts,
+ unsigned int version);
/** @name Diff Generator Functions
*
@@ -804,23 +809,6 @@ GIT_EXTERN(int) git_diff_find_similar(
git_diff *diff,
const git_diff_find_options *options);
-/**
- * Initialize diff options structure
- *
- * In most cases, you can probably just use `GIT_DIFF_OPTIONS_INIT` to
- * initialize the diff options structure, but in some cases that is not
- * going to work. You can call this function instead. Note that you
- * must pass both a pointer to the structure to be initialized and the
- * `GIT_DIFF_OPTIONS_VERSION` value from the header you compiled with.
- *
- * @param options Pointer to git_diff_options memory to be initialized
- * @param version Should be `GIT_DIFF_OPTIONS_VERSION`
- * @return 0 on success, negative on failure (such as unsupported version)
- */
-GIT_EXTERN(int) git_diff_options_init(
- git_diff_options *options,
- unsigned int version);
-
/**@}*/
@@ -1233,17 +1221,17 @@ GIT_EXTERN(int) git_diff_commit_as_email(
const git_diff_options *diff_opts);
/**
-* Initializes a `git_diff_format_email_options` with default values. Equivalent to
-* creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
-*
-* @param opts the `git_diff_format_email_options` instance to initialize.
-* @param version the version of the struct; you should pass
-* `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION` here.
-* @return Zero on success; -1 on failure.
-*/
+ * Initializes a `git_diff_format_email_options` with default values.
+ *
+ * Equivalent to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_format_email_options` struct to initialize
+ * @param version Version of struct; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
GIT_EXTERN(int) git_diff_format_email_init_options(
git_diff_format_email_options *opts,
- int version);
+ unsigned int version);
GIT_END_DECL
diff --git a/include/git2/index.h b/include/git2/index.h
index 05e58a632..cdb87282c 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -61,7 +61,7 @@ typedef struct git_index_entry {
unsigned short flags;
unsigned short flags_extended;
- char *path;
+ const char *path;
} git_index_entry;
/**
diff --git a/include/git2/merge.h b/include/git2/merge.h
index 6d97e81e6..ef2dc3804 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -57,7 +57,7 @@ typedef struct {
*/
GIT_EXTERN(int) git_merge_file_init_input(
git_merge_file_input *opts,
- int version);
+ unsigned int version);
/**
* Flags for `git_merge_tree` options. A combination of these flags can be
@@ -164,7 +164,7 @@ typedef struct {
*/
GIT_EXTERN(int) git_merge_file_init_options(
git_merge_file_options *opts,
- int version);
+ unsigned int version);
typedef struct {
/**
@@ -232,7 +232,7 @@ typedef struct {
*/
GIT_EXTERN(int) git_merge_init_options(
git_merge_options *opts,
- int version);
+ unsigned int version);
/**
* The results of `git_merge_analysis` indicate the merge opportunities.
diff --git a/include/git2/push.h b/include/git2/push.h
index 7a8bec12c..cbf115661 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -49,8 +49,8 @@ typedef struct {
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_push_init_options(
- git_push_options* opts,
- int version);
+ git_push_options *opts,
+ unsigned int version);
/** Push network progress notification function */
typedef int (*git_push_transfer_progress)(
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 11e1e26d0..3633501e2 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -501,14 +501,13 @@ struct git_remote_callbacks {
* Initializes a `git_remote_callbacks` with default values. Equivalent to
* creating an instance with GIT_REMOTE_CALLBACKS_INIT.
*
- * @param opts the `git_remote_callbacks` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REMOTE_CALLBACKS_VERSION` here.
+ * @param opts the `git_remote_callbacks` struct to initialize
+ * @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_remote_init_callbacks(
- git_remote_callbacks* opts,
- int version);
+ git_remote_callbacks *opts,
+ unsigned int version);
/**
* Set the callbacks for a remote
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 4433e71a2..e3f687a29 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -271,14 +271,13 @@ typedef struct {
* Initializes a `git_repository_init_options` with default values. Equivalent
* to creating an instance with GIT_REPOSITORY_INIT_OPTIONS_INIT.
*
- * @param opts the `git_repository_init_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REPOSITORY_INIT_OPTIONS_VERSION` here.
+ * @param opts the `git_repository_init_options` struct to initialize
+ * @param version Version of struct; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_repository_init_init_options(
- git_repository_init_options* opts,
- int version);
+ git_repository_init_options *opts,
+ unsigned int version);
/**
* Create a new Git repository in the given folder with extended controls.
diff --git a/include/git2/revert.h b/include/git2/revert.h
index 3a6beb6b8..da37fbe7b 100644
--- a/include/git2/revert.h
+++ b/include/git2/revert.h
@@ -37,14 +37,13 @@ typedef struct {
* Initializes a `git_revert_options` with default values. Equivalent to
* creating an instance with GIT_REVERT_OPTIONS_INIT.
*
- * @param opts the `git_revert_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REVERT_OPTIONS_VERSION` here.
+ * @param opts the `git_revert_options` struct to initialize
+ * @param version Version of struct; pass `GIT_REVERT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
-GIT_EXTERN(int) git_revert_init_opts(
- git_revert_options* opts,
- int version);
+GIT_EXTERN(int) git_revert_init_options(
+ git_revert_options *opts,
+ unsigned int version);
/**
* Reverts the given commit against the given "our" commit, producing an
diff --git a/include/git2/status.h b/include/git2/status.h
index 6af45c7dd..effe5e1ea 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -121,6 +121,11 @@ typedef enum {
* - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
* doing a "soft" index reload (i.e. reloading the index data if the
* file on disk has been modified outside libgit2).
+ * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
+ * in the index for files that are unchanged but have out of date stat
+ * information in the index. It will result in less work being done on
+ * subsequent calls to get status. This is mutually exclusive with the
+ * NO_REFRESH option.
*
* Calling `git_status_foreach()` is like calling the extended version
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
@@ -141,6 +146,7 @@ typedef enum {
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
+ GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
} git_status_opt_t;
#define GIT_STATUS_OPT_DEFAULTS \
@@ -178,14 +184,13 @@ typedef struct {
* Initializes a `git_status_options` with default values. Equivalent to
* creating an instance with GIT_STATUS_OPTIONS_INIT.
*
- * @param opts the `git_status_options` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_STATUS_OPTIONS_VERSION` here.
+ * @param opts The `git_status_options` instance to initialize.
+ * @param version Version of struct; pass `GIT_STATUS_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_status_init_options(
- git_status_options* opts,
- int version);
+ git_status_options *opts,
+ unsigned int version);
/**
* A status entry, providing the differences between the file as it exists
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 3df2ba327..46bb65293 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -73,14 +73,13 @@ struct git_config_backend {
* Initializes a `git_config_backend` with default values. Equivalent to
* creating an instance with GIT_CONFIG_BACKEND_INIT.
*
- * @param opts the `git_config_backend` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_CONFIG_BACKEND_VERSION` here.
+ * @param opts the `git_config_backend` struct to initialize.
+ * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_config_init_backend(
- git_config_backend* backend,
- int version);
+ git_config_backend *backend,
+ unsigned int version);
/**
* Add a generic config file instance to an existing config
diff --git a/include/git2/sys/diff.h b/include/git2/sys/diff.h
index bc6cdf393..48d72f4f9 100644
--- a/include/git2/sys/diff.h
+++ b/include/git2/sys/diff.h
@@ -10,6 +10,8 @@
#include "git2/common.h"
#include "git2/types.h"
#include "git2/oid.h"
+#include "git2/diff.h"
+#include "git2/status.h"
/**
* @file git2/sys/diff.h
@@ -58,6 +60,32 @@ GIT_EXTERN(int) git_diff_print_callback__to_file_handle(
const git_diff_line *line,
void *payload); /*< payload must be a `FILE *` */
+
+typedef struct {
+ unsigned int version;
+ size_t stat_calls;
+ size_t oid_calculations;
+} git_diff_perfdata;
+
+#define GIT_DIFF_PERFDATA_VERSION 1
+#define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0}
+
+/**
+ * Get performance data for a diff object.
+ *
+ * @param out Structure to be filled with diff performance data
+ * @param diff Diff to read performance data from
+ * @return 0 for success, <0 for error
+ */
+GIT_EXTERN(int) git_diff_get_perfdata(
+ git_diff_perfdata *out, const git_diff *diff);
+
+/**
+ * Get performance data for diffs from a git_status_list
+ */
+GIT_EXTERN(int) git_status_list_get_perfdata(
+ git_diff_perfdata *out, const git_status_list *status);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h
index 77fe0dd31..1fc3c3159 100644
--- a/include/git2/sys/odb_backend.h
+++ b/include/git2/sys/odb_backend.h
@@ -93,14 +93,13 @@ struct git_odb_backend {
* Initializes a `git_odb_backend` with default values. Equivalent to
* creating an instance with GIT_ODB_BACKEND_INIT.
*
- * @param opts the `git_odb_backend` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_ODB_BACKEND_VERSION` here.
+ * @param opts the `git_odb_backend` struct to initialize.
+ * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_odb_init_backend(
- git_odb_backend* backend,
- int version);
+ git_odb_backend *backend,
+ unsigned int version);
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index dce142c77..3b216a287 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -162,14 +162,13 @@ struct git_refdb_backend {
* Initializes a `git_refdb_backend` with default values. Equivalent to
* creating an instance with GIT_REFDB_BACKEND_INIT.
*
- * @param opts the `git_refdb_backend` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_REFDB_BACKEND_VERSION` here.
+ * @param opts the `git_refdb_backend` struct to initialize
+ * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_refdb_init_backend(
- git_refdb_backend* backend,
- int version);
+ git_refdb_backend *backend,
+ unsigned int version);
/**
* Constructors for default filesystem-based refdb backend
diff --git a/include/git2/transport.h b/include/git2/transport.h
index a33146ca8..af7812b5d 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -314,14 +314,13 @@ struct git_transport {
* Initializes a `git_transport` with default values. Equivalent to
* creating an instance with GIT_TRANSPORT_INIT.
*
- * @param opts the `git_transport` instance to initialize.
- * @param version the version of the struct; you should pass
- * `GIT_TRANSPORT_VERSION` here.
+ * @param opts the `git_transport` struct to initialize
+ * @param version Version of struct; pass `GIT_TRANSPORT_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_transport_init(
- git_transport* opts,
- int version);
+ git_transport *opts,
+ unsigned int version);
/**
* Function to use to create a transport from a URL. The transport database
diff --git a/src/blame.c b/src/blame.c
index e45c0ee1c..eb977c287 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -480,14 +480,9 @@ int git_blame_buffer(
return 0;
}
-int git_blame_init_options(git_blame_options* opts, int version)
+int git_blame_init_options(git_blame_options *opts, unsigned int version)
{
- if (version != GIT_BLAME_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_blame_options", version);
- return -1;
- } else {
- git_blame_options o = GIT_BLAME_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/checkout.c b/src/checkout.c
index bc976b854..b869efe2b 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -184,9 +184,7 @@ static bool checkout_is_workdir_modified(
if (baseitem->size && wditem->file_size != baseitem->size)
return true;
- if (git_diff__oid_for_file(
- data->repo, wditem->path, wditem->mode,
- wditem->file_size, &oid) < 0)
+ if (git_diff__oid_for_entry(&oid, data->diff, wditem, NULL) < 0)
return false;
return (git_oid__cmp(&baseitem->id, &oid) != 0);
@@ -2242,14 +2240,9 @@ int git_checkout_head(
return git_checkout_tree(repo, NULL, opts);
}
-int git_checkout_init_opts(git_checkout_options* opts, int version)
+int git_checkout_init_options(git_checkout_options *opts, unsigned int version)
{
- if (version != GIT_CHECKOUT_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_checkout_options", version);
- return -1;
- } else {
- git_checkout_options o = GIT_CHECKOUT_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_checkout_options, GIT_CHECKOUT_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/cherrypick.c b/src/cherrypick.c
index 6a5ca834c..e02348a03 100644
--- a/src/cherrypick.c
+++ b/src/cherrypick.c
@@ -217,14 +217,10 @@ done:
return error;
}
-int git_cherry_pick_init_opts(git_cherry_pick_options* opts, int version)
+int git_cherry_pick_init_options(
+ git_cherry_pick_options *opts, unsigned int version)
{
- if (version != GIT_CHERRY_PICK_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_cherry_pick_options", version);
- return -1;
- } else {
- git_cherry_pick_options o = GIT_CHERRY_PICK_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_cherry_pick_options, GIT_CHERRY_PICK_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/clone.c b/src/clone.c
index 62f103561..c6be00f0e 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -445,14 +445,9 @@ int git_clone(
return error;
}
-int git_clone_init_options(git_clone_options* opts, int version)
+int git_clone_init_options(git_clone_options *opts, unsigned int version)
{
- if (version != GIT_CLONE_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_clone_options", version);
- return -1;
- } else {
- git_clone_options o = GIT_CLONE_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/common.h b/src/common.h
index dd97a3099..807e5fa39 100644
--- a/src/common.h
+++ b/src/common.h
@@ -170,6 +170,11 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
}
#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
+#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
+ TYPE _tmpl = TPL; \
+ GITERR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
+ memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
+
/* NOTE: other giterr functions are in the public errors.h header file */
#include "util.h"
diff --git a/src/config.c b/src/config.c
index 16854c0c8..f9d697197 100644
--- a/src/config.c
+++ b/src/config.c
@@ -984,24 +984,22 @@ int git_config__global_location(git_buf *buf)
{
const git_buf *paths;
const char *sep, *start;
- size_t len;
if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0)
return -1;
/* no paths, so give up */
- if (git_buf_len(paths) == 0)
+ if (!paths || !git_buf_len(paths))
return -1;
- start = git_buf_cstr(paths);
- sep = strchr(start, GIT_PATH_LIST_SEPARATOR);
-
- if (sep)
- len = sep - start;
- else
- len = paths->size;
+ /* find unescaped separator or end of string */
+ for (sep = start = git_buf_cstr(paths); *sep; ++sep) {
+ if (*sep == GIT_PATH_LIST_SEPARATOR &&
+ (sep <= start || sep[-1] != '\\'))
+ break;
+ }
- if (git_buf_set(buf, start, len) < 0)
+ if (git_buf_set(buf, start, (size_t)(sep - start)) < 0)
return -1;
return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
@@ -1276,14 +1274,9 @@ cleanup:
return error;
}
-int git_config_init_backend(git_config_backend* backend, int version)
+int git_config_init_backend(git_config_backend *backend, unsigned int version)
{
- if (version != GIT_CONFIG_BACKEND_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_config_backend", version);
- return -1;
- } else {
- git_config_backend b = GIT_CONFIG_BACKEND_INIT;
- memcpy(backend, &b, sizeof(b));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT);
+ return 0;
}
diff --git a/src/diff.c b/src/diff.c
index 4b6fbe25a..781f23ec6 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -442,6 +442,14 @@ static int diff_list_apply_options(
diff->new_src = tmp_src;
}
+ /* Unset UPDATE_INDEX unless diffing workdir and index */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) &&
+ (!(diff->old_src == GIT_ITERATOR_TYPE_WORKDIR ||
+ diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ||
+ !(diff->old_src == GIT_ITERATOR_TYPE_INDEX ||
+ diff->new_src == GIT_ITERATOR_TYPE_INDEX)))
+ diff->opts.flags &= ~GIT_DIFF_UPDATE_INDEX;
+
/* if ignore_submodules not explicitly set, check diff config */
if (diff->opts.ignore_submodules <= 0) {
const git_config_entry *entry;
@@ -510,76 +518,105 @@ void git_diff_addref(git_diff *diff)
}
int git_diff__oid_for_file(
- git_repository *repo,
+ git_oid *out,
+ git_diff *diff,
const char *path,
uint16_t mode,
- git_off_t size,
- git_oid *oid)
+ git_off_t size)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.mode = mode;
+ entry.file_size = size;
+ entry.path = (char *)path;
+
+ return git_diff__oid_for_entry(out, diff, &entry, NULL);
+}
+
+int git_diff__oid_for_entry(
+ git_oid *out,
+ git_diff *diff,
+ const git_index_entry *src,
+ const git_oid *update_match)
{
- int result = 0;
+ int error = 0;
git_buf full_path = GIT_BUF_INIT;
+ git_index_entry entry = *src;
+ git_filter_list *fl = NULL;
+
+ memset(out, 0, sizeof(*out));
if (git_buf_joinpath(
- &full_path, git_repository_workdir(repo), path) < 0)
+ &full_path, git_repository_workdir(diff->repo), entry.path) < 0)
return -1;
- if (!mode) {
+ if (!entry.mode) {
struct stat st;
- if (p_stat(path, &st) < 0) {
- giterr_set(GITERR_OS, "Could not stat '%s'", path);
- result = -1;
- goto cleanup;
+ diff->perf.stat_calls++;
+
+ if (p_stat(full_path.ptr, &st) < 0) {
+ error = git_path_set_error(errno, entry.path, "stat");
+ git_buf_free(&full_path);
+ return error;
}
- mode = st.st_mode;
- size = st.st_size;
+ git_index_entry__init_from_stat(
+ &entry, &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0);
}
/* calculate OID for file if possible */
- if (S_ISGITLINK(mode)) {
+ if (S_ISGITLINK(entry.mode)) {
git_submodule *sm;
- memset(oid, 0, sizeof(*oid));
-
- if (!git_submodule_lookup(&sm, repo, path)) {
+ if (!git_submodule_lookup(&sm, diff->repo, entry.path)) {
const git_oid *sm_oid = git_submodule_wd_id(sm);
if (sm_oid)
- git_oid_cpy(oid, sm_oid);
+ git_oid_cpy(out, sm_oid);
git_submodule_free(sm);
} else {
/* if submodule lookup failed probably just in an intermediate
* state where some init hasn't happened, so ignore the error
*/
giterr_clear();
- memset(oid, 0, sizeof(*oid));
}
- } else if (S_ISLNK(mode)) {
- result = git_odb__hashlink(oid, full_path.ptr);
- } else if (!git__is_sizet(size)) {
- giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
- result = -1;
- } else {
- git_filter_list *fl = NULL;
-
- result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB);
- if (!result) {
- int fd = git_futils_open_ro(full_path.ptr);
- if (fd < 0)
- result = fd;
- else {
- result = git_odb__hashfd_filtered(
- oid, fd, (size_t)size, GIT_OBJ_BLOB, fl);
- p_close(fd);
- }
-
- git_filter_list_free(fl);
+ } else if (S_ISLNK(entry.mode)) {
+ error = git_odb__hashlink(out, full_path.ptr);
+ diff->perf.oid_calculations++;
+ } else if (!git__is_sizet(entry.file_size)) {
+ giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'",
+ entry.path);
+ error = -1;
+ } else if (!(error = git_filter_list_load(
+ &fl, diff->repo, NULL, entry.path, GIT_FILTER_TO_ODB)))
+ {
+ int fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0)
+ error = fd;
+ else {
+ error = git_odb__hashfd_filtered(
+ out, fd, (size_t)entry.file_size, GIT_OBJ_BLOB, fl);
+ p_close(fd);
+ diff->perf.oid_calculations++;
}
+
+ git_filter_list_free(fl);
}
-cleanup:
+ /* update index for entry if requested */
+ if (!error && update_match && git_oid_equal(out, update_match)) {
+ git_index *idx;
+
+ if (!(error = git_repository_index(&idx, diff->repo))) {
+ memcpy(&entry.id, out, sizeof(entry.id));
+ error = git_index_add(idx, &entry);
+ git_index_free(idx);
+ }
+ }
+
git_buf_free(&full_path);
- return result;
+ return error;
}
static bool diff_time_eq(
@@ -660,6 +697,7 @@ static int maybe_modified(
unsigned int omode = oitem->mode;
unsigned int nmode = nitem->mode;
bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
+ bool modified_uncertain = false;
const char *matched_pathspec;
int error = 0;
@@ -727,15 +765,21 @@ static int maybe_modified(
/* if the stat data looks different, then mark modified - this just
* means that the OID will be recalculated below to confirm change
*/
- else if (omode != nmode ||
- oitem->file_size != nitem->file_size ||
- !diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
+ else if (omode != nmode || oitem->file_size != nitem->file_size) {
+ status = GIT_DELTA_MODIFIED;
+ modified_uncertain =
+ (oitem->file_size <= 0 && nitem->file_size > 0);
+ }
+ else if (!diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) ||
(use_ctime &&
!diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) ||
oitem->ino != nitem->ino ||
oitem->uid != nitem->uid ||
oitem->gid != nitem->gid)
+ {
status = GIT_DELTA_MODIFIED;
+ modified_uncertain = true;
+ }
}
/* if mode is GITLINK and submodules are ignored, then skip */
@@ -746,10 +790,14 @@ static int maybe_modified(
/* if we got here and decided that the files are modified, but we
* haven't calculated the OID of the new item, then calculate it now
*/
- if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->id)) {
+ if (modified_uncertain && git_oid_iszero(&nitem->id)) {
if (git_oid_iszero(&noid)) {
- if ((error = git_diff__oid_for_file(diff->repo,
- nitem->path, nitem->mode, nitem->file_size, &noid)) < 0)
+ const git_oid *update_check =
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) ?
+ &oitem->id : NULL;
+
+ if ((error = git_diff__oid_for_entry(
+ &noid, diff, nitem, update_check)) < 0)
return error;
}
@@ -1066,6 +1114,8 @@ int git_diff__from_iterators(
error = 0;
}
+ diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls;
+
cleanup:
if (!error)
*diff_ptr = diff;
@@ -1174,6 +1224,9 @@ int git_diff_index_to_workdir(
&b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
);
+ if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX))
+ error = git_index_write(index);
+
return error;
}
@@ -1226,20 +1279,6 @@ int git_diff_tree_to_workdir_with_index(
return error;
}
-int git_diff_options_init(git_diff_options *options, unsigned int version)
-{
- git_diff_options template = GIT_DIFF_OPTIONS_INIT;
-
- if (version != template.version) {
- giterr_set(GITERR_INVALID,
- "Invalid version %d for git_diff_options", (int)version);
- return -1;
- }
-
- memcpy(options, &template, sizeof(*options));
- return 0;
-}
-
size_t git_diff_num_deltas(const git_diff *diff)
{
assert(diff);
@@ -1271,6 +1310,15 @@ int git_diff_is_sorted_icase(const git_diff *diff)
return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
}
+int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
+{
+ assert(out);
+ GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
+ out->stat_calls = diff->perf.stat_calls;
+ out->oid_calculations = diff->perf.oid_calculations;
+ return 0;
+}
+
int git_diff__paired_foreach(
git_diff *head2idx,
git_diff *idx2wd,
@@ -1573,38 +1621,26 @@ int git_diff_commit_as_email(
return error;
}
-int git_diff_init_options(git_diff_options* opts, int version)
+int git_diff_init_options(git_diff_options *opts, unsigned int version)
{
- if (version != GIT_DIFF_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_options", version);
- return -1;
- } else {
- git_diff_options o = GIT_DIFF_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
+ return 0;
}
-int git_diff_find_init_options(git_diff_find_options* opts, int version)
+int git_diff_find_init_options(
+ git_diff_find_options *opts, unsigned int version)
{
- if (version != GIT_DIFF_FIND_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_find_options", version);
- return -1;
- } else {
- git_diff_find_options o = GIT_DIFF_FIND_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
+ return 0;
}
-int git_diff_format_email_init_options(git_diff_format_email_options* opts, int version)
+int git_diff_format_email_init_options(
+ git_diff_format_email_options *opts, unsigned int version)
{
- if (version != GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_diff_format_email_options", version);
- return -1;
- } else {
- git_diff_format_email_options o = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_format_email_options,
+ GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/diff.h b/src/diff.h
index aae8fbff1..3305238d0 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -8,6 +8,7 @@
#define INCLUDE_diff_h__
#include "git2/diff.h"
+#include "git2/sys/diff.h"
#include "git2/oid.h"
#include <stdio.h>
@@ -62,6 +63,7 @@ struct git_diff {
git_iterator_type_t old_src;
git_iterator_type_t new_src;
uint32_t diffcaps;
+ git_diff_perfdata perf;
int (*strcomp)(const char *, const char *);
int (*strncomp)(const char *, const char *, size_t);
@@ -90,7 +92,9 @@ extern int git_diff_delta__format_file_header(
int oid_strlen);
extern int git_diff__oid_for_file(
- git_repository *, const char *, uint16_t, git_off_t, git_oid *);
+ git_oid *out, git_diff *, const char *, uint16_t, git_off_t);
+extern int git_diff__oid_for_entry(
+ git_oid *out, git_diff *, const git_index_entry *, const git_oid *update);
extern int git_diff__from_iterators(
git_diff **diff_ptr,
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 97fbc2883..a2dab0ae2 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -574,14 +574,14 @@ static int similarity_measure(
if (exact_match) {
if (git_oid_iszero(&a_file->id) &&
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(diff->repo, a_file->path,
- a_file->mode, a_file->size, &a_file->id))
+ !git_diff__oid_for_file(&a_file->id,
+ diff, a_file->path, a_file->mode, a_file->size))
a_file->flags |= GIT_DIFF_FLAG_VALID_ID;
if (git_oid_iszero(&b_file->id) &&
diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
- !git_diff__oid_for_file(diff->repo, b_file->path,
- b_file->mode, b_file->size, &b_file->id))
+ !git_diff__oid_for_file(&b_file->id,
+ diff, b_file->path, b_file->mode, b_file->size))
b_file->flags |= GIT_DIFF_FLAG_VALID_ID;
}
diff --git a/src/index.c b/src/index.c
index c044af402..8a7f29279 100644
--- a/src/index.c
+++ b/src/index.c
@@ -842,7 +842,7 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
{
- char *tgt_path = tgt->path;
+ const char *tgt_path = tgt->path;
memcpy(tgt, src, sizeof(*tgt));
tgt->path = tgt_path; /* reset to existing path data */
}
@@ -2282,9 +2282,7 @@ static int read_tree_cb(
entry->mode == old_entry->mode &&
git_oid_equal(&entry->id, &old_entry->id))
{
- char *oldpath = entry->path;
- memcpy(entry, old_entry, sizeof(*entry));
- entry->path = oldpath;
+ index_entry_cpy(entry, old_entry);
entry->flags_extended = 0;
}
diff --git a/src/iterator.c b/src/iterator.c
index ef27fa71f..4f8087c8d 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -1016,6 +1016,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
fs_iterator__free_frame(ff);
return GIT_ENOTFOUND;
}
+ fi->base.stat_calls += ff->entries.length;
fs_iterator__seek_frame_start(fi, ff);
@@ -1304,9 +1305,10 @@ static int workdir_iterator__enter_dir(fs_iterator *fi)
/* convert submodules to GITLINK and remove trailing slashes */
git_vector_foreach(&ff->entries, pos, entry) {
- if (S_ISDIR(entry->st.st_mode) &&
- git_submodule__is_submodule(fi->base.repo, entry->path))
- {
+ if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path))
+ continue;
+
+ if (git_submodule__is_submodule(fi->base.repo, entry->path)) {
entry->st.st_mode = GIT_FILEMODE_COMMIT;
entry->path_len--;
entry->path[entry->path_len] = '\0';
diff --git a/src/iterator.h b/src/iterator.h
index ba9c1e486..f67830212 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -52,6 +52,7 @@ struct git_iterator {
char *start;
char *end;
int (*prefixcomp)(const char *str, const char *prefix);
+ size_t stat_calls;
unsigned int flags;
};
diff --git a/src/merge.c b/src/merge.c
index 69c42bc0c..6a8e5874f 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -2803,38 +2803,24 @@ void git_merge_head_free(git_merge_head *head)
git__free(head);
}
-int git_merge_init_options(git_merge_options *opts, int version)
+int git_merge_init_options(git_merge_options *opts, unsigned int version)
{
- if (version != GIT_MERGE_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_options", version);
- return -1;
- } else {
- git_merge_options default_opts = GIT_MERGE_OPTIONS_INIT;
- memcpy(opts, &default_opts, sizeof(git_merge_options));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT);
+ return 0;
}
-int git_merge_file_init_input(git_merge_file_input *input, int version)
+int git_merge_file_init_input(git_merge_file_input *input, unsigned int version)
{
- if (version != GIT_MERGE_FILE_INPUT_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_input", version);
- return -1;
- } else {
- git_merge_file_input i = GIT_MERGE_FILE_INPUT_INIT;
- memcpy(input, &i, sizeof(i));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT);
+ return 0;
}
-int git_merge_file_init_options(git_merge_file_options *opts, int version)
+int git_merge_file_init_options(
+ git_merge_file_options *opts, unsigned int version)
{
- if (version != GIT_MERGE_FILE_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_options", version);
- return -1;
- } else {
- git_merge_file_options o = GIT_MERGE_FILE_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/odb.c b/src/odb.c
index 00740d2e2..20a3f6c6e 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -1123,14 +1123,9 @@ int git_odb__error_ambiguous(const char *message)
return GIT_EAMBIGUOUS;
}
-int git_odb_init_backend(git_odb_backend* backend, int version)
+int git_odb_init_backend(git_odb_backend *backend, unsigned int version)
{
- if (version != GIT_ODB_BACKEND_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_odb_backend", version);
- return -1;
- } else {
- git_odb_backend b = GIT_ODB_BACKEND_INIT;
- memcpy(backend, &b, sizeof(b));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT);
+ return 0;
}
diff --git a/src/push.c b/src/push.c
index 9943f215c..be5ec1c0e 100644
--- a/src/push.c
+++ b/src/push.c
@@ -716,14 +716,9 @@ void git_push_free(git_push *push)
git__free(push);
}
-int git_push_init_options(git_push_options* opts, int version)
+int git_push_init_options(git_push_options *opts, unsigned int version)
{
- if (version != GIT_PUSH_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_push_options", version);
- return -1;
- } else {
- git_push_options o = GIT_PUSH_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_push_options, GIT_PUSH_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/refdb.c b/src/refdb.c
index 3e7a592f8..69bf74734 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -236,14 +236,9 @@ int git_refdb_ensure_log(git_refdb *db, const char *refname)
return db->backend->ensure_log(db->backend, refname);
}
-int git_refdb_init_backend(git_refdb_backend* backend, int version)
+int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
{
- if (version != GIT_REFDB_BACKEND_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_refdb_backend", version);
- return -1;
- } else {
- git_refdb_backend b = GIT_REFDB_BACKEND_INIT;
- memcpy(backend, &b, sizeof(b));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
+ return 0;
}
diff --git a/src/remote.c b/src/remote.c
index be7198a98..8f302a51e 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -1736,14 +1736,9 @@ const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
return git_vector_get(&remote->refspecs, n);
}
-int git_remote_init_callbacks(git_remote_callbacks* opts, int version)
+int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
{
- if (version != GIT_REMOTE_CALLBACKS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_remote_callbacks", version);
- return -1;
- } else {
- git_remote_callbacks o = GIT_REMOTE_CALLBACKS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
+ return 0;
}
diff --git a/src/repository.c b/src/repository.c
index 8daa04d5d..ac7af7692 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -2026,14 +2026,11 @@ int git_repository_is_shallow(git_repository *repo)
return st.st_size == 0 ? 0 : 1;
}
-int git_repository_init_init_options(git_repository_init_options* opts, int version)
+int git_repository_init_init_options(
+ git_repository_init_options *opts, unsigned int version)
{
- if (version != GIT_REPOSITORY_INIT_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_repository_init_options", version);
- return -1;
- } else {
- git_repository_init_options o = GIT_REPOSITORY_INIT_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_repository_init_options,
+ GIT_REPOSITORY_INIT_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/revert.c b/src/revert.c
index 29e124f6c..9c587724b 100644
--- a/src/revert.c
+++ b/src/revert.c
@@ -220,14 +220,9 @@ done:
return error;
}
-int git_revert_init_opts(git_revert_options* opts, int version)
+int git_revert_init_options(git_revert_options *opts, unsigned int version)
{
- if (version != GIT_REVERT_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_revert_options", version);
- return -1;
- } else {
- git_revert_options o = GIT_REVERT_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_revert_options, GIT_REVERT_OPTIONS_INIT);
+ return 0;
}
diff --git a/src/status.c b/src/status.c
index c4b990a84..8d7612f72 100644
--- a/src/status.c
+++ b/src/status.c
@@ -81,15 +81,15 @@ static unsigned int workdir_delta2status(
if (git_oid_iszero(&idx2wd->old_file.id) &&
diff->old_src == GIT_ITERATOR_TYPE_WORKDIR &&
!git_diff__oid_for_file(
- diff->repo, idx2wd->old_file.path, idx2wd->old_file.mode,
- idx2wd->old_file.size, &idx2wd->old_file.id))
+ &idx2wd->old_file.id, diff, idx2wd->old_file.path,
+ idx2wd->old_file.mode, idx2wd->old_file.size))
idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
if (git_oid_iszero(&idx2wd->new_file.id) &&
diff->new_src == GIT_ITERATOR_TYPE_WORKDIR &&
!git_diff__oid_for_file(
- diff->repo, idx2wd->new_file.path, idx2wd->new_file.mode,
- idx2wd->new_file.size, &idx2wd->new_file.id))
+ &idx2wd->new_file.id, diff, idx2wd->new_file.path,
+ idx2wd->new_file.mode, idx2wd->new_file.size))
idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id))
@@ -225,6 +225,28 @@ static git_status_list *git_status_list_alloc(git_index *index)
return status;
}
+static int status_validate_options(const git_status_options *opts)
+{
+ if (!opts)
+ return 0;
+
+ GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+
+ if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) {
+ giterr_set(GITERR_INVALID, "Unknown status 'show' option");
+ return -1;
+ }
+
+ if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 &&
+ (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) {
+ giterr_set(GITERR_INVALID, "Updating index from status "
+ "is not allowed when index refresh is disabled");
+ return -1;
+ }
+
+ return 0;
+}
+
int git_status_list_new(
git_status_list **out,
git_repository *repo,
@@ -240,11 +262,10 @@ int git_status_list_new(
int error = 0;
unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
- assert(show <= GIT_STATUS_SHOW_WORKDIR_ONLY);
-
*out = NULL;
- GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+ if (status_validate_options(opts) < 0)
+ return -1;
if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
(error = git_repository_index(&index, repo)) < 0)
@@ -287,6 +308,8 @@ int git_status_list_new(
diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
+ if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX;
if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
findopt.flags = findopt.flags |
@@ -495,14 +518,31 @@ int git_status_should_ignore(
return git_ignore_path_is_ignored(ignored, repo, path);
}
-int git_status_init_options(git_status_options* opts, int version)
+int git_status_init_options(git_status_options *opts, unsigned int version)
{
- if (version != GIT_STATUS_OPTIONS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_status_options", version);
- return -1;
- } else {
- git_status_options o = GIT_STATUS_OPTIONS_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT);
+ return 0;
+}
+
+int git_status_list_get_perfdata(
+ git_diff_perfdata *out, const git_status_list *status)
+{
+ assert(out);
+ GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
+
+ out->stat_calls = 0;
+ out->oid_calculations = 0;
+
+ if (status->head2idx) {
+ out->stat_calls += status->head2idx->perf.stat_calls;
+ out->oid_calculations += status->head2idx->perf.oid_calculations;
+ }
+ if (status->idx2wd) {
+ out->stat_calls += status->idx2wd->perf.stat_calls;
+ out->oid_calculations += status->idx2wd->perf.oid_calculations;
}
+
+ return 0;
}
+
diff --git a/src/sysdir.c b/src/sysdir.c
index aebf23135..e0c24f3b3 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -194,14 +194,19 @@ static int git_sysdir_find_in_dirlist(
const git_buf *syspath;
GITERR_CHECK_ERROR(git_sysdir_get(&syspath, which));
+ if (!syspath || !git_buf_len(syspath))
+ goto done;
for (scan = git_buf_cstr(syspath); scan; scan = next) {
- for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR);
- next && next > scan && next[-1] == '\\';
- next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR))
- /* find unescaped separator or end of string */;
+ /* find unescaped separator or end of string */
+ for (next = scan; *next; ++next) {
+ if (*next == GIT_PATH_LIST_SEPARATOR &&
+ (next <= scan || next[-1] != '\\'))
+ break;
+ }
- len = next ? (size_t)(next++ - scan) : strlen(scan);
+ len = (size_t)(next - scan);
+ next = (*next ? next + 1 : NULL);
if (!len)
continue;
@@ -213,6 +218,7 @@ static int git_sysdir_find_in_dirlist(
return 0;
}
+done:
git_buf_free(path);
giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
return GIT_ENOTFOUND;
diff --git a/src/transport.c b/src/transport.c
index dc074a503..2194b1864 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -218,14 +218,9 @@ int git_remote_supported_url(const char* url)
return fn != &git_transport_dummy;
}
-int git_transport_init(git_transport* opts, int version)
+int git_transport_init(git_transport *opts, unsigned int version)
{
- if (version != GIT_TRANSPORT_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_transport", version);
- return -1;
- } else {
- git_transport o = GIT_TRANSPORT_INIT;
- memcpy(opts, &o, sizeof(o));
- return 0;
- }
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_transport, GIT_TRANSPORT_INIT);
+ return 0;
}
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
index 5eadc7b3e..b187db01c 100644
--- a/tests/attr/ignore.c
+++ b/tests/attr/ignore.c
@@ -148,12 +148,11 @@ void test_attr_ignore__skip_gitignore_directory(void)
void test_attr_ignore__expand_tilde_to_homedir(void)
{
- git_buf cleanup = GIT_BUF_INIT;
git_config *cfg;
assert_is_ignored(false, "example.global_with_tilde");
- cl_fake_home(&cleanup);
+ cl_fake_home();
/* construct fake home with fake global excludes */
cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n");
@@ -168,7 +167,7 @@ void test_attr_ignore__expand_tilde_to_homedir(void)
cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_fake_home_cleanup(&cleanup);
+ cl_fake_home_cleanup(NULL);
git_attr_cache_flush(g_repo); /* must reset to pick up change */
diff --git a/tests/attr/repo.c b/tests/attr/repo.c
index 9aab7ed96..5e812a72b 100644
--- a/tests/attr/repo.c
+++ b/tests/attr/repo.c
@@ -9,11 +9,6 @@ static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
{
- /* Before each test, instantiate the attr repo from the fixtures and
- * rename the .gitted to .git so it is a repo with a working dir.
- * Also rename gitattributes to .gitattributes, because it contains
- * macro definitions which are only allowed in the root.
- */
g_repo = cl_git_sandbox_init("attr");
}
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
index 6f6143dad..f457adb33 100644
--- a/tests/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -484,23 +484,36 @@ void clar__assert_equal_file(
(size_t)expected_bytes, (size_t)total_bytes);
}
-void cl_fake_home(git_buf *restore)
+static char *_cl_restore_home = NULL;
+
+void cl_fake_home_cleanup(void *payload)
+{
+ char *restore = _cl_restore_home;
+ _cl_restore_home = NULL;
+
+ GIT_UNUSED(payload);
+
+ if (restore) {
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore));
+ git__free(restore);
+ }
+}
+
+void cl_fake_home(void)
{
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_libgit2_opts(
- GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore));
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &path));
+
+ _cl_restore_home = git_buf_detach(&path);
+ cl_set_cleanup(cl_fake_home_cleanup, NULL);
- cl_must_pass(p_mkdir("home", 0777));
+ if (!git_path_exists("home"))
+ cl_must_pass(p_mkdir("home", 0777));
cl_git_pass(git_path_prettify(&path, "home", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_buf_free(&path);
}
-
-void cl_fake_home_cleanup(git_buf *restore)
-{
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore->ptr));
- git_buf_free(restore);
-}
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
index 082fa9f4a..f84d9e353 100644
--- a/tests/clar_libgit2.h
+++ b/tests/clar_libgit2.h
@@ -131,7 +131,12 @@ int cl_repo_get_bool(git_repository *repo, const char *cfg);
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
-void cl_fake_home(git_buf *restore);
-void cl_fake_home_cleanup(git_buf *restore);
+/* set up a fake "home" directory and set libgit2 GLOBAL search path.
+ *
+ * automatically configures cleanup function to restore the regular search
+ * path, although you can call it explicitly if you wish (with NULL).
+ */
+void cl_fake_home(void);
+void cl_fake_home_cleanup(void *);
#endif
diff --git a/tests/config/global.c b/tests/config/global.c
index d5f95f504..006b34628 100644
--- a/tests/config/global.c
+++ b/tests/config/global.c
@@ -2,22 +2,36 @@
#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;
- cl_assert_equal_i(0, p_mkdir("home", 0777));
+ /* 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(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_assert_equal_i(0, p_mkdir("xdg", 0777));
- cl_assert_equal_i(0, p_mkdir("xdg/git", 0777));
+ cl_git_pass(git_futils_mkdir_r("xdg/git", NULL, 0777));
cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
- cl_assert_equal_i(0, p_mkdir("etc", 0777));
+ cl_git_pass(git_futils_mkdir_r("etc", NULL, 0777));
cl_git_pass(git_path_prettify(&path, "etc", NULL));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
@@ -27,13 +41,18 @@ 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));
-
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, NULL);
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL);
- git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL);
}
void test_config_global__open_global(void)
diff --git a/tests/diff/blob.c b/tests/diff/blob.c
index d1fff9c5b..527007965 100644
--- a/tests/diff/blob.c
+++ b/tests/diff/blob.c
@@ -26,7 +26,7 @@ void test_diff_blob__initialize(void)
g_repo = cl_git_sandbox_init("attr");
- cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+ cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION));
opts.context_lines = 1;
memset(&expected, 0, sizeof(expected));
diff --git a/tests/diff/tree.c b/tests/diff/tree.c
index 582174b8b..6ab49fdb0 100644
--- a/tests/diff/tree.c
+++ b/tests/diff/tree.c
@@ -9,7 +9,7 @@ static diff_expects expect;
void test_diff_tree__initialize(void)
{
- cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+ cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION));
memset(&expect, 0, sizeof(expect));
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
index 6128e820e..a6d48abc6 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -1,13 +1,10 @@
#include "clar_libgit2.h"
#include "diff_helpers.h"
#include "repository.h"
+#include "git2/sys/diff.h"
static git_repository *g_repo = NULL;
-void test_diff_workdir__initialize(void)
-{
-}
-
void test_diff_workdir__cleanup(void)
{
cl_git_sandbox_cleanup();
@@ -60,6 +57,14 @@ void test_diff_workdir__to_index(void)
cl_assert_equal_i(5, exp.line_dels);
}
+ {
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(
+ 13 /* in root */ + 3 /* in subdir */, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+ }
+
git_diff_free(diff);
}
@@ -1490,3 +1495,88 @@ void test_diff_workdir__with_stale_index(void)
git_index_free(idx);
}
+
+static int touch_file(void *payload, git_buf *path)
+{
+ int fd;
+ char b;
+
+ GIT_UNUSED(payload);
+ if (git_path_isdir(path->ptr))
+ return 0;
+
+ cl_assert((fd = p_open(path->ptr, O_RDWR)) >= 0);
+ cl_assert_equal_i(1, p_read(fd, &b, 1));
+ cl_must_pass(p_lseek(fd, 0, SEEK_SET));
+ cl_must_pass(p_write(fd, &b, 1));
+ cl_must_pass(p_close(fd));
+
+ return 0;
+}
+
+static void basic_diff_status(git_diff **out, const git_diff_options *opts)
+{
+ diff_expects exp;
+
+ cl_git_pass(git_diff_index_to_workdir(out, g_repo, NULL, opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ *out, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+}
+
+void test_diff_workdir__can_update_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ /* touch all the files so stat times are different */
+ {
+ git_buf path = GIT_BUF_INIT;
+ cl_git_pass(git_buf_sets(&path, "status"));
+ cl_git_pass(git_path_direach(&path, 0, touch_file, NULL));
+ git_buf_free(&path);
+ }
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_diff_free(diff);
+
+ /* now allow diff to update stat cache */
+ opts.flags |= GIT_DIFF_UPDATE_INDEX;
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_diff_free(diff);
+
+ /* now if we do it again, we should see fewer OID calculations */
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(0, perf.oid_calculations);
+
+ git_diff_free(diff);
+}
diff --git a/tests/refs/branches/create.c b/tests/refs/branches/create.c
index b91eed6e8..38af2f681 100644
--- a/tests/refs/branches/create.c
+++ b/tests/refs/branches/create.c
@@ -7,10 +7,9 @@ static git_reference *branch;
void test_refs_branches_create__initialize(void)
{
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-
+ repo = cl_git_sandbox_init("testrepo.git");
branch = NULL;
+ target = NULL;
}
void test_refs_branches_create__cleanup(void)
@@ -21,10 +20,8 @@ void test_refs_branches_create__cleanup(void)
git_commit_free(target);
target = NULL;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
}
static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha)
diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c
index ed5f1627b..e3199e230 100644
--- a/tests/refs/branches/delete.c
+++ b/tests/refs/branches/delete.c
@@ -10,8 +10,7 @@ void test_refs_branches_delete__initialize(void)
{
git_oid id;
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ repo = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL, NULL));
@@ -22,10 +21,8 @@ void test_refs_branches_delete__cleanup(void)
git_reference_free(fake_remote);
fake_remote = NULL;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
}
void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
diff --git a/tests/refs/branches/ishead.c b/tests/refs/branches/ishead.c
index 12a8c4449..d16a79652 100644
--- a/tests/refs/branches/ishead.c
+++ b/tests/refs/branches/ishead.c
@@ -7,7 +7,8 @@ static git_reference *branch;
void test_refs_branches_ishead__initialize(void)
{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ repo = cl_git_sandbox_init("testrepo.git");
+ branch = NULL;
}
void test_refs_branches_ishead__cleanup(void)
@@ -15,7 +16,7 @@ void test_refs_branches_ishead__cleanup(void)
git_reference_free(branch);
branch = NULL;
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
repo = NULL;
}
@@ -28,34 +29,20 @@ void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void)
void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void)
{
- git_repository_free(repo);
-
- repo = cl_git_sandbox_init("testrepo.git");
-
make_head_unborn(repo, NON_EXISTING_HEAD);
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
cl_assert_equal_i(false, git_branch_is_head(branch));
-
- cl_git_sandbox_cleanup();
- repo = NULL;
}
void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void)
{
- git_repository_free(repo);
-
- repo = cl_git_sandbox_init("testrepo.git");
-
delete_head(repo);
cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
cl_assert_equal_i(false, git_branch_is_head(branch));
-
- cl_git_sandbox_cleanup();
- repo = NULL;
}
void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void)
@@ -95,9 +82,6 @@ void test_refs_branches_ishead__only_direct_references_are_considered(void)
{
git_reference *linked, *super, *head;
- git_repository_free(repo);
- repo = cl_git_sandbox_init("testrepo.git");
-
cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0, NULL, NULL));
cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0, NULL, NULL));
cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1, NULL, NULL));
@@ -111,6 +95,4 @@ void test_refs_branches_ishead__only_direct_references_are_considered(void)
git_reference_free(linked);
git_reference_free(super);
git_reference_free(head);
- cl_git_sandbox_cleanup();
- repo = NULL;
}
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
index d88b2eb6b..caa1f1927 100644
--- a/tests/status/ignore.c
+++ b/tests/status/ignore.c
@@ -364,7 +364,6 @@ void test_status_ignore__leading_slash_ignores(void)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
status_entry_counts counts;
- git_buf home = GIT_BUF_INIT;
static const char *paths_2[] = {
"dir/.gitignore",
"dir/a/ignore_me",
@@ -385,7 +384,7 @@ void test_status_ignore__leading_slash_ignores(void)
make_test_data(test_repo_1, test_files_1);
- cl_fake_home(&home);
+ cl_fake_home();
cl_git_mkfile("home/.gitignore", "/ignore_me\n");
{
git_config *cfg;
@@ -412,8 +411,6 @@ void test_status_ignore__leading_slash_ignores(void)
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);
-
- cl_fake_home_cleanup(&home);
}
void test_status_ignore__contained_dir_with_matching_name(void)
diff --git a/tests/status/worktree.c b/tests/status/worktree.c
index def3d60f0..ca9068aba 100644
--- a/tests/status/worktree.c
+++ b/tests/status/worktree.c
@@ -5,6 +5,8 @@
#include "posix.h"
#include "util.h"
#include "path.h"
+#include "../diff/diff_helpers.h"
+#include "git2/sys/diff.h"
/**
* Cleanup
@@ -40,11 +42,15 @@ void test_status_worktree__whole_repository(void)
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
-void assert_show(const int entry_counts, const char *entry_paths[],
- const unsigned int entry_statuses[], git_status_show_t show)
+void assert_show(
+ const int entry_counts,
+ const char *entry_paths[],
+ const unsigned int entry_statuses[],
+ git_repository *repo,
+ git_status_show_t show,
+ unsigned int extra_flags)
{
status_entry_counts counts;
- git_repository *repo = cl_git_sandbox_init("status");
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
memset(&counts, 0x0, sizeof(status_entry_counts));
@@ -52,7 +58,7 @@ void assert_show(const int entry_counts, const char *entry_paths[],
counts.expected_paths = entry_paths;
counts.expected_statuses = entry_statuses;
- opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | extra_flags;
opts.show = show;
cl_git_pass(
@@ -67,19 +73,19 @@ void assert_show(const int entry_counts, const char *entry_paths[],
void test_status_worktree__show_index_and_workdir(void)
{
assert_show(entry_count0, entry_paths0, entry_statuses0,
- GIT_STATUS_SHOW_INDEX_AND_WORKDIR);
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_AND_WORKDIR, 0);
}
void test_status_worktree__show_index_only(void)
{
assert_show(entry_count5, entry_paths5, entry_statuses5,
- GIT_STATUS_SHOW_INDEX_ONLY);
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_ONLY, 0);
}
void test_status_worktree__show_workdir_only(void)
{
assert_show(entry_count6, entry_paths6, entry_statuses6,
- GIT_STATUS_SHOW_WORKDIR_ONLY);
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_WORKDIR_ONLY, 0);
}
/* this test is equivalent to t18-status.c:statuscb1 */
@@ -578,7 +584,11 @@ void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void
cl_git_pass(git_status_file(&status, repo, "current_file"));
- cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+ /* stat data on file should no longer match stat cache, even though
+ * file diff will be empty because of line-ending conversion - matches
+ * the Git command-line behavior here.
+ */
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
}
void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void)
@@ -873,3 +883,55 @@ void test_status_worktree__long_filenames(void)
cl_assert_equal_i(0, counts.wrong_sorted_path);
}
+/* The update stat cache tests mostly just mirror other tests and try
+ * to make sure that updating the stat cache doesn't change the results
+ * while reducing the amount of work that needs to be done
+ */
+
+static void check_status0(git_status_list *status)
+{
+ size_t i, max_i = git_status_list_entrycount(status);
+ cl_assert_equal_sz(entry_count0, max_i);
+ for (i = 0; i < max_i; ++i) {
+ const git_status_entry *entry = git_status_byindex(status, i);
+ cl_assert_equal_i(entry_statuses0[i], entry->status);
+ }
+}
+
+void test_status_worktree__update_stat_cache_0(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *status;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_status_list_free(status);
+
+ opts.flags |= GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_status_list_free(status);
+
+ opts.flags &= ~GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(0, perf.oid_calculations);
+
+ git_status_list_free(status);
+}
diff --git a/tests/structinit/structinit.c b/tests/structinit/structinit.c
index 2942099dd..38bedada7 100644
--- a/tests/structinit/structinit.c
+++ b/tests/structinit/structinit.c
@@ -48,7 +48,7 @@ void test_structinit_structinit__compare(void)
/* checkout */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, \
- GIT_CHECKOUT_OPTIONS_INIT, git_checkout_init_opts);
+ GIT_CHECKOUT_OPTIONS_INIT, git_checkout_init_options);
/* clone */
CHECK_MACRO_FUNC_INIT_EQUAL( \
@@ -98,7 +98,7 @@ void test_structinit_structinit__compare(void)
/* revert */
CHECK_MACRO_FUNC_INIT_EQUAL( \
git_revert_options, GIT_REVERT_OPTIONS_VERSION, \
- GIT_REVERT_OPTIONS_INIT, git_revert_init_opts);
+ GIT_REVERT_OPTIONS_INIT, git_revert_init_options);
/* status */
CHECK_MACRO_FUNC_INIT_EQUAL( \