mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-05 20:33:41 +00:00
Merge branch 'master' into fix-init-ordering
This commit is contained in:
commit
9830fbba05
22
CHANGELOG.md
22
CHANGELOG.md
@ -1,4 +1,21 @@
|
|||||||
v0.22 + 1
|
v0.23 + 1
|
||||||
|
-------
|
||||||
|
|
||||||
|
### Changes or improvements
|
||||||
|
|
||||||
|
* Custom filters can now be registered with wildcard attributes, for
|
||||||
|
example `filter=*`. Consumers should examine the attributes parameter
|
||||||
|
of the `check` function for details.
|
||||||
|
|
||||||
|
### API additions
|
||||||
|
|
||||||
|
### API removals
|
||||||
|
|
||||||
|
### Breaking API changes
|
||||||
|
|
||||||
|
* `git_cert` descendent types now have a proper `parent` member
|
||||||
|
|
||||||
|
v0.23
|
||||||
------
|
------
|
||||||
|
|
||||||
### Changes or improvements
|
### Changes or improvements
|
||||||
@ -177,6 +194,9 @@ v0.22 + 1
|
|||||||
* `git_submodule_save()` has been removed. The submodules are no
|
* `git_submodule_save()` has been removed. The submodules are no
|
||||||
longer configured via the objects.
|
longer configured via the objects.
|
||||||
|
|
||||||
|
* `git_submodule_reload_all()` has been removed as we no longer cache
|
||||||
|
submodules.
|
||||||
|
|
||||||
### Breaking API changes
|
### Breaking API changes
|
||||||
|
|
||||||
* `git_smart_subtransport_cb` now has a `param` parameter.
|
* `git_smart_subtransport_cb` now has a `param` parameter.
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
# > cmake --build . --target install
|
# > cmake --build . --target install
|
||||||
|
|
||||||
PROJECT(libgit2 C)
|
PROJECT(libgit2 C)
|
||||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||||
CMAKE_POLICY(SET CMP0015 NEW)
|
CMAKE_POLICY(SET CMP0015 NEW)
|
||||||
|
|
||||||
# Add find modules to the path
|
# Add find modules to the path
|
||||||
@ -336,6 +336,7 @@ IF (MSVC)
|
|||||||
|
|
||||||
IF (MSVC_CRTDBG)
|
IF (MSVC_CRTDBG)
|
||||||
SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG")
|
SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG")
|
||||||
|
SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}" "Dbghelp.lib")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# /Zi - Create debugging information
|
# /Zi - Create debugging information
|
||||||
|
@ -88,7 +88,7 @@ Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthrea
|
|||||||
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
|
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
|
||||||
for threading.
|
for threading.
|
||||||
|
|
||||||
The `libgit2` library is built using [CMake](<http://www.cmake.org>) (version 2.6 or newer) on all platforms.
|
The `libgit2` library is built using [CMake](<http://www.cmake.org>) (version 2.8 or newer) on all platforms.
|
||||||
|
|
||||||
On most systems you can build the library using the following commands
|
On most systems you can build the library using the following commands
|
||||||
|
|
||||||
|
@ -23,32 +23,6 @@ static int progress_cb(const char *str, int len, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *download(void *ptr)
|
|
||||||
{
|
|
||||||
struct dl_data *data = (struct dl_data *)ptr;
|
|
||||||
|
|
||||||
// Connect to the remote end specifying that we want to fetch
|
|
||||||
// information from it.
|
|
||||||
if (git_remote_connect(data->remote, GIT_DIRECTION_FETCH, &data->fetch_opts->callbacks) < 0) {
|
|
||||||
data->ret = -1;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download the packfile and index it. This function updates the
|
|
||||||
// amount of received data and the indexer stats which lets you
|
|
||||||
// inform the user about progress.
|
|
||||||
if (git_remote_download(data->remote, NULL, data->fetch_opts) < 0) {
|
|
||||||
data->ret = -1;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->ret = 0;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
data->finished = 1;
|
|
||||||
return &data->ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function gets called for each remote-tracking branch that gets
|
* This function gets called for each remote-tracking branch that gets
|
||||||
* updated. The message we output depends on whether it's a new one or
|
* updated. The message we output depends on whether it's a new one or
|
||||||
@ -73,6 +47,24 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This gets called during the download and indexing. Here we show
|
||||||
|
* processed and total objects in the pack and the amount of received
|
||||||
|
* data. Most frontends will probably want to show a percentage and
|
||||||
|
* the download rate.
|
||||||
|
*/
|
||||||
|
static int transfer_progress_cb(const git_transfer_progress *stats, void *payload)
|
||||||
|
{
|
||||||
|
if (stats->received_objects == stats->total_objects) {
|
||||||
|
printf("Resolving deltas %d/%d\r",
|
||||||
|
stats->indexed_deltas, stats->total_deltas);
|
||||||
|
} else if (stats->total_objects > 0) {
|
||||||
|
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
|
||||||
|
stats->received_objects, stats->total_objects,
|
||||||
|
stats->indexed_objects, stats->received_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Entry point for this command */
|
/** Entry point for this command */
|
||||||
int fetch(git_repository *repo, int argc, char **argv)
|
int fetch(git_repository *repo, int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -80,9 +72,6 @@ int fetch(git_repository *repo, int argc, char **argv)
|
|||||||
const git_transfer_progress *stats;
|
const git_transfer_progress *stats;
|
||||||
struct dl_data data;
|
struct dl_data data;
|
||||||
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
|
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
|
||||||
#ifndef _WIN32
|
|
||||||
pthread_t worker;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]);
|
fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]);
|
||||||
@ -99,67 +88,31 @@ int fetch(git_repository *repo, int argc, char **argv)
|
|||||||
// Set up the callbacks (only update_tips for now)
|
// Set up the callbacks (only update_tips for now)
|
||||||
fetch_opts.callbacks.update_tips = &update_cb;
|
fetch_opts.callbacks.update_tips = &update_cb;
|
||||||
fetch_opts.callbacks.sideband_progress = &progress_cb;
|
fetch_opts.callbacks.sideband_progress = &progress_cb;
|
||||||
|
fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
|
||||||
fetch_opts.callbacks.credentials = cred_acquire_cb;
|
fetch_opts.callbacks.credentials = cred_acquire_cb;
|
||||||
|
|
||||||
// Set up the information for the background worker thread
|
/**
|
||||||
data.remote = remote;
|
* Perform the fetch with the configured refspecs from the
|
||||||
data.fetch_opts = &fetch_opts;
|
* config. Update the reflog for the updated references with
|
||||||
data.ret = 0;
|
* "fetch".
|
||||||
data.finished = 0;
|
*/
|
||||||
|
if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
|
||||||
stats = git_remote_stats(remote);
|
return -1;
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
download(&data);
|
|
||||||
#else
|
|
||||||
pthread_create(&worker, NULL, download, &data);
|
|
||||||
|
|
||||||
// Loop while the worker thread is still running. Here we show processed
|
|
||||||
// and total objects in the pack and the amount of received
|
|
||||||
// data. Most frontends will probably want to show a percentage and
|
|
||||||
// the download rate.
|
|
||||||
do {
|
|
||||||
usleep(10000);
|
|
||||||
|
|
||||||
if (stats->received_objects == stats->total_objects) {
|
|
||||||
printf("Resolving deltas %d/%d\r",
|
|
||||||
stats->indexed_deltas, stats->total_deltas);
|
|
||||||
} else if (stats->total_objects > 0) {
|
|
||||||
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
|
|
||||||
stats->received_objects, stats->total_objects,
|
|
||||||
stats->indexed_objects, stats->received_bytes);
|
|
||||||
}
|
|
||||||
} while (!data.finished);
|
|
||||||
|
|
||||||
if (data.ret < 0)
|
|
||||||
goto on_error;
|
|
||||||
|
|
||||||
pthread_join(worker, NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there are local objects (we got a thin pack), then tell
|
* If there are local objects (we got a thin pack), then tell
|
||||||
* the user how many objects we saved from having to cross the
|
* the user how many objects we saved from having to cross the
|
||||||
* network.
|
* network.
|
||||||
*/
|
*/
|
||||||
|
stats = git_remote_stats(remote);
|
||||||
if (stats->local_objects > 0) {
|
if (stats->local_objects > 0) {
|
||||||
printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n",
|
printf("\rReceived %d/%d objects in %" PRIuZ " bytes (used %d local objects)\n",
|
||||||
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
|
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
|
||||||
} else{
|
} else{
|
||||||
printf("\rReceived %d/%d objects in %zu bytes\n",
|
printf("\rReceived %d/%d objects in %" PRIuZ "bytes\n",
|
||||||
stats->indexed_objects, stats->total_objects, stats->received_bytes);
|
stats->indexed_objects, stats->total_objects, stats->received_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect the underlying connection to prevent from idling.
|
|
||||||
git_remote_disconnect(remote);
|
|
||||||
|
|
||||||
// Update the references in the remote's namespace to point to the
|
|
||||||
// right commits. This may be needed even if there was no packfile
|
|
||||||
// to download, which can happen e.g. when the branches have been
|
|
||||||
// changed but all the needed objects are available locally.
|
|
||||||
if (git_remote_update_tips(remote, &fetch_opts.callbacks, 1, fetch_opts.download_tags, NULL) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
git_remote_free(remote);
|
git_remote_free(remote);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -39,6 +39,7 @@ ok Adam Simpkins <adam@adamsimpkins.net> (http transport)
|
|||||||
ok Adrian Johnson <ajohnson@redneon.com>
|
ok Adrian Johnson <ajohnson@redneon.com>
|
||||||
ok Alexey Shumkin <alex.crezoff@gmail.com>
|
ok Alexey Shumkin <alex.crezoff@gmail.com>
|
||||||
ok Andreas Ericsson <ae@op5.se>
|
ok Andreas Ericsson <ae@op5.se>
|
||||||
|
ok Antoine Pelisse <apelisse@gmail.com>
|
||||||
ok Boyd Lynn Gerber <gerberb@zenez.com>
|
ok Boyd Lynn Gerber <gerberb@zenez.com>
|
||||||
ok Brandon Casey <drafnel@gmail.com>
|
ok Brandon Casey <drafnel@gmail.com>
|
||||||
ok Brian Downing <bdowning@lavos.net>
|
ok Brian Downing <bdowning@lavos.net>
|
||||||
|
@ -835,6 +835,25 @@ GIT_EXTERN(int) git_diff_tree_to_workdir_with_index(
|
|||||||
git_tree *old_tree,
|
git_tree *old_tree,
|
||||||
const git_diff_options *opts); /**< can be NULL for defaults */
|
const git_diff_options *opts); /**< can be NULL for defaults */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a diff with the difference between two index objects.
|
||||||
|
*
|
||||||
|
* The first index will be used for the "old_file" side of the delta and the
|
||||||
|
* second index will be used for the "new_file" side of the delta.
|
||||||
|
*
|
||||||
|
* @param diff Output pointer to a git_diff pointer to be allocated.
|
||||||
|
* @param repo The repository containing the indexes.
|
||||||
|
* @param old_index A git_index object to diff from.
|
||||||
|
* @param new_index A git_index object to diff to.
|
||||||
|
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_diff_index_to_index(
|
||||||
|
git_diff **diff,
|
||||||
|
git_repository *repo,
|
||||||
|
git_index *old_index,
|
||||||
|
git_index *new_index,
|
||||||
|
const git_diff_options *opts); /**< can be NULL for defaults */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge one diff into another.
|
* Merge one diff into another.
|
||||||
*
|
*
|
||||||
|
@ -137,7 +137,13 @@ GIT_EXTERN(int) git_filter_list_apply_to_data(
|
|||||||
git_buf *in);
|
git_buf *in);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply filter list to the contents of a file on disk
|
* Apply a filter list to the contents of a file on disk
|
||||||
|
*
|
||||||
|
* @param out buffer into which to store the filtered file
|
||||||
|
* @param filters the list of filters to apply
|
||||||
|
* @param repo the repository in which to perform the filtering
|
||||||
|
* @param path the path of the file to filter, a relative path will be
|
||||||
|
* taken as relative to the workdir
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_filter_list_apply_to_file(
|
GIT_EXTERN(int) git_filter_list_apply_to_file(
|
||||||
git_buf *out,
|
git_buf *out,
|
||||||
@ -146,24 +152,51 @@ GIT_EXTERN(int) git_filter_list_apply_to_file(
|
|||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply filter list to the contents of a blob
|
* Apply a filter list to the contents of a blob
|
||||||
|
*
|
||||||
|
* @param out buffer into which to store the filtered file
|
||||||
|
* @param filters the list of filters to apply
|
||||||
|
* @param blob the blob to filter
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_filter_list_apply_to_blob(
|
GIT_EXTERN(int) git_filter_list_apply_to_blob(
|
||||||
git_buf *out,
|
git_buf *out,
|
||||||
git_filter_list *filters,
|
git_filter_list *filters,
|
||||||
git_blob *blob);
|
git_blob *blob);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a filter list to an arbitrary buffer as a stream
|
||||||
|
*
|
||||||
|
* @param filters the list of filters to apply
|
||||||
|
* @param data the buffer to filter
|
||||||
|
* @param target the stream into which the data will be written
|
||||||
|
*/
|
||||||
GIT_EXTERN(int) git_filter_list_stream_data(
|
GIT_EXTERN(int) git_filter_list_stream_data(
|
||||||
git_filter_list *filters,
|
git_filter_list *filters,
|
||||||
git_buf *data,
|
git_buf *data,
|
||||||
git_writestream *target);
|
git_writestream *target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a filter list to a file as a stream
|
||||||
|
*
|
||||||
|
* @param filters the list of filters to apply
|
||||||
|
* @param repo the repository in which to perform the filtering
|
||||||
|
* @param path the path of the file to filter, a relative path will be
|
||||||
|
* taken as relative to the workdir
|
||||||
|
* @param target the stream into which the data will be written
|
||||||
|
*/
|
||||||
GIT_EXTERN(int) git_filter_list_stream_file(
|
GIT_EXTERN(int) git_filter_list_stream_file(
|
||||||
git_filter_list *filters,
|
git_filter_list *filters,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *path,
|
const char *path,
|
||||||
git_writestream *target);
|
git_writestream *target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a filter list to a blob as a stream
|
||||||
|
*
|
||||||
|
* @param filters the list of filters to apply
|
||||||
|
* @param blob the blob to filter
|
||||||
|
* @param target the stream into which the data will be written
|
||||||
|
*/
|
||||||
GIT_EXTERN(int) git_filter_list_stream_blob(
|
GIT_EXTERN(int) git_filter_list_stream_blob(
|
||||||
git_filter_list *filters,
|
git_filter_list *filters,
|
||||||
git_blob *blob,
|
git_blob *blob,
|
||||||
|
@ -511,6 +511,14 @@ typedef enum {
|
|||||||
GIT_REMOTE_DOWNLOAD_TAGS_ALL,
|
GIT_REMOTE_DOWNLOAD_TAGS_ALL,
|
||||||
} git_remote_autotag_option_t;
|
} git_remote_autotag_option_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch options structure.
|
||||||
|
*
|
||||||
|
* Zero out for defaults. Initialize with `GIT_FETCH_OPTIONS_INIT` macro to
|
||||||
|
* correctly set the `version` field. E.g.
|
||||||
|
*
|
||||||
|
* git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int version;
|
int version;
|
||||||
|
|
||||||
@ -739,7 +747,7 @@ GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote);
|
|||||||
* stored here for further processing by the caller. Always free this
|
* stored here for further processing by the caller. Always free this
|
||||||
* strarray on successful return.
|
* strarray on successful return.
|
||||||
* @param repo the repository in which to rename
|
* @param repo the repository in which to rename
|
||||||
* @param name the current name of the reamote
|
* @param name the current name of the remote
|
||||||
* @param new_name the new name the remote should bear
|
* @param new_name the new name the remote should bear
|
||||||
* @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
|
* @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
|
||||||
*/
|
*/
|
||||||
|
@ -583,17 +583,6 @@ GIT_EXTERN(int) git_submodule_open(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
|
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
|
||||||
|
|
||||||
/**
|
|
||||||
* Reread all submodule info.
|
|
||||||
*
|
|
||||||
* Call this to reload all cached submodule information for the repo.
|
|
||||||
*
|
|
||||||
* @param repo The repository to reload submodule data for
|
|
||||||
* @param force Force full reload even if the data doesn't seem out of date
|
|
||||||
* @return 0 on success, <0 on error
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the status for a submodule.
|
* Get the status for a submodule.
|
||||||
*
|
*
|
||||||
|
@ -240,7 +240,10 @@ typedef void (*git_filter_cleanup_fn)(
|
|||||||
* for this filter (e.g. "eol crlf text"). If the attribute name is bare,
|
* for this filter (e.g. "eol crlf text"). If the attribute name is bare,
|
||||||
* it will be simply loaded and passed to the `check` callback. If it has
|
* it will be simply loaded and passed to the `check` callback. If it has
|
||||||
* a value (i.e. "name=value"), the attribute must match that value for
|
* a value (i.e. "name=value"), the attribute must match that value for
|
||||||
* the filter to be applied.
|
* the filter to be applied. The value may be a wildcard (eg, "name=*"),
|
||||||
|
* in which case the filter will be invoked for any value for the given
|
||||||
|
* attribute name. See the attribute parameter of the `check` callback
|
||||||
|
* for the attribute value that was specified.
|
||||||
*
|
*
|
||||||
* The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks
|
* The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks
|
||||||
* are all documented above with the respective function pointer typedefs.
|
* are all documented above with the respective function pointer typedefs.
|
||||||
|
@ -37,39 +37,32 @@ typedef enum {
|
|||||||
* Hostkey information taken from libssh2
|
* Hostkey information taken from libssh2
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
git_cert parent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of certificate. Here to share the header with
|
* A hostkey type from libssh2, either
|
||||||
* `git_cert`.
|
* `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
|
||||||
*/
|
*/
|
||||||
git_cert_t cert_type;
|
|
||||||
/**
|
|
||||||
* A hostkey type from libssh2, either
|
|
||||||
* `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1`
|
|
||||||
*/
|
|
||||||
git_cert_ssh_t type;
|
git_cert_ssh_t type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
|
* Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will
|
||||||
* have the MD5 hash of the hostkey.
|
* have the MD5 hash of the hostkey.
|
||||||
*/
|
*/
|
||||||
unsigned char hash_md5[16];
|
unsigned char hash_md5[16];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
|
* Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will
|
||||||
* have the SHA-1 hash of the hostkey.
|
* have the SHA-1 hash of the hostkey.
|
||||||
*/
|
*/
|
||||||
unsigned char hash_sha1[20];
|
unsigned char hash_sha1[20];
|
||||||
} git_cert_hostkey;
|
} git_cert_hostkey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* X.509 certificate information
|
* X.509 certificate information
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/**
|
git_cert parent;
|
||||||
* Type of certificate. Here to share the header with
|
|
||||||
* `git_cert`.
|
|
||||||
*/
|
|
||||||
git_cert_t cert_type;
|
|
||||||
/**
|
/**
|
||||||
* Pointer to the X.509 certificate data
|
* Pointer to the X.509 certificate data
|
||||||
*/
|
*/
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
brew install libssh2 cmake
|
brew update
|
||||||
|
brew install libssh2
|
||||||
|
@ -304,21 +304,16 @@ static void blame_chunk(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int my_emit(
|
static int my_emit(
|
||||||
xdfenv_t *xe,
|
long start_a, long count_a,
|
||||||
xdchange_t *xscr,
|
long start_b, long count_b,
|
||||||
xdemitcb_t *ecb,
|
void *cb_data)
|
||||||
xdemitconf_t const *xecfg)
|
|
||||||
{
|
{
|
||||||
xdchange_t *xch = xscr;
|
blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data;
|
||||||
GIT_UNUSED(xe);
|
|
||||||
GIT_UNUSED(xecfg);
|
blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
|
||||||
while (xch) {
|
d->plno = start_a + count_a;
|
||||||
blame_chunk_cb_data *d = ecb->priv;
|
d->tlno = start_b + count_b;
|
||||||
blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent);
|
|
||||||
d->plno = xch->i1 + xch->chg1;
|
|
||||||
d->tlno = xch->i2 + xch->chg2;
|
|
||||||
xch = xch->next;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +347,7 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
|
|||||||
xdemitconf_t xecfg = {0};
|
xdemitconf_t xecfg = {0};
|
||||||
xdemitcb_t ecb = {0};
|
xdemitcb_t ecb = {0};
|
||||||
|
|
||||||
xecfg.emit_func = (void(*)(void))my_emit;
|
xecfg.hunk_func = my_emit;
|
||||||
ecb.priv = cb_data;
|
ecb.priv = cb_data;
|
||||||
|
|
||||||
trim_common_tail(&file_a, &file_b, 0);
|
trim_common_tail(&file_a, &file_b, 0);
|
||||||
|
@ -50,16 +50,16 @@ void git_cache_dump_stats(git_cache *cache)
|
|||||||
if (kh_size(cache->map) == 0)
|
if (kh_size(cache->map) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
printf("Cache %p: %d items cached, %d bytes\n",
|
printf("Cache %p: %d items cached, %"PRIdZ" bytes\n",
|
||||||
cache, kh_size(cache->map), (int)cache->used_memory);
|
cache, kh_size(cache->map), cache->used_memory);
|
||||||
|
|
||||||
kh_foreach_value(cache->map, object, {
|
kh_foreach_value(cache->map, object, {
|
||||||
char oid_str[9];
|
char oid_str[9];
|
||||||
printf(" %s%c %s (%d)\n",
|
printf(" %s%c %s (%"PRIuZ")\n",
|
||||||
git_object_type2string(object->type),
|
git_object_type2string(object->type),
|
||||||
object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
|
object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
|
||||||
git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
|
git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
|
||||||
(int)object->size
|
object->size
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1299,8 +1299,8 @@ static int checkout_get_actions(
|
|||||||
if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
|
if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
|
||||||
(data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
|
(data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
|
||||||
{
|
{
|
||||||
giterr_set(GITERR_CHECKOUT, "%d %s checkout",
|
giterr_set(GITERR_CHECKOUT, "%"PRIuZ" %s checkout",
|
||||||
(int)counts[CHECKOUT_ACTION__CONFLICT],
|
counts[CHECKOUT_ACTION__CONFLICT],
|
||||||
counts[CHECKOUT_ACTION__CONFLICT] == 1 ?
|
counts[CHECKOUT_ACTION__CONFLICT] == 1 ?
|
||||||
"conflict prevents" : "conflicts prevent");
|
"conflict prevents" : "conflicts prevent");
|
||||||
error = GIT_ECONFLICT;
|
error = GIT_ECONFLICT;
|
||||||
|
@ -46,6 +46,10 @@
|
|||||||
# ifdef GIT_THREADS
|
# ifdef GIT_THREADS
|
||||||
# include "win32/pthread.h"
|
# include "win32/pthread.h"
|
||||||
# endif
|
# endif
|
||||||
|
# if defined(GIT_MSVC_CRTDBG)
|
||||||
|
# include "win32/w32_stack.h"
|
||||||
|
# include "win32/w32_crtdbg_stacktrace.h"
|
||||||
|
# endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@ -67,9 +67,9 @@ static int curls_certificate(git_cert **out, git_stream *stream)
|
|||||||
|
|
||||||
/* No information is available, can happen with SecureTransport */
|
/* No information is available, can happen with SecureTransport */
|
||||||
if (certinfo->num_of_certs == 0) {
|
if (certinfo->num_of_certs == 0) {
|
||||||
s->cert_info.cert_type = GIT_CERT_NONE;
|
s->cert_info.parent.cert_type = GIT_CERT_NONE;
|
||||||
s->cert_info.data = NULL;
|
s->cert_info.data = NULL;
|
||||||
s->cert_info.len = 0;
|
s->cert_info.len = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,11 +85,11 @@ static int curls_certificate(git_cert **out, git_stream *stream)
|
|||||||
s->cert_info_strings.strings = (char **) strings.contents;
|
s->cert_info_strings.strings = (char **) strings.contents;
|
||||||
s->cert_info_strings.count = strings.length;
|
s->cert_info_strings.count = strings.length;
|
||||||
|
|
||||||
s->cert_info.cert_type = GIT_CERT_STRARRAY;
|
s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
|
||||||
s->cert_info.data = &s->cert_info_strings;
|
s->cert_info.data = &s->cert_info_strings;
|
||||||
s->cert_info.len = strings.length;
|
s->cert_info.len = strings.length;
|
||||||
|
|
||||||
*out = (git_cert *) &s->cert_info;
|
*out = &s->cert_info.parent;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
25
src/diff.c
25
src/diff.c
@ -1421,6 +1421,31 @@ int git_diff_tree_to_workdir_with_index(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_diff_index_to_index(
|
||||||
|
git_diff **diff,
|
||||||
|
git_repository *repo,
|
||||||
|
git_index *old_index,
|
||||||
|
git_index *new_index,
|
||||||
|
const git_diff_options *opts)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
assert(diff && old_index && new_index);
|
||||||
|
|
||||||
|
DIFF_FROM_ITERATORS(
|
||||||
|
git_iterator_for_index(
|
||||||
|
&a, old_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx),
|
||||||
|
git_iterator_for_index(
|
||||||
|
&b, new_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx)
|
||||||
|
);
|
||||||
|
|
||||||
|
/* if index is in case-insensitive order, re-sort deltas to match */
|
||||||
|
if (!error && (old_index->ignore_case || new_index->ignore_case))
|
||||||
|
diff_set_ignore_case(*diff, true);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
size_t git_diff_num_deltas(const git_diff *diff)
|
size_t git_diff_num_deltas(const git_diff *diff)
|
||||||
{
|
{
|
||||||
assert(diff);
|
assert(diff);
|
||||||
|
@ -433,8 +433,11 @@ static int filter_list_check_attributes(
|
|||||||
want_type = git_attr_value(want);
|
want_type = git_attr_value(want);
|
||||||
found_type = git_attr_value(strs[i]);
|
found_type = git_attr_value(strs[i]);
|
||||||
|
|
||||||
if (want_type != found_type ||
|
if (want_type != found_type)
|
||||||
(want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i])))
|
error = GIT_ENOTFOUND;
|
||||||
|
else if (want_type == GIT_ATTR_VALUE_T &&
|
||||||
|
strcmp(want, strs[i]) &&
|
||||||
|
strcmp(want, "*"))
|
||||||
error = GIT_ENOTFOUND;
|
error = GIT_ENOTFOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/global.c
18
src/global.c
@ -11,7 +11,10 @@
|
|||||||
#include "git2/global.h"
|
#include "git2/global.h"
|
||||||
#include "git2/sys/openssl.h"
|
#include "git2/sys/openssl.h"
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
#include "win32/w32_stack.h"
|
||||||
|
#include "win32/w32_crtdbg_stacktrace.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
git_mutex git__mwindow_mutex;
|
git_mutex git__mwindow_mutex;
|
||||||
|
|
||||||
@ -225,6 +228,11 @@ int git_libgit2_init(void)
|
|||||||
|
|
||||||
/* Only do work on a 0 -> 1 transition of the refcount */
|
/* Only do work on a 0 -> 1 transition of the refcount */
|
||||||
if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
|
if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
git_win32__crtdbg_stacktrace_init();
|
||||||
|
git_win32__stack_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (synchronized_threads_init() < 0)
|
if (synchronized_threads_init() < 0)
|
||||||
ret = -1;
|
ret = -1;
|
||||||
}
|
}
|
||||||
@ -254,9 +262,15 @@ int git_libgit2_shutdown(void)
|
|||||||
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
|
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
|
||||||
|
|
||||||
/* Only do work on a 1 -> 0 transition of the refcount */
|
/* Only do work on a 1 -> 0 transition of the refcount */
|
||||||
if ((ret = git_atomic_dec(&git__n_inits)) == 0)
|
if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
|
||||||
synchronized_threads_shutdown();
|
synchronized_threads_shutdown();
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
git_win32__crtdbg_stacktrace_cleanup();
|
||||||
|
git_win32__stack_cleanup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* Exit the lock */
|
/* Exit the lock */
|
||||||
InterlockedExchange(&_mutex, 0);
|
InterlockedExchange(&_mutex, 0);
|
||||||
|
|
||||||
|
@ -1921,8 +1921,8 @@ int git_iterator_walk(
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
git__free(iterator_item);
|
git__free((git_index_entry **)iterator_item);
|
||||||
git__free(cur_items);
|
git__free((git_index_entry **)cur_items);
|
||||||
|
|
||||||
if (error == GIT_ITEROVER)
|
if (error == GIT_ITEROVER)
|
||||||
error = 0;
|
error = 0;
|
||||||
|
@ -619,4 +619,4 @@ typedef const char *kh_cstr_t;
|
|||||||
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||||
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
#endif /* __AC_KHASH_H */
|
#endif /* __AC_KHASH_H */
|
||||||
|
@ -79,7 +79,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito
|
|||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (length < 2) {
|
if (length < 2) {
|
||||||
giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
|
giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co
|
|||||||
assert(out && repo && input_array);
|
assert(out && repo && input_array);
|
||||||
|
|
||||||
if (length < 2) {
|
if (length < 2) {
|
||||||
giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
|
giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2451,7 +2451,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new)
|
|||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if ((conflicts = index_conflicts + wd_conflicts) > 0) {
|
if ((conflicts = index_conflicts + wd_conflicts) > 0) {
|
||||||
giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
|
giterr_set(GITERR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge",
|
||||||
conflicts, (conflicts != 1) ? "s" : "");
|
conflicts, (conflicts != 1) ? "s" : "");
|
||||||
error = GIT_ECONFLICT;
|
error = GIT_ECONFLICT;
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,9 @@ int openssl_connect(git_stream *stream)
|
|||||||
|
|
||||||
SSL_set_bio(st->ssl, bio, bio);
|
SSL_set_bio(st->ssl, bio, bio);
|
||||||
/* specify the host in case SNI is needed */
|
/* specify the host in case SNI is needed */
|
||||||
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||||
SSL_set_tlsext_host_name(st->ssl, st->host);
|
SSL_set_tlsext_host_name(st->ssl, st->host);
|
||||||
|
#endif
|
||||||
|
|
||||||
if ((ret = SSL_connect(st->ssl)) <= 0)
|
if ((ret = SSL_connect(st->ssl)) <= 0)
|
||||||
return ssl_set_error(st->ssl, ret);
|
return ssl_set_error(st->ssl, ret);
|
||||||
@ -358,11 +360,12 @@ int openssl_certificate(git_cert **out, git_stream *stream)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->cert_info.cert_type = GIT_CERT_X509;
|
st->cert_info.parent.cert_type = GIT_CERT_X509;
|
||||||
st->cert_info.data = encoded_cert;
|
st->cert_info.data = encoded_cert;
|
||||||
st->cert_info.len = len;
|
st->cert_info.len = len;
|
||||||
|
|
||||||
*out = (git_cert *)&st->cert_info;
|
*out = &st->cert_info.parent;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ static int rebase_setupfiles_merge(git_rebase *rebase)
|
|||||||
size_t i;
|
size_t i;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
if ((error = rebase_setupfile(rebase, END_FILE, -1, "%d\n", git_array_size(rebase->operations))) < 0 ||
|
if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
|
||||||
(error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0)
|
(error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
@ -789,7 +789,7 @@ static int rebase_next_merge(
|
|||||||
normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
|
normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
|
||||||
|
|
||||||
if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
|
if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
|
||||||
(error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 ||
|
(error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
|
||||||
(error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
|
(error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
|
||||||
(error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 ||
|
(error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 ||
|
||||||
(error = git_merge__check_result(rebase->repo, index)) < 0 ||
|
(error = git_merge__check_result(rebase->repo, index)) < 0 ||
|
||||||
|
@ -867,7 +867,9 @@ static int reserved_names_add8dot3(git_repository *repo, const char *path)
|
|||||||
{
|
{
|
||||||
char *name = git_win32_path_8dot3_name(path);
|
char *name = git_win32_path_8dot3_name(path);
|
||||||
const char *def = GIT_DIR_SHORTNAME;
|
const char *def = GIT_DIR_SHORTNAME;
|
||||||
|
const char *def_dot_git = DOT_GIT;
|
||||||
size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
|
size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
|
||||||
|
size_t def_dot_git_len = CONST_STRLEN(DOT_GIT);
|
||||||
git_buf *buf;
|
git_buf *buf;
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
@ -875,7 +877,8 @@ static int reserved_names_add8dot3(git_repository *repo, const char *path)
|
|||||||
|
|
||||||
name_len = strlen(name);
|
name_len = strlen(name);
|
||||||
|
|
||||||
if (name_len == def_len && memcmp(name, def, def_len) == 0) {
|
if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
|
||||||
|
(name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) {
|
||||||
git__free(name);
|
git__free(name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -770,7 +770,7 @@ static int ensure_clean_index(git_repository *repo, git_index *index)
|
|||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (git_diff_num_deltas(index_diff) > 0) {
|
if (git_diff_num_deltas(index_diff) > 0) {
|
||||||
giterr_set(GITERR_STASH, "%d uncommitted changes exist in the index",
|
giterr_set(GITERR_STASH, "%" PRIuZ " uncommitted changes exist in the index",
|
||||||
git_diff_num_deltas(index_diff));
|
git_diff_num_deltas(index_diff));
|
||||||
error = GIT_EUNCOMMITTED;
|
error = GIT_EUNCOMMITTED;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,7 @@ int stransport_certificate(git_cert **out, git_stream *stream)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
st->cert_info.cert_type = GIT_CERT_X509;
|
st->cert_info.parent.cert_type = GIT_CERT_X509;
|
||||||
st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
|
st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
|
||||||
st->cert_info.len = CFDataGetLength(st->der_data);
|
st->cert_info.len = CFDataGetLength(st->der_data);
|
||||||
|
|
||||||
|
@ -1385,7 +1385,7 @@ int git_submodule_reload(git_submodule *sm, int force)
|
|||||||
|
|
||||||
git_buf_sets(&path, "submodule\\.");
|
git_buf_sets(&path, "submodule\\.");
|
||||||
git_buf_text_puts_escape_regex(&path, sm->name);
|
git_buf_text_puts_escape_regex(&path, sm->name);
|
||||||
git_buf_puts(&path, ".*");
|
git_buf_puts(&path, "\\..*");
|
||||||
|
|
||||||
if (git_buf_oom(&path)) {
|
if (git_buf_oom(&path)) {
|
||||||
error = -1;
|
error = -1;
|
||||||
@ -1647,7 +1647,7 @@ static int submodule_load_from_config(
|
|||||||
} else {
|
} else {
|
||||||
khiter_t pos;
|
khiter_t pos;
|
||||||
git_strmap *map = data->map;
|
git_strmap *map = data->map;
|
||||||
pos = git_strmap_lookup_index(map, name.ptr);
|
pos = git_strmap_lookup_index(map, path ? path : name.ptr);
|
||||||
if (git_strmap_valid_index(map, pos)) {
|
if (git_strmap_valid_index(map, pos)) {
|
||||||
sm = git_strmap_value_at(map, pos);
|
sm = git_strmap_value_at(map, pos);
|
||||||
} else {
|
} else {
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#elif defined(hpux) || defined(__hpux) || defined(_hpux)
|
#elif defined(hpux) || defined(__hpux) || defined(_hpux)
|
||||||
# include <sys/pstat.h>
|
# include <sys/pstat.h>
|
||||||
|
@ -511,7 +511,7 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
|
|||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
|
|
||||||
/* Chunk header */
|
/* Chunk header */
|
||||||
git_buf_printf(&buf, "%X\r\n", (unsigned)len);
|
git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
|
||||||
|
|
||||||
if (git_buf_oom(&buf))
|
if (git_buf_oom(&buf))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -523,7 +523,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
|
|||||||
|
|
||||||
if (len > 0xffff) {
|
if (len > 0xffff) {
|
||||||
giterr_set(GITERR_NET,
|
giterr_set(GITERR_NET,
|
||||||
"Tried to produce packet with invalid length %d", len);
|
"Tried to produce packet with invalid length %" PRIuZ, len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,10 +525,10 @@ static int _git_ssh_setup_conn(
|
|||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (t->owner->certificate_check_cb != NULL) {
|
if (t->owner->certificate_check_cb != NULL) {
|
||||||
git_cert_hostkey cert = { 0 }, *cert_ptr;
|
git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
|
||||||
const char *key;
|
const char *key;
|
||||||
|
|
||||||
cert.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
|
cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
|
||||||
|
|
||||||
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
||||||
if (key != NULL) {
|
if (key != NULL) {
|
||||||
|
@ -228,7 +228,7 @@ static int certificate_check(winhttp_stream *s, int valid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
cert.cert_type = GIT_CERT_X509;
|
cert.parent.cert_type = GIT_CERT_X509;
|
||||||
cert.data = cert_ctx->pbCertEncoded;
|
cert.data = cert_ctx->pbCertEncoded;
|
||||||
cert.len = cert_ctx->cbCertEncoded;
|
cert.len = cert_ctx->cbCertEncoded;
|
||||||
error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload);
|
error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload);
|
||||||
|
13
src/util.h
13
src/util.h
@ -38,6 +38,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <crtdbg.h>
|
#include <crtdbg.h>
|
||||||
|
#include "win32/w32_crtdbg_stacktrace.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -65,23 +66,24 @@
|
|||||||
#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1)
|
#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1)
|
||||||
|
|
||||||
#if defined(GIT_MSVC_CRTDBG)
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
|
||||||
GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
|
GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
|
||||||
{
|
{
|
||||||
void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, file, line);
|
void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
|
||||||
if (!ptr) giterr_set_oom();
|
if (!ptr) giterr_set_oom();
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
|
GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
|
||||||
{
|
{
|
||||||
void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, file, line);
|
void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
|
||||||
if (!ptr) giterr_set_oom();
|
if (!ptr) giterr_set_oom();
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
|
GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
|
||||||
{
|
{
|
||||||
char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, file, line);
|
char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
|
||||||
if (!ptr) giterr_set_oom();
|
if (!ptr) giterr_set_oom();
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
@ -121,7 +123,7 @@ GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const cha
|
|||||||
|
|
||||||
GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
|
GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
|
||||||
{
|
{
|
||||||
void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, file, line);
|
void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
|
||||||
if (!new_ptr) giterr_set_oom();
|
if (!new_ptr) giterr_set_oom();
|
||||||
return new_ptr;
|
return new_ptr;
|
||||||
}
|
}
|
||||||
@ -129,8 +131,9 @@ GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file
|
|||||||
GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
|
GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
|
||||||
{
|
{
|
||||||
size_t newsize;
|
size_t newsize;
|
||||||
|
|
||||||
return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
|
return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
|
||||||
NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, file, line);
|
NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
|
GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
|
||||||
|
343
src/win32/w32_crtdbg_stacktrace.c
Normal file
343
src/win32/w32_crtdbg_stacktrace.c
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
#include "w32_stack.h"
|
||||||
|
#include "w32_crtdbg_stacktrace.h"
|
||||||
|
|
||||||
|
#define CRTDBG_STACKTRACE__UID_LEN (15)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The stacktrace of an allocation can be distilled
|
||||||
|
* to a unique id based upon the stackframe pointers
|
||||||
|
* and ignoring any size arguments. We will use these
|
||||||
|
* UIDs as the (char const*) __FILE__ argument we
|
||||||
|
* give to the CRT malloc routines.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
char uid[CRTDBG_STACKTRACE__UID_LEN + 1];
|
||||||
|
} git_win32__crtdbg_stacktrace__uid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All mallocs with the same stacktrace will be de-duped
|
||||||
|
* and aggregated into this row.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
git_win32__crtdbg_stacktrace__uid uid; /* must be first */
|
||||||
|
git_win32__stack__raw_data raw_data;
|
||||||
|
unsigned int count_allocs; /* times this alloc signature seen since init */
|
||||||
|
unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
|
||||||
|
unsigned int transient_count_leaks; /* sum of leaks */
|
||||||
|
} git_win32__crtdbg_stacktrace__row;
|
||||||
|
|
||||||
|
static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRTDBG memory leak tracking takes a "char const * const file_name"
|
||||||
|
* and stores the pointer in the heap data (instead of allocing a copy
|
||||||
|
* for itself). Normally, this is not a problem, since we usually pass
|
||||||
|
* in __FILE__. But I'm going to lie to it and pass in the address of
|
||||||
|
* the UID in place of the file_name. Also, I do not want to alloc the
|
||||||
|
* stacktrace data (because we are called from inside our alloc routines).
|
||||||
|
* Therefore, I'm creating a very large static pool array to store row
|
||||||
|
* data. This also eliminates the temptation to realloc it (and move the
|
||||||
|
* UID pointers).
|
||||||
|
*
|
||||||
|
* And to efficiently look for duplicates we need an index on the rows
|
||||||
|
* so we can bsearch it. Again, without mallocing.
|
||||||
|
*
|
||||||
|
* If we observe more than MY_ROW_LIMIT unique malloc signatures, we
|
||||||
|
* fall through and use the traditional __FILE__ processing and don't
|
||||||
|
* try to de-dup them. If your testing hits this limit, just increase
|
||||||
|
* it and try again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MY_ROW_LIMIT (1024 * 1024)
|
||||||
|
static git_win32__crtdbg_stacktrace__row g_cs_rows[MY_ROW_LIMIT];
|
||||||
|
static git_win32__crtdbg_stacktrace__row *g_cs_index[MY_ROW_LIMIT];
|
||||||
|
|
||||||
|
static unsigned int g_cs_end = MY_ROW_LIMIT;
|
||||||
|
static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
|
||||||
|
static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
|
||||||
|
static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
|
||||||
|
static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
|
||||||
|
static bool g_limit_reached = false; /* had allocs after we filled row table */
|
||||||
|
|
||||||
|
static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
|
||||||
|
static bool g_transient_leaks_since_mark = false; /* payload for hook */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare function for bsearch on g_cs_index table.
|
||||||
|
*/
|
||||||
|
static int row_cmp(const void *v1, const void *v2)
|
||||||
|
{
|
||||||
|
git_win32__stack__raw_data *d1 = (git_win32__stack__raw_data*)v1;
|
||||||
|
git_win32__crtdbg_stacktrace__row *r2 = (git_win32__crtdbg_stacktrace__row *)v2;
|
||||||
|
|
||||||
|
return (git_win32__stack_compare(d1, &r2->raw_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique insert the new data into the row and index tables.
|
||||||
|
* We have to sort by the stackframe data itself, not the uid.
|
||||||
|
*/
|
||||||
|
static git_win32__crtdbg_stacktrace__row * insert_unique(
|
||||||
|
const git_win32__stack__raw_data *pdata)
|
||||||
|
{
|
||||||
|
size_t pos;
|
||||||
|
if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
|
||||||
|
/* Append new unique item to row table. */
|
||||||
|
memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
|
||||||
|
sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
|
||||||
|
|
||||||
|
/* Insert pointer to it into the proper place in the index table. */
|
||||||
|
if (pos < g_cs_ins)
|
||||||
|
memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
|
||||||
|
g_cs_index[pos] = &g_cs_rows[g_cs_ins];
|
||||||
|
|
||||||
|
g_cs_ins++;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_cs_index[pos]->count_allocs++;
|
||||||
|
|
||||||
|
return g_cs_index[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook function to receive leak data from the CRT. (This includes
|
||||||
|
* both "<file_name>:(<line_number>)" data, but also each of the
|
||||||
|
* various headers and fields.
|
||||||
|
*
|
||||||
|
* Scan this for the special "##<pos>" UID forms that we substituted
|
||||||
|
* for the "<file_name>". Map <pos> back to the row data and
|
||||||
|
* increment its leak count.
|
||||||
|
*
|
||||||
|
* See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
|
||||||
|
*
|
||||||
|
* We suppress the actual crtdbg output.
|
||||||
|
*/
|
||||||
|
static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
|
||||||
|
{
|
||||||
|
static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
|
||||||
|
unsigned int pos;
|
||||||
|
|
||||||
|
*retVal = 0; /* do not invoke debugger */
|
||||||
|
|
||||||
|
if ((szMsg[0] != '#') || (szMsg[1] != '#'))
|
||||||
|
return hook_result;
|
||||||
|
|
||||||
|
if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
|
||||||
|
return hook_result;
|
||||||
|
if (pos >= g_cs_ins)
|
||||||
|
return hook_result;
|
||||||
|
|
||||||
|
if (g_transient_leaks_since_mark) {
|
||||||
|
if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
|
||||||
|
return hook_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_cs_rows[pos].transient_count_leaks++;
|
||||||
|
|
||||||
|
if (g_cs_rows[pos].transient_count_leaks == 1)
|
||||||
|
g_transient_count_dedup_leaks++;
|
||||||
|
|
||||||
|
g_transient_count_total_leaks++;
|
||||||
|
|
||||||
|
return hook_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write leak data to all of the various places we need.
|
||||||
|
* We force the caller to sprintf() the message first
|
||||||
|
* because we want to avoid fprintf() because it allocs.
|
||||||
|
*/
|
||||||
|
static void my_output(const char *buf)
|
||||||
|
{
|
||||||
|
fwrite(buf, strlen(buf), 1, stderr);
|
||||||
|
OutputDebugString(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each row with leaks, dump a stacktrace for it.
|
||||||
|
*/
|
||||||
|
static void dump_summary(const char *label)
|
||||||
|
{
|
||||||
|
unsigned int k;
|
||||||
|
char buf[10 * 1024];
|
||||||
|
|
||||||
|
if (g_transient_count_total_leaks == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
my_output("\n");
|
||||||
|
|
||||||
|
if (g_limit_reached) {
|
||||||
|
sprintf(buf,
|
||||||
|
"LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
|
||||||
|
MY_ROW_LIMIT);
|
||||||
|
my_output(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!label)
|
||||||
|
label = "";
|
||||||
|
|
||||||
|
if (g_transient_leaks_since_mark) {
|
||||||
|
sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
|
||||||
|
g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
|
||||||
|
my_output(buf);
|
||||||
|
} else {
|
||||||
|
sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
|
||||||
|
g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
|
||||||
|
my_output(buf);
|
||||||
|
}
|
||||||
|
my_output("\n");
|
||||||
|
|
||||||
|
for (k = 0; k < g_cs_ins; k++) {
|
||||||
|
if (g_cs_rows[k].transient_count_leaks > 0) {
|
||||||
|
sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
|
||||||
|
g_cs_rows[k].uid.uid,
|
||||||
|
g_cs_rows[k].transient_count_leaks,
|
||||||
|
g_cs_rows[k].count_allocs);
|
||||||
|
my_output(buf);
|
||||||
|
|
||||||
|
if (git_win32__stack_format(
|
||||||
|
buf, sizeof(buf), &g_cs_rows[k].raw_data,
|
||||||
|
NULL, NULL) >= 0) {
|
||||||
|
my_output(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
my_output("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_win32__crtdbg_stacktrace_init(void)
|
||||||
|
{
|
||||||
|
InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
|
||||||
|
EnterCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
|
||||||
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||||
|
|
||||||
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
||||||
|
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
||||||
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
||||||
|
|
||||||
|
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
|
||||||
|
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||||
|
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
|
||||||
|
|
||||||
|
LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_win32__crtdbg_stacktrace__dump(
|
||||||
|
git_win32__crtdbg_stacktrace_options opt,
|
||||||
|
const char *label)
|
||||||
|
{
|
||||||
|
_CRT_REPORT_HOOK old;
|
||||||
|
unsigned int k;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
|
||||||
|
|
||||||
|
bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK);
|
||||||
|
bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK);
|
||||||
|
bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL);
|
||||||
|
bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET);
|
||||||
|
|
||||||
|
if (b_leaks_since_mark && b_leaks_total) {
|
||||||
|
giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
|
||||||
|
return GIT_ERROR;
|
||||||
|
}
|
||||||
|
if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
|
||||||
|
giterr_set(GITERR_INVALID, "Nothing to do.");
|
||||||
|
return GIT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
|
||||||
|
if (b_leaks_since_mark || b_leaks_total) {
|
||||||
|
/* All variables with "transient" in the name are per-dump counters
|
||||||
|
* and reset before each dump. This lets us handle checkpoints.
|
||||||
|
*/
|
||||||
|
g_transient_count_total_leaks = 0;
|
||||||
|
g_transient_count_dedup_leaks = 0;
|
||||||
|
for (k = 0; k < g_cs_ins; k++) {
|
||||||
|
g_cs_rows[k].transient_count_leaks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_transient_leaks_since_mark = b_leaks_since_mark;
|
||||||
|
|
||||||
|
old = _CrtSetReportHook(report_hook);
|
||||||
|
_CrtDumpMemoryLeaks();
|
||||||
|
_CrtSetReportHook(old);
|
||||||
|
|
||||||
|
if (b_leaks_since_mark || b_leaks_total) {
|
||||||
|
r = g_transient_count_dedup_leaks;
|
||||||
|
|
||||||
|
if (!b_quiet)
|
||||||
|
dump_summary(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b_set_mark) {
|
||||||
|
for (k = 0; k < g_cs_ins; k++) {
|
||||||
|
g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_checkpoint_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_win32__crtdbg_stacktrace_cleanup(void)
|
||||||
|
{
|
||||||
|
/* At shutdown/cleanup, dump cummulative leak info
|
||||||
|
* with everything since startup. This might generate
|
||||||
|
* extra noise if the caller has been doing checkpoint
|
||||||
|
* dumps, but it might also eliminate some false
|
||||||
|
* positives for resources previously reported during
|
||||||
|
* checkpoints.
|
||||||
|
*/
|
||||||
|
git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
|
||||||
|
"CLEANUP");
|
||||||
|
|
||||||
|
DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *git_win32__crtdbg_stacktrace(int skip, const char *file)
|
||||||
|
{
|
||||||
|
git_win32__stack__raw_data new_data;
|
||||||
|
git_win32__crtdbg_stacktrace__row *row;
|
||||||
|
const char * result = file;
|
||||||
|
|
||||||
|
if (git_win32__stack_capture(&new_data, skip+1) < 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
EnterCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
|
||||||
|
if (g_cs_ins < g_cs_end) {
|
||||||
|
row = insert_unique(&new_data);
|
||||||
|
result = row->uid.uid;
|
||||||
|
} else {
|
||||||
|
g_limit_reached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_count_total_allocs++;
|
||||||
|
|
||||||
|
LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
93
src/win32/w32_crtdbg_stacktrace.h
Normal file
93
src/win32/w32_crtdbg_stacktrace.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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_w32_crtdbg_stacktrace_h__
|
||||||
|
#define INCLUDE_w32_crtdbg_stacktrace_h__
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize our memory leak tracking and de-dup data structures.
|
||||||
|
* This should ONLY be called by git_libgit2_init().
|
||||||
|
*/
|
||||||
|
void git_win32__crtdbg_stacktrace_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown our memory leak tracking and dump summary data.
|
||||||
|
* This should ONLY be called by git_libgit2_shutdown().
|
||||||
|
*
|
||||||
|
* We explicitly call _CrtDumpMemoryLeaks() during here so
|
||||||
|
* that we can compute summary data for the leaks. We print
|
||||||
|
* the stacktrace of each unique leak.
|
||||||
|
*
|
||||||
|
* This cleanup does not happen if the app calls exit()
|
||||||
|
* without calling the libgit2 shutdown code.
|
||||||
|
*
|
||||||
|
* This info we print here is independent of any automatic
|
||||||
|
* reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF.
|
||||||
|
* Set it in your app if you also want traditional reporting.
|
||||||
|
*/
|
||||||
|
void git_win32__crtdbg_stacktrace_cleanup(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkpoint options.
|
||||||
|
*/
|
||||||
|
typedef enum git_win32__crtdbg_stacktrace_options {
|
||||||
|
/**
|
||||||
|
* Set checkpoint marker.
|
||||||
|
*/
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK = (1 << 0),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump leaks since last checkpoint marker.
|
||||||
|
* May not be combined with __LEAKS_TOTAL.
|
||||||
|
*
|
||||||
|
* Note that this may generate false positives for global TLS
|
||||||
|
* error state and other global caches that aren't cleaned up
|
||||||
|
* until the thread/process terminates. So when using this
|
||||||
|
* around a region of interest, also check the final (at exit)
|
||||||
|
* dump before digging into leaks reported here.
|
||||||
|
*/
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK = (1 << 1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump leaks since init. May not be combined
|
||||||
|
* with __LEAKS_SINCE_MARK.
|
||||||
|
*/
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL = (1 << 2),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suppress printing during dumps.
|
||||||
|
* Just return leak count.
|
||||||
|
*/
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET = (1 << 3),
|
||||||
|
|
||||||
|
} git_win32__crtdbg_stacktrace_options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checkpoint memory state and/or dump unique stack traces of
|
||||||
|
* current memory leaks.
|
||||||
|
*
|
||||||
|
* @return number of unique leaks (relative to requested starting
|
||||||
|
* point) or error.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump(
|
||||||
|
git_win32__crtdbg_stacktrace_options opt,
|
||||||
|
const char *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct stacktrace and append it to the global buffer.
|
||||||
|
* Return pointer to start of this string. On any error or
|
||||||
|
* lack of buffer space, just return the given file buffer
|
||||||
|
* so it will behave as usual.
|
||||||
|
*
|
||||||
|
* This should ONLY be called by our internal memory allocations
|
||||||
|
* routines.
|
||||||
|
*/
|
||||||
|
const char *git_win32__crtdbg_stacktrace(int skip, const char *file);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
192
src/win32/w32_stack.c
Normal file
192
src/win32/w32_stack.c
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
#include "Windows.h"
|
||||||
|
#include "Dbghelp.h"
|
||||||
|
#include "win32/posix.h"
|
||||||
|
#include "w32_stack.h"
|
||||||
|
#include "hash.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues.
|
||||||
|
*/
|
||||||
|
USHORT WINAPI RtlCaptureStackBackTrace(ULONG, ULONG, PVOID*, PULONG);
|
||||||
|
|
||||||
|
static bool g_win32_stack_initialized = false;
|
||||||
|
static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
|
||||||
|
static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL;
|
||||||
|
static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL;
|
||||||
|
|
||||||
|
int git_win32__stack__set_aux_cb(
|
||||||
|
git_win32__stack__aux_cb_alloc cb_alloc,
|
||||||
|
git_win32__stack__aux_cb_lookup cb_lookup)
|
||||||
|
{
|
||||||
|
g_aux_cb_alloc = cb_alloc;
|
||||||
|
g_aux_cb_lookup = cb_lookup;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_win32__stack_init(void)
|
||||||
|
{
|
||||||
|
if (!g_win32_stack_initialized) {
|
||||||
|
g_win32_stack_process = GetCurrentProcess();
|
||||||
|
SymSetOptions(SYMOPT_LOAD_LINES);
|
||||||
|
SymInitialize(g_win32_stack_process, NULL, TRUE);
|
||||||
|
g_win32_stack_initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_win32__stack_cleanup(void)
|
||||||
|
{
|
||||||
|
if (g_win32_stack_initialized) {
|
||||||
|
SymCleanup(g_win32_stack_process);
|
||||||
|
g_win32_stack_process = INVALID_HANDLE_VALUE;
|
||||||
|
g_win32_stack_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip)
|
||||||
|
{
|
||||||
|
if (!g_win32_stack_initialized) {
|
||||||
|
giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
|
||||||
|
return GIT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(pdata, 0, sizeof(*pdata));
|
||||||
|
pdata->nr_frames = RtlCaptureStackBackTrace(
|
||||||
|
skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL);
|
||||||
|
|
||||||
|
/* If an "aux" data provider was registered, ask it to capture
|
||||||
|
* whatever data it needs and give us an "aux_id" to it so that
|
||||||
|
* we can refer to it later when reporting.
|
||||||
|
*/
|
||||||
|
if (g_aux_cb_alloc)
|
||||||
|
(g_aux_cb_alloc)(&pdata->aux_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_win32__stack_compare(
|
||||||
|
git_win32__stack__raw_data *d1,
|
||||||
|
git_win32__stack__raw_data *d2)
|
||||||
|
{
|
||||||
|
return memcmp(d1, d2, sizeof(*d1));
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_win32__stack_format(
|
||||||
|
char *pbuf, int buf_len,
|
||||||
|
const git_win32__stack__raw_data *pdata,
|
||||||
|
const char *prefix, const char *suffix)
|
||||||
|
{
|
||||||
|
#define MY_MAX_FILENAME 255
|
||||||
|
|
||||||
|
/* SYMBOL_INFO has char FileName[1] at the end. The docs say to
|
||||||
|
* to malloc it with extra space for your desired max filename.
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
SYMBOL_INFO symbol;
|
||||||
|
char extra[MY_MAX_FILENAME + 1];
|
||||||
|
} s;
|
||||||
|
|
||||||
|
IMAGEHLP_LINE64 line;
|
||||||
|
int buf_used = 0;
|
||||||
|
unsigned int k;
|
||||||
|
char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
|
||||||
|
int detail_len;
|
||||||
|
|
||||||
|
if (!g_win32_stack_initialized) {
|
||||||
|
giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
|
||||||
|
return GIT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prefix)
|
||||||
|
prefix = "\t";
|
||||||
|
if (!suffix)
|
||||||
|
suffix = "\n";
|
||||||
|
|
||||||
|
memset(pbuf, 0, buf_len);
|
||||||
|
|
||||||
|
memset(&s, 0, sizeof(s));
|
||||||
|
s.symbol.MaxNameLen = MY_MAX_FILENAME;
|
||||||
|
s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||||
|
|
||||||
|
memset(&line, 0, sizeof(line));
|
||||||
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
|
|
||||||
|
for (k=0; k < pdata->nr_frames; k++) {
|
||||||
|
DWORD64 frame_k = (DWORD64)pdata->frames[k];
|
||||||
|
DWORD dwUnused;
|
||||||
|
|
||||||
|
if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
|
||||||
|
SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
|
||||||
|
const char *pslash;
|
||||||
|
const char *pfile;
|
||||||
|
|
||||||
|
pslash = strrchr(line.FileName, '\\');
|
||||||
|
pfile = ((pslash) ? (pslash+1) : line.FileName);
|
||||||
|
p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
|
||||||
|
prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
|
||||||
|
} else {
|
||||||
|
/* This happens when we cross into another module.
|
||||||
|
* For example, in CLAR tests, this is typically
|
||||||
|
* the CRT startup code. Just print an unknown
|
||||||
|
* frame and continue.
|
||||||
|
*/
|
||||||
|
p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
|
||||||
|
}
|
||||||
|
detail_len = strlen(detail);
|
||||||
|
|
||||||
|
if (buf_len < (buf_used + detail_len + 1)) {
|
||||||
|
/* we don't have room for this frame in the buffer, so just stop. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&pbuf[buf_used], detail, detail_len);
|
||||||
|
buf_used += detail_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
|
||||||
|
* allocs that occur before the aux callbacks were registered.
|
||||||
|
*/
|
||||||
|
if (pdata->aux_id > 0) {
|
||||||
|
p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
|
||||||
|
prefix, pdata->aux_id, suffix);
|
||||||
|
detail_len = strlen(detail);
|
||||||
|
if ((buf_used + detail_len + 1) < buf_len) {
|
||||||
|
memcpy(&pbuf[buf_used], detail, detail_len);
|
||||||
|
buf_used += detail_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If an "aux" data provider is still registered, ask it to append its detailed
|
||||||
|
* data to the end of ours using the "aux_id" it gave us when this de-duped
|
||||||
|
* item was created.
|
||||||
|
*/
|
||||||
|
if (g_aux_cb_lookup)
|
||||||
|
(g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_win32__stack(
|
||||||
|
char * pbuf, int buf_len,
|
||||||
|
int skip,
|
||||||
|
const char *prefix, const char *suffix)
|
||||||
|
{
|
||||||
|
git_win32__stack__raw_data data;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if ((error = git_win32__stack_capture(&data, skip)) < 0)
|
||||||
|
return error;
|
||||||
|
if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
|
||||||
|
return error;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
138
src/win32/w32_stack.h
Normal file
138
src/win32/w32_stack.h
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 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_w32_stack_h__
|
||||||
|
#define INCLUDE_w32_stack_h__
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type defines a callback to be used to augment a C stacktrace
|
||||||
|
* with "aux" data. This can be used, for example, to allow LibGit2Sharp
|
||||||
|
* (or other interpreted consumer libraries) to give us C# stacktrace
|
||||||
|
* data for the PInvoke.
|
||||||
|
*
|
||||||
|
* This callback will be called during crtdbg-instrumented allocs.
|
||||||
|
*
|
||||||
|
* @param aux_id [out] A returned "aux_id" representing a unique
|
||||||
|
* (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved
|
||||||
|
* to mean no aux stacktrace data.
|
||||||
|
*/
|
||||||
|
typedef void (*git_win32__stack__aux_cb_alloc)(unsigned int *aux_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This type defines a callback to be used to augment the output of
|
||||||
|
* a stacktrace. This will be used to request the C# layer format
|
||||||
|
* the C# stacktrace associated with "aux_id" into the provided
|
||||||
|
* buffer.
|
||||||
|
*
|
||||||
|
* This callback will be called during leak reporting.
|
||||||
|
*
|
||||||
|
* @param aux_id The "aux_id" key associated with a stacktrace.
|
||||||
|
* @param aux_msg A buffer where a formatted message should be written.
|
||||||
|
* @param aux_msg_len The size of the buffer.
|
||||||
|
*/
|
||||||
|
typedef void (*git_win32__stack__aux_cb_lookup)(unsigned int aux_id, char *aux_msg, unsigned int aux_msg_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an "aux" data provider to augment our C stacktrace data.
|
||||||
|
*
|
||||||
|
* This can be used, for example, to allow LibGit2Sharp (or other
|
||||||
|
* interpreted consumer libraries) to give us the C# stacktrace of
|
||||||
|
* the PInvoke.
|
||||||
|
*
|
||||||
|
* If you choose to use this feature, it should be registered during
|
||||||
|
* initialization and not changed for the duration of the process.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_win32__stack__set_aux_cb(
|
||||||
|
git_win32__stack__aux_cb_alloc cb_alloc,
|
||||||
|
git_win32__stack__aux_cb_lookup cb_lookup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of stackframes to record for a
|
||||||
|
* single stacktrace.
|
||||||
|
*/
|
||||||
|
#define GIT_WIN32__STACK__MAX_FRAMES 30
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper containing the raw unprocessed stackframe
|
||||||
|
* data for a single stacktrace and any "aux_id".
|
||||||
|
*
|
||||||
|
* I put the aux_id first so leaks will be sorted by it.
|
||||||
|
* So, for example, if a specific callstack in C# leaks
|
||||||
|
* a repo handle, all of the pointers within the associated
|
||||||
|
* repo pointer will be grouped together.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned int aux_id;
|
||||||
|
unsigned int nr_frames;
|
||||||
|
void *frames[GIT_WIN32__STACK__MAX_FRAMES];
|
||||||
|
} git_win32__stack__raw_data;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load symbol table data. This should be done in the primary
|
||||||
|
* thread at startup (under a lock if there are other threads
|
||||||
|
* active).
|
||||||
|
*/
|
||||||
|
void git_win32__stack_init(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup symbol table data. This should be done in the
|
||||||
|
* primary thead at shutdown (under a lock if there are other
|
||||||
|
* threads active).
|
||||||
|
*/
|
||||||
|
void git_win32__stack_cleanup(void);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capture raw stack trace data for the current process/thread.
|
||||||
|
*
|
||||||
|
* @param skip Number of initial frames to skip. Pass 0 to
|
||||||
|
* begin with the caller of this routine. Pass 1 to begin
|
||||||
|
* with its caller. And so on.
|
||||||
|
*/
|
||||||
|
int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare 2 raw stacktraces with the usual -1,0,+1 result.
|
||||||
|
* This includes any "aux_id" values in the comparison, so that
|
||||||
|
* our de-dup is also "aux" context relative.
|
||||||
|
*/
|
||||||
|
int git_win32__stack_compare(
|
||||||
|
git_win32__stack__raw_data *d1,
|
||||||
|
git_win32__stack__raw_data *d2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format raw stacktrace data into buffer WITHOUT using any mallocs.
|
||||||
|
*
|
||||||
|
* @param prefix String written before each frame; defaults to "\t".
|
||||||
|
* @param suffix String written after each frame; defaults to "\n".
|
||||||
|
*/
|
||||||
|
int git_win32__stack_format(
|
||||||
|
char *pbuf, int buf_len,
|
||||||
|
const git_win32__stack__raw_data *pdata,
|
||||||
|
const char *prefix, const char *suffix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience routine to capture and format stacktrace into
|
||||||
|
* a buffer WITHOUT using any mallocs. This is primarily a
|
||||||
|
* wrapper for testing.
|
||||||
|
*
|
||||||
|
* @param skip Number of initial frames to skip. Pass 0 to
|
||||||
|
* begin with the caller of this routine. Pass 1 to begin
|
||||||
|
* with its caller. And so on.
|
||||||
|
* @param prefix String written before each frame; defaults to "\t".
|
||||||
|
* @param suffix String written after each frame; defaults to "\n".
|
||||||
|
*/
|
||||||
|
int git_win32__stack(
|
||||||
|
char * pbuf, int buf_len,
|
||||||
|
int skip,
|
||||||
|
const char *prefix, const char *suffix);
|
||||||
|
|
||||||
|
#endif /* GIT_MSVC_CRTDBG */
|
||||||
|
#endif /* INCLUDE_w32_stack_h__ */
|
@ -32,14 +32,14 @@ extern "C" {
|
|||||||
#define XDF_IGNORE_WHITESPACE (1 << 2)
|
#define XDF_IGNORE_WHITESPACE (1 << 2)
|
||||||
#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
|
#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
|
||||||
#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
|
#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
|
||||||
#define XDF_PATIENCE_DIFF (1 << 5)
|
|
||||||
#define XDF_HISTOGRAM_DIFF (1 << 6)
|
|
||||||
#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
|
#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
|
||||||
|
|
||||||
#define XDL_PATCH_NORMAL '-'
|
#define XDF_PATIENCE_DIFF (1 << 5)
|
||||||
#define XDL_PATCH_REVERSE '+'
|
#define XDF_HISTOGRAM_DIFF (1 << 6)
|
||||||
#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
|
#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
|
||||||
#define XDL_PATCH_IGNOREBSPACE (1 << 8)
|
#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
|
||||||
|
|
||||||
|
#define XDF_IGNORE_BLANK_LINES (1 << 7)
|
||||||
|
|
||||||
#define XDL_EMIT_FUNCNAMES (1 << 0)
|
#define XDL_EMIT_FUNCNAMES (1 << 0)
|
||||||
#define XDL_EMIT_COMMON (1 << 1)
|
#define XDL_EMIT_COMMON (1 << 1)
|
||||||
@ -88,13 +88,17 @@ typedef struct s_xdemitcb {
|
|||||||
|
|
||||||
typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
|
typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
|
||||||
|
|
||||||
|
typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
|
||||||
|
long start_b, long count_b,
|
||||||
|
void *cb_data);
|
||||||
|
|
||||||
typedef struct s_xdemitconf {
|
typedef struct s_xdemitconf {
|
||||||
long ctxlen;
|
long ctxlen;
|
||||||
long interhunkctxlen;
|
long interhunkctxlen;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
find_func_t find_func;
|
find_func_t find_func;
|
||||||
void *find_func_priv;
|
void *find_func_priv;
|
||||||
void (*emit_func)(void);
|
xdl_emit_hunk_consume_func_t hunk_func;
|
||||||
} xdemitconf_t;
|
} xdemitconf_t;
|
||||||
|
|
||||||
typedef struct s_bdiffparam {
|
typedef struct s_bdiffparam {
|
||||||
|
@ -328,10 +328,10 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
|
|||||||
xdalgoenv_t xenv;
|
xdalgoenv_t xenv;
|
||||||
diffdata_t dd1, dd2;
|
diffdata_t dd1, dd2;
|
||||||
|
|
||||||
if (xpp->flags & XDF_PATIENCE_DIFF)
|
if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
|
||||||
return xdl_do_patience_diff(mf1, mf2, xpp, xe);
|
return xdl_do_patience_diff(mf1, mf2, xpp, xe);
|
||||||
|
|
||||||
if (xpp->flags & XDF_HISTOGRAM_DIFF)
|
if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
|
||||||
return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
|
return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
|
||||||
|
|
||||||
if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
|
if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
|
||||||
@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
|
|||||||
xch->i2 = i2;
|
xch->i2 = i2;
|
||||||
xch->chg1 = chg1;
|
xch->chg1 = chg1;
|
||||||
xch->chg2 = chg2;
|
xch->chg2 = chg2;
|
||||||
|
xch->ignore = 0;
|
||||||
|
|
||||||
return xch;
|
return xch;
|
||||||
}
|
}
|
||||||
@ -538,13 +539,51 @@ void xdl_free_script(xdchange_t *xscr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
||||||
|
xdemitconf_t const *xecfg)
|
||||||
|
{
|
||||||
|
xdchange_t *xch, *xche;
|
||||||
|
|
||||||
|
(void)xe;
|
||||||
|
|
||||||
|
for (xch = xscr; xch; xch = xche->next) {
|
||||||
|
xche = xdl_get_hunk(&xch, xecfg);
|
||||||
|
if (!xch)
|
||||||
|
break;
|
||||||
|
if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
|
||||||
|
xch->i2, xche->i2 + xche->chg2 - xch->i2,
|
||||||
|
ecb->priv) < 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
|
||||||
|
{
|
||||||
|
xdchange_t *xch;
|
||||||
|
|
||||||
|
for (xch = xscr; xch; xch = xch->next) {
|
||||||
|
int ignore = 1;
|
||||||
|
xrecord_t **rec;
|
||||||
|
long i;
|
||||||
|
|
||||||
|
rec = &xe->xdf1.recs[xch->i1];
|
||||||
|
for (i = 0; i < xch->chg1 && ignore; i++)
|
||||||
|
ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
|
||||||
|
|
||||||
|
rec = &xe->xdf2.recs[xch->i2];
|
||||||
|
for (i = 0; i < xch->chg2 && ignore; i++)
|
||||||
|
ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
|
||||||
|
|
||||||
|
xch->ignore = ignore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
|
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
|
||||||
xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
|
xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
|
||||||
xdchange_t *xscr;
|
xdchange_t *xscr;
|
||||||
xdfenv_t xe;
|
xdfenv_t xe;
|
||||||
emit_func_t ef = xecfg->emit_func ?
|
emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
|
||||||
(emit_func_t)xecfg->emit_func : xdl_emit_diff;
|
|
||||||
|
|
||||||
if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
|
if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
|
||||||
|
|
||||||
@ -558,6 +597,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (xscr) {
|
if (xscr) {
|
||||||
|
if (xpp->flags & XDF_IGNORE_BLANK_LINES)
|
||||||
|
xdl_mark_ignorable(xscr, &xe, xpp->flags);
|
||||||
|
|
||||||
if (ef(&xe, xscr, ecb, xecfg) < 0) {
|
if (ef(&xe, xscr, ecb, xecfg) < 0) {
|
||||||
|
|
||||||
xdl_free_script(xscr);
|
xdl_free_script(xscr);
|
||||||
|
@ -41,6 +41,7 @@ typedef struct s_xdchange {
|
|||||||
struct s_xdchange *next;
|
struct s_xdchange *next;
|
||||||
long i1, i2;
|
long i1, i2;
|
||||||
long chg1, chg2;
|
long chg1, chg2;
|
||||||
|
int ignore;
|
||||||
} xdchange_t;
|
} xdchange_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
|
|||||||
/*
|
/*
|
||||||
* Starting at the passed change atom, find the latest change atom to be included
|
* Starting at the passed change atom, find the latest change atom to be included
|
||||||
* inside the differential hunk according to the specified configuration.
|
* inside the differential hunk according to the specified configuration.
|
||||||
|
* Also advance xscr if the first changes must be discarded.
|
||||||
*/
|
*/
|
||||||
xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
|
xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
|
||||||
xdchange_t *xch, *xchp;
|
{
|
||||||
|
xdchange_t *xch, *xchp, *lxch;
|
||||||
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
|
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
|
||||||
|
long max_ignorable = xecfg->ctxlen;
|
||||||
|
unsigned long ignored = 0; /* number of ignored blank lines */
|
||||||
|
|
||||||
for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
|
/* remove ignorable changes that are too far before other changes */
|
||||||
if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
|
for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
|
||||||
|
xch = xchp->next;
|
||||||
|
|
||||||
|
if (xch == NULL ||
|
||||||
|
xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
|
||||||
|
*xscr = xch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*xscr == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
lxch = *xscr;
|
||||||
|
|
||||||
|
for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
|
||||||
|
long distance = xch->i1 - (xchp->i1 + xchp->chg1);
|
||||||
|
if (distance > max_common)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
return xchp;
|
if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
|
||||||
|
lxch = xch;
|
||||||
|
ignored = 0;
|
||||||
|
} else if (distance < max_ignorable && xch->ignore) {
|
||||||
|
ignored += xch->chg2;
|
||||||
|
} else if (lxch != xchp &&
|
||||||
|
xch->i1 + ignored - (lxch->i1 + lxch->chg1) > (unsigned long)max_common) {
|
||||||
|
break;
|
||||||
|
} else if (!xch->ignore) {
|
||||||
|
lxch = xch;
|
||||||
|
ignored = 0;
|
||||||
|
} else {
|
||||||
|
ignored += xch->chg2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lxch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -144,7 +179,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
|||||||
return xdl_emit_common(xe, xscr, ecb, xecfg);
|
return xdl_emit_common(xe, xscr, ecb, xecfg);
|
||||||
|
|
||||||
for (xch = xscr; xch; xch = xche->next) {
|
for (xch = xscr; xch; xch = xche->next) {
|
||||||
xche = xdl_get_hunk(xch, xecfg);
|
xche = xdl_get_hunk(&xch, xecfg);
|
||||||
|
if (!xch)
|
||||||
|
break;
|
||||||
|
|
||||||
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
|
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
|
||||||
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
|
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
||||||
xdemitconf_t const *xecfg);
|
xdemitconf_t const *xecfg);
|
||||||
|
|
||||||
xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
|
xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
|
||||||
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
|
||||||
xdemitconf_t const *xecfg);
|
xdemitconf_t const *xecfg);
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ static int fall_back_to_classic_diff(struct histindex *index,
|
|||||||
int line1, int count1, int line2, int count2)
|
int line1, int count1, int line2, int count2)
|
||||||
{
|
{
|
||||||
xpparam_t xpp;
|
xpparam_t xpp;
|
||||||
xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
|
xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
|
||||||
|
|
||||||
return xdl_fall_back_diff(index->env, &xpp,
|
return xdl_fall_back_diff(index->env, &xpp,
|
||||||
line1, count1, line2, count2);
|
line1, count1, line2, count2);
|
||||||
|
@ -245,11 +245,11 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
|
|||||||
dest ? dest + size : NULL);
|
dest ? dest + size : NULL);
|
||||||
/* Postimage from side #1 */
|
/* Postimage from side #1 */
|
||||||
if (m->mode & 1)
|
if (m->mode & 1)
|
||||||
size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
|
size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2),
|
||||||
dest ? dest + size : NULL);
|
dest ? dest + size : NULL);
|
||||||
/* Postimage from side #2 */
|
/* Postimage from side #2 */
|
||||||
if (m->mode & 2)
|
if (m->mode & 2)
|
||||||
size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
|
size += xdl_recs_copy(xe2, m->i2, m->chg2, 0,
|
||||||
dest ? dest + size : NULL);
|
dest ? dest + size : NULL);
|
||||||
} else
|
} else
|
||||||
continue;
|
continue;
|
||||||
|
@ -288,7 +288,7 @@ static int fall_back_to_classic_diff(struct hashmap *map,
|
|||||||
int line1, int count1, int line2, int count2)
|
int line1, int count1, int line2, int count2)
|
||||||
{
|
{
|
||||||
xpparam_t xpp;
|
xpparam_t xpp;
|
||||||
xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
|
xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
|
||||||
|
|
||||||
return xdl_fall_back_diff(map->env, &xpp,
|
return xdl_fall_back_diff(map->env, &xpp,
|
||||||
line1, count1, line2, count2);
|
line1, count1, line2, count2);
|
||||||
|
@ -181,7 +181,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
|
|||||||
if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
|
if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
|
||||||
goto abort;
|
goto abort;
|
||||||
|
|
||||||
if (xpp->flags & XDF_HISTOGRAM_DIFF)
|
if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
|
||||||
hbits = hsize = 0;
|
hbits = hsize = 0;
|
||||||
else {
|
else {
|
||||||
hbits = xdl_hashbits((unsigned int) narec);
|
hbits = xdl_hashbits((unsigned int) narec);
|
||||||
@ -209,8 +209,8 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
|
|||||||
crec->ha = hav;
|
crec->ha = hav;
|
||||||
recs[nrec++] = crec;
|
recs[nrec++] = crec;
|
||||||
|
|
||||||
if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
|
if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
|
||||||
xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
|
xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
|
||||||
goto abort;
|
goto abort;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,16 +273,15 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
|
|||||||
* (nrecs) will be updated correctly anyway by
|
* (nrecs) will be updated correctly anyway by
|
||||||
* xdl_prepare_ctx().
|
* xdl_prepare_ctx().
|
||||||
*/
|
*/
|
||||||
sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
|
sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
|
||||||
|
? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
|
||||||
|
|
||||||
enl1 = xdl_guess_lines(mf1, sample) + 1;
|
enl1 = xdl_guess_lines(mf1, sample) + 1;
|
||||||
enl2 = xdl_guess_lines(mf2, sample) + 1;
|
enl2 = xdl_guess_lines(mf2, sample) + 1;
|
||||||
|
|
||||||
if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
|
if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
|
||||||
xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
|
xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
|
if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
|
||||||
|
|
||||||
@ -296,9 +295,9 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
|
if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
|
||||||
!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
|
(XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
|
||||||
xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
|
xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
|
||||||
|
|
||||||
xdl_free_ctx(&xe->xdf2);
|
xdl_free_ctx(&xe->xdf2);
|
||||||
xdl_free_ctx(&xe->xdf1);
|
xdl_free_ctx(&xe->xdf1);
|
||||||
|
@ -120,35 +120,6 @@ void *xdl_cha_alloc(chastore_t *cha) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void *xdl_cha_first(chastore_t *cha) {
|
|
||||||
chanode_t *sncur;
|
|
||||||
|
|
||||||
if (!(cha->sncur = sncur = cha->head))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
cha->scurr = 0;
|
|
||||||
|
|
||||||
return (char *) sncur + sizeof(chanode_t) + cha->scurr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void *xdl_cha_next(chastore_t *cha) {
|
|
||||||
chanode_t *sncur;
|
|
||||||
|
|
||||||
if (!(sncur = cha->sncur))
|
|
||||||
return NULL;
|
|
||||||
cha->scurr += cha->isize;
|
|
||||||
if (cha->scurr == sncur->icurr) {
|
|
||||||
if (!(sncur = cha->sncur = sncur->next))
|
|
||||||
return NULL;
|
|
||||||
cha->scurr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (char *) sncur + sizeof(chanode_t) + cha->scurr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
long xdl_guess_lines(mmfile_t *mf, long sample) {
|
long xdl_guess_lines(mmfile_t *mf, long sample) {
|
||||||
long nl = 0, size, tsize = 0;
|
long nl = 0, size, tsize = 0;
|
||||||
char const *data, *cur, *top;
|
char const *data, *cur, *top;
|
||||||
@ -170,6 +141,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
|
|||||||
return nl + 1;
|
return nl + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int xdl_blankline(const char *line, long size, long flags)
|
||||||
|
{
|
||||||
|
long i;
|
||||||
|
|
||||||
|
if (!(flags & XDF_WHITESPACE_FLAGS))
|
||||||
|
return (size <= 1);
|
||||||
|
|
||||||
|
for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
|
||||||
|
;
|
||||||
|
|
||||||
|
return (i == size);
|
||||||
|
}
|
||||||
|
|
||||||
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
|
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
|
||||||
{
|
{
|
||||||
int i1, i2;
|
int i1, i2;
|
||||||
|
@ -34,6 +34,7 @@ void *xdl_cha_alloc(chastore_t *cha);
|
|||||||
void *xdl_cha_first(chastore_t *cha);
|
void *xdl_cha_first(chastore_t *cha);
|
||||||
void *xdl_cha_next(chastore_t *cha);
|
void *xdl_cha_next(chastore_t *cha);
|
||||||
long xdl_guess_lines(mmfile_t *mf, long sample);
|
long xdl_guess_lines(mmfile_t *mf, long sample);
|
||||||
|
int xdl_blankline(const char *line, long size, long flags);
|
||||||
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
|
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
|
||||||
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
|
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
|
||||||
unsigned int xdl_hashbits(unsigned int size);
|
unsigned int xdl_hashbits(unsigned int size);
|
||||||
|
@ -4,7 +4,7 @@ void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
|
|||||||
{
|
{
|
||||||
va_list arglist;
|
va_list arglist;
|
||||||
|
|
||||||
printf("Hunk %zd (line %d +%d): ", idx,
|
printf("Hunk %"PRIuZ" (line %d +%d): ", idx,
|
||||||
hunk->final_start_line_number, hunk->lines_in_hunk-1);
|
hunk->final_start_line_number, hunk->lines_in_hunk-1);
|
||||||
|
|
||||||
va_start(arglist, fmt);
|
va_start(arglist, fmt);
|
||||||
|
@ -486,8 +486,8 @@ void clar__assert_equal_file(
|
|||||||
for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
|
for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
|
||||||
/* find differing byte offset */;
|
/* find differing byte offset */;
|
||||||
p_snprintf(
|
p_snprintf(
|
||||||
buf, sizeof(buf), "file content mismatch at byte %d",
|
buf, sizeof(buf), "file content mismatch at byte %"PRIdZ,
|
||||||
(int)(total_bytes + pos));
|
(ssize_t)(total_bytes + pos));
|
||||||
p_close(fd);
|
p_close(fd);
|
||||||
clar__fail(file, line, path, buf, 1);
|
clar__fail(file, line, path, buf, 1);
|
||||||
}
|
}
|
||||||
|
@ -142,9 +142,28 @@ void _cl_trace_cb__event_handler(
|
|||||||
switch (ev) {
|
switch (ev) {
|
||||||
case CL_TRACE__SUITE_BEGIN:
|
case CL_TRACE__SUITE_BEGIN:
|
||||||
git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
|
git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
|
||||||
|
#if 0 && defined(GIT_MSVC_CRTDBG)
|
||||||
|
git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
|
||||||
|
suite_name);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CL_TRACE__SUITE_END:
|
case CL_TRACE__SUITE_END:
|
||||||
|
#if 0 && defined(GIT_MSVC_CRTDBG)
|
||||||
|
/* As an example of checkpointing, dump leaks within this suite.
|
||||||
|
* This may generate false positives for things like the global
|
||||||
|
* TLS error state and maybe the odb cache since they aren't
|
||||||
|
* freed until the global shutdown and outside the scope of this
|
||||||
|
* set of tests.
|
||||||
|
*
|
||||||
|
* This may under-report if the test itself uses a checkpoint.
|
||||||
|
* See tests/trace/windows/stacktrace.c
|
||||||
|
*/
|
||||||
|
git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
suite_name);
|
||||||
|
#endif
|
||||||
git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR);
|
git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -268,3 +268,35 @@ void test_diff_index__not_in_head_conflicted(void)
|
|||||||
git_index_free(index);
|
git_index_free(index);
|
||||||
git_tree_free(a);
|
git_tree_free(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_diff_index__to_index(void)
|
||||||
|
{
|
||||||
|
const char *a_commit = "26a125ee1bf"; /* the current HEAD */
|
||||||
|
git_tree *old_tree;
|
||||||
|
git_index *old_index;
|
||||||
|
git_index *new_index;
|
||||||
|
git_diff *diff;
|
||||||
|
diff_expects exp;
|
||||||
|
|
||||||
|
cl_git_pass(git_index_new(&old_index));
|
||||||
|
old_tree = resolve_commit_oid_to_tree(g_repo, a_commit);
|
||||||
|
cl_git_pass(git_index_read_tree(old_index, old_tree));
|
||||||
|
|
||||||
|
cl_git_pass(git_repository_index(&new_index, g_repo));
|
||||||
|
|
||||||
|
cl_git_pass(git_diff_index_to_index(&diff, g_repo, old_index, new_index, NULL));
|
||||||
|
|
||||||
|
memset(&exp, 0, sizeof(diff_expects));
|
||||||
|
cl_git_pass(git_diff_foreach(
|
||||||
|
diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
|
||||||
|
cl_assert_equal_i(8, exp.files);
|
||||||
|
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
|
||||||
|
cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
|
||||||
|
cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
|
||||||
|
cl_assert_equal_i(0, exp.file_status[GIT_DELTA_CONFLICTED]);
|
||||||
|
|
||||||
|
git_diff_free(diff);
|
||||||
|
git_index_free(new_index);
|
||||||
|
git_index_free(old_index);
|
||||||
|
git_tree_free(old_tree);
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "buf_text.h"
|
#include "buf_text.h"
|
||||||
#include "git2/sys/filter.h"
|
#include "git2/sys/filter.h"
|
||||||
#include "git2/sys/repository.h"
|
#include "git2/sys/repository.h"
|
||||||
|
#include "custom_helpers.h"
|
||||||
|
|
||||||
/* going TO_WORKDIR, filters are executed low to high
|
/* going TO_WORKDIR, filters are executed low to high
|
||||||
* going TO_ODB, filters are executed high to low
|
* going TO_ODB, filters are executed high to low
|
||||||
@ -12,8 +13,6 @@
|
|||||||
#define BITFLIP_FILTER_PRIORITY -1
|
#define BITFLIP_FILTER_PRIORITY -1
|
||||||
#define REVERSE_FILTER_PRIORITY -2
|
#define REVERSE_FILTER_PRIORITY -2
|
||||||
|
|
||||||
#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff)
|
|
||||||
|
|
||||||
#ifdef GIT_WIN32
|
#ifdef GIT_WIN32
|
||||||
# define NEWLINE "\r\n"
|
# define NEWLINE "\r\n"
|
||||||
#else
|
#else
|
||||||
@ -27,6 +26,8 @@ static char workdir_data[] =
|
|||||||
"trivially" NEWLINE
|
"trivially" NEWLINE
|
||||||
"scrambled." NEWLINE;
|
"scrambled." NEWLINE;
|
||||||
|
|
||||||
|
#define REVERSED_DATA_LEN 51
|
||||||
|
|
||||||
/* Represents the data above scrambled (bits flipped) after \r\n -> \n
|
/* Represents the data above scrambled (bits flipped) after \r\n -> \n
|
||||||
* conversion, then bytewise reversed
|
* conversion, then bytewise reversed
|
||||||
*/
|
*/
|
||||||
@ -63,107 +64,6 @@ void test_filter_custom__cleanup(void)
|
|||||||
g_repo = NULL;
|
g_repo = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bitflip_filter_apply(
|
|
||||||
git_filter *self,
|
|
||||||
void **payload,
|
|
||||||
git_buf *to,
|
|
||||||
const git_buf *from,
|
|
||||||
const git_filter_source *source)
|
|
||||||
{
|
|
||||||
const unsigned char *src = (const unsigned char *)from->ptr;
|
|
||||||
unsigned char *dst;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
GIT_UNUSED(self); GIT_UNUSED(payload);
|
|
||||||
|
|
||||||
/* verify that attribute path match worked as expected */
|
|
||||||
cl_assert_equal_i(
|
|
||||||
0, git__strncmp("hero", git_filter_source_path(source), 4));
|
|
||||||
|
|
||||||
if (!from->size)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cl_git_pass(git_buf_grow(to, from->size));
|
|
||||||
|
|
||||||
dst = (unsigned char *)to->ptr;
|
|
||||||
|
|
||||||
for (i = 0; i < from->size; i++)
|
|
||||||
dst[i] = VERY_SECURE_ENCRYPTION(src[i]);
|
|
||||||
|
|
||||||
to->size = from->size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bitflip_filter_free(git_filter *f)
|
|
||||||
{
|
|
||||||
git__free(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static git_filter *create_bitflip_filter(void)
|
|
||||||
{
|
|
||||||
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
|
||||||
cl_assert(filter);
|
|
||||||
|
|
||||||
filter->version = GIT_FILTER_VERSION;
|
|
||||||
filter->attributes = "+bitflip";
|
|
||||||
filter->shutdown = bitflip_filter_free;
|
|
||||||
filter->apply = bitflip_filter_apply;
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int reverse_filter_apply(
|
|
||||||
git_filter *self,
|
|
||||||
void **payload,
|
|
||||||
git_buf *to,
|
|
||||||
const git_buf *from,
|
|
||||||
const git_filter_source *source)
|
|
||||||
{
|
|
||||||
const unsigned char *src = (const unsigned char *)from->ptr;
|
|
||||||
const unsigned char *end = src + from->size;
|
|
||||||
unsigned char *dst;
|
|
||||||
|
|
||||||
GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source);
|
|
||||||
|
|
||||||
/* verify that attribute path match worked as expected */
|
|
||||||
cl_assert_equal_i(
|
|
||||||
0, git__strncmp("hero", git_filter_source_path(source), 4));
|
|
||||||
|
|
||||||
if (!from->size)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cl_git_pass(git_buf_grow(to, from->size));
|
|
||||||
|
|
||||||
dst = (unsigned char *)to->ptr + from->size - 1;
|
|
||||||
|
|
||||||
while (src < end)
|
|
||||||
*dst-- = *src++;
|
|
||||||
|
|
||||||
to->size = from->size;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reverse_filter_free(git_filter *f)
|
|
||||||
{
|
|
||||||
git__free(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static git_filter *create_reverse_filter(const char *attrs)
|
|
||||||
{
|
|
||||||
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
|
||||||
cl_assert(filter);
|
|
||||||
|
|
||||||
filter->version = GIT_FILTER_VERSION;
|
|
||||||
filter->attributes = attrs;
|
|
||||||
filter->shutdown = reverse_filter_free;
|
|
||||||
filter->apply = reverse_filter_apply;
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void register_custom_filters(void)
|
static void register_custom_filters(void)
|
||||||
{
|
{
|
||||||
static int filters_registered = 0;
|
static int filters_registered = 0;
|
||||||
@ -186,7 +86,6 @@ static void register_custom_filters(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void test_filter_custom__to_odb(void)
|
void test_filter_custom__to_odb(void)
|
||||||
{
|
{
|
||||||
git_filter_list *fl;
|
git_filter_list *fl;
|
||||||
|
108
tests/filter/custom_helpers.c
Normal file
108
tests/filter/custom_helpers.c
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "posix.h"
|
||||||
|
#include "filter.h"
|
||||||
|
#include "buf_text.h"
|
||||||
|
#include "git2/sys/filter.h"
|
||||||
|
|
||||||
|
#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff)
|
||||||
|
|
||||||
|
int bitflip_filter_apply(
|
||||||
|
git_filter *self,
|
||||||
|
void **payload,
|
||||||
|
git_buf *to,
|
||||||
|
const git_buf *from,
|
||||||
|
const git_filter_source *source)
|
||||||
|
{
|
||||||
|
const unsigned char *src = (const unsigned char *)from->ptr;
|
||||||
|
unsigned char *dst;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
GIT_UNUSED(self); GIT_UNUSED(payload);
|
||||||
|
|
||||||
|
/* verify that attribute path match worked as expected */
|
||||||
|
cl_assert_equal_i(
|
||||||
|
0, git__strncmp("hero", git_filter_source_path(source), 4));
|
||||||
|
|
||||||
|
if (!from->size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cl_git_pass(git_buf_grow(to, from->size));
|
||||||
|
|
||||||
|
dst = (unsigned char *)to->ptr;
|
||||||
|
|
||||||
|
for (i = 0; i < from->size; i++)
|
||||||
|
dst[i] = VERY_SECURE_ENCRYPTION(src[i]);
|
||||||
|
|
||||||
|
to->size = from->size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bitflip_filter_free(git_filter *f)
|
||||||
|
{
|
||||||
|
git__free(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_filter *create_bitflip_filter(void)
|
||||||
|
{
|
||||||
|
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
||||||
|
cl_assert(filter);
|
||||||
|
|
||||||
|
filter->version = GIT_FILTER_VERSION;
|
||||||
|
filter->attributes = "+bitflip";
|
||||||
|
filter->shutdown = bitflip_filter_free;
|
||||||
|
filter->apply = bitflip_filter_apply;
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int reverse_filter_apply(
|
||||||
|
git_filter *self,
|
||||||
|
void **payload,
|
||||||
|
git_buf *to,
|
||||||
|
const git_buf *from,
|
||||||
|
const git_filter_source *source)
|
||||||
|
{
|
||||||
|
const unsigned char *src = (const unsigned char *)from->ptr;
|
||||||
|
const unsigned char *end = src + from->size;
|
||||||
|
unsigned char *dst;
|
||||||
|
|
||||||
|
GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source);
|
||||||
|
|
||||||
|
/* verify that attribute path match worked as expected */
|
||||||
|
cl_assert_equal_i(
|
||||||
|
0, git__strncmp("hero", git_filter_source_path(source), 4));
|
||||||
|
|
||||||
|
if (!from->size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cl_git_pass(git_buf_grow(to, from->size));
|
||||||
|
|
||||||
|
dst = (unsigned char *)to->ptr + from->size - 1;
|
||||||
|
|
||||||
|
while (src < end)
|
||||||
|
*dst-- = *src++;
|
||||||
|
|
||||||
|
to->size = from->size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reverse_filter_free(git_filter *f)
|
||||||
|
{
|
||||||
|
git__free(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_filter *create_reverse_filter(const char *attrs)
|
||||||
|
{
|
||||||
|
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
||||||
|
cl_assert(filter);
|
||||||
|
|
||||||
|
filter->version = GIT_FILTER_VERSION;
|
||||||
|
filter->attributes = attrs;
|
||||||
|
filter->shutdown = reverse_filter_free;
|
||||||
|
filter->apply = reverse_filter_apply;
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
18
tests/filter/custom_helpers.h
Normal file
18
tests/filter/custom_helpers.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "git2/sys/filter.h"
|
||||||
|
|
||||||
|
extern git_filter *create_bitflip_filter(void);
|
||||||
|
extern git_filter *create_reverse_filter(const char *attr);
|
||||||
|
|
||||||
|
extern int bitflip_filter_apply(
|
||||||
|
git_filter *self,
|
||||||
|
void **payload,
|
||||||
|
git_buf *to,
|
||||||
|
const git_buf *from,
|
||||||
|
const git_filter_source *source);
|
||||||
|
|
||||||
|
extern int reverse_filter_apply(
|
||||||
|
git_filter *self,
|
||||||
|
void **payload,
|
||||||
|
git_buf *to,
|
||||||
|
const git_buf *from,
|
||||||
|
const git_filter_source *source);
|
@ -25,6 +25,7 @@ void test_filter_stream__cleanup(void)
|
|||||||
g_repo = NULL;
|
g_repo = NULL;
|
||||||
|
|
||||||
git_filter_unregister("compress");
|
git_filter_unregister("compress");
|
||||||
|
git__free(compress_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHUNKSIZE 10240
|
#define CHUNKSIZE 10240
|
||||||
@ -123,11 +124,6 @@ static int compress_filter_stream_init(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compress_filter_free(git_filter *f)
|
|
||||||
{
|
|
||||||
git__free(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
git_filter *create_compress_filter(void)
|
git_filter *create_compress_filter(void)
|
||||||
{
|
{
|
||||||
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
||||||
@ -136,7 +132,6 @@ git_filter *create_compress_filter(void)
|
|||||||
filter->version = GIT_FILTER_VERSION;
|
filter->version = GIT_FILTER_VERSION;
|
||||||
filter->attributes = "+compress";
|
filter->attributes = "+compress";
|
||||||
filter->stream = compress_filter_stream_init;
|
filter->stream = compress_filter_stream_init;
|
||||||
filter->shutdown = compress_filter_free;
|
|
||||||
|
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
184
tests/filter/wildcard.c
Normal file
184
tests/filter/wildcard.c
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "posix.h"
|
||||||
|
#include "blob.h"
|
||||||
|
#include "filter.h"
|
||||||
|
#include "buf_text.h"
|
||||||
|
#include "git2/sys/filter.h"
|
||||||
|
#include "git2/sys/repository.h"
|
||||||
|
#include "custom_helpers.h"
|
||||||
|
|
||||||
|
static git_repository *g_repo = NULL;
|
||||||
|
|
||||||
|
static git_filter *create_wildcard_filter(void);
|
||||||
|
|
||||||
|
#define DATA_LEN 32
|
||||||
|
|
||||||
|
static unsigned char input[] = {
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||||
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||||
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||||
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned char reversed[] = {
|
||||||
|
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
|
||||||
|
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||||
|
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||||
|
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned char flipped[] = {
|
||||||
|
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
|
||||||
|
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
|
||||||
|
0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
|
||||||
|
0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_filter_wildcard__initialize(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(git_filter_register(
|
||||||
|
"wildcard", create_wildcard_filter(), GIT_FILTER_DRIVER_PRIORITY));
|
||||||
|
|
||||||
|
g_repo = cl_git_sandbox_init("empty_standard_repo");
|
||||||
|
|
||||||
|
cl_git_rewritefile(
|
||||||
|
"empty_standard_repo/.gitattributes",
|
||||||
|
"* binary\n"
|
||||||
|
"hero-flip-* filter=wcflip\n"
|
||||||
|
"hero-reverse-* filter=wcreverse\n"
|
||||||
|
"none-* filter=unregistered\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_filter_wildcard__cleanup(void)
|
||||||
|
{
|
||||||
|
cl_git_pass(git_filter_unregister("wildcard"));
|
||||||
|
|
||||||
|
cl_git_sandbox_cleanup();
|
||||||
|
g_repo = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wildcard_filter_check(
|
||||||
|
git_filter *self,
|
||||||
|
void **payload,
|
||||||
|
const git_filter_source *src,
|
||||||
|
const char **attr_values)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(self);
|
||||||
|
GIT_UNUSED(src);
|
||||||
|
|
||||||
|
if (strcmp(attr_values[0], "wcflip") == 0 ||
|
||||||
|
strcmp(attr_values[0], "wcreverse") == 0) {
|
||||||
|
*payload = git__strdup(attr_values[0]);
|
||||||
|
GITERR_CHECK_ALLOC(*payload);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GIT_PASSTHROUGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wildcard_filter_apply(
|
||||||
|
git_filter *self,
|
||||||
|
void **payload,
|
||||||
|
git_buf *to,
|
||||||
|
const git_buf *from,
|
||||||
|
const git_filter_source *source)
|
||||||
|
{
|
||||||
|
const char *filtername = *payload;
|
||||||
|
|
||||||
|
if (filtername && strcmp(filtername, "wcflip") == 0)
|
||||||
|
return bitflip_filter_apply(self, payload, to, from, source);
|
||||||
|
else if (filtername && strcmp(filtername, "wcreverse") == 0)
|
||||||
|
return reverse_filter_apply(self, payload, to, from, source);
|
||||||
|
|
||||||
|
cl_fail("Unexpected attribute");
|
||||||
|
return GIT_PASSTHROUGH;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wildcard_filter_cleanup(git_filter *self, void *payload)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(self);
|
||||||
|
git__free(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wildcard_filter_free(git_filter *f)
|
||||||
|
{
|
||||||
|
git__free(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static git_filter *create_wildcard_filter(void)
|
||||||
|
{
|
||||||
|
git_filter *filter = git__calloc(1, sizeof(git_filter));
|
||||||
|
cl_assert(filter);
|
||||||
|
|
||||||
|
filter->version = GIT_FILTER_VERSION;
|
||||||
|
filter->attributes = "filter=*";
|
||||||
|
filter->check = wildcard_filter_check;
|
||||||
|
filter->apply = wildcard_filter_apply;
|
||||||
|
filter->cleanup = wildcard_filter_cleanup;
|
||||||
|
filter->shutdown = wildcard_filter_free;
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_filter_wildcard__reverse(void)
|
||||||
|
{
|
||||||
|
git_filter_list *fl;
|
||||||
|
git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
cl_git_pass(git_filter_list_load(
|
||||||
|
&fl, g_repo, NULL, "hero-reverse-foo", GIT_FILTER_TO_ODB, 0));
|
||||||
|
|
||||||
|
cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN));
|
||||||
|
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
|
||||||
|
|
||||||
|
cl_assert_equal_i(DATA_LEN, out.size);
|
||||||
|
|
||||||
|
cl_assert_equal_i(
|
||||||
|
0, memcmp(reversed, out.ptr, out.size));
|
||||||
|
|
||||||
|
git_filter_list_free(fl);
|
||||||
|
git_buf_free(&out);
|
||||||
|
git_buf_free(&in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_filter_wildcard__flip(void)
|
||||||
|
{
|
||||||
|
git_filter_list *fl;
|
||||||
|
git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
cl_git_pass(git_filter_list_load(
|
||||||
|
&fl, g_repo, NULL, "hero-flip-foo", GIT_FILTER_TO_ODB, 0));
|
||||||
|
|
||||||
|
cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN));
|
||||||
|
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
|
||||||
|
|
||||||
|
cl_assert_equal_i(DATA_LEN, out.size);
|
||||||
|
|
||||||
|
cl_assert_equal_i(
|
||||||
|
0, memcmp(flipped, out.ptr, out.size));
|
||||||
|
|
||||||
|
git_filter_list_free(fl);
|
||||||
|
git_buf_free(&out);
|
||||||
|
git_buf_free(&in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_filter_wildcard__none(void)
|
||||||
|
{
|
||||||
|
git_filter_list *fl;
|
||||||
|
git_buf in = GIT_BUF_INIT, out = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
cl_git_pass(git_filter_list_load(
|
||||||
|
&fl, g_repo, NULL, "none-foo", GIT_FILTER_TO_ODB, 0));
|
||||||
|
|
||||||
|
cl_git_pass(git_buf_put(&in, (char *)input, DATA_LEN));
|
||||||
|
cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
|
||||||
|
|
||||||
|
cl_assert_equal_i(DATA_LEN, out.size);
|
||||||
|
|
||||||
|
cl_assert_equal_i(
|
||||||
|
0, memcmp(input, out.ptr, out.size));
|
||||||
|
|
||||||
|
git_filter_list_free(fl);
|
||||||
|
git_buf_free(&out);
|
||||||
|
git_buf_free(&in);
|
||||||
|
}
|
19
tests/main.c
19
tests/main.c
@ -1,10 +1,3 @@
|
|||||||
|
|
||||||
#if defined(GIT_MSVC_CRTDBG)
|
|
||||||
/* Enable MSVC CRTDBG memory leak reporting. See src/util.h for details. */
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <crtdbg.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "clar_libgit2.h"
|
#include "clar_libgit2.h"
|
||||||
#include "clar_libgit2_trace.h"
|
#include "clar_libgit2_trace.h"
|
||||||
|
|
||||||
@ -16,18 +9,6 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
#if defined(GIT_MSVC_CRTDBG)
|
|
||||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
||||||
|
|
||||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
|
||||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
|
||||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
|
|
||||||
|
|
||||||
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
|
|
||||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
|
||||||
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
clar_test_init(argc, argv);
|
clar_test_init(argc, argv);
|
||||||
|
|
||||||
git_libgit2_init();
|
git_libgit2_init();
|
||||||
|
@ -249,3 +249,42 @@ void test_merge_files__automerge_whitespace_change(void)
|
|||||||
|
|
||||||
git_merge_file_result_free(&result);
|
git_merge_file_result_free(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_merge_files__doesnt_add_newline(void)
|
||||||
|
{
|
||||||
|
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
|
||||||
|
ours = GIT_MERGE_FILE_INPUT_INIT,
|
||||||
|
theirs = GIT_MERGE_FILE_INPUT_INIT;
|
||||||
|
git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
|
||||||
|
git_merge_file_result result = {0};
|
||||||
|
const char *expected = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen";
|
||||||
|
|
||||||
|
ancestor.ptr = "0\n1\n2\n3\n4\n5 XXX\n6YYY\n7\n8\n9\n10";
|
||||||
|
ancestor.size = strlen(ancestor.ptr);
|
||||||
|
ancestor.path = "testfile.txt";
|
||||||
|
ancestor.mode = 0100755;
|
||||||
|
|
||||||
|
ours.ptr = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\n10";
|
||||||
|
ours.size = strlen(ours.ptr);
|
||||||
|
ours.path = "testfile.txt";
|
||||||
|
ours.mode = 0100755;
|
||||||
|
|
||||||
|
theirs.ptr = "0\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen";
|
||||||
|
theirs.size = strlen(theirs.ptr);
|
||||||
|
theirs.path = "testfile.txt";
|
||||||
|
theirs.mode = 0100755;
|
||||||
|
|
||||||
|
opts.flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE;
|
||||||
|
cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
|
||||||
|
|
||||||
|
cl_assert_equal_i(1, result.automergeable);
|
||||||
|
|
||||||
|
cl_assert_equal_s("testfile.txt", result.path);
|
||||||
|
cl_assert_equal_i(0100755, result.mode);
|
||||||
|
|
||||||
|
cl_assert_equal_i(strlen(expected), result.len);
|
||||||
|
cl_assert_equal_strn(expected, result.ptr, result.len);
|
||||||
|
|
||||||
|
git_merge_file_result_free(&result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ void merge__dump_index_entries(git_vector *index_entries)
|
|||||||
size_t i;
|
size_t i;
|
||||||
const git_index_entry *index_entry;
|
const git_index_entry *index_entry;
|
||||||
|
|
||||||
printf ("\nINDEX [%d]:\n", (int)index_entries->length);
|
printf ("\nINDEX [%"PRIuZ"]:\n", index_entries->length);
|
||||||
for (i = 0; i < index_entries->length; i++) {
|
for (i = 0; i < index_entries->length; i++) {
|
||||||
index_entry = index_entries->contents[i];
|
index_entry = index_entries->contents[i];
|
||||||
|
|
||||||
|
@ -334,16 +334,18 @@ void test_revert_workdir__again_after_edit_two(void)
|
|||||||
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
|
cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
|
||||||
|
|
||||||
cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt"));
|
cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt"));
|
||||||
cl_assert(strcmp(diff_buf.ptr, "a\n" \
|
cl_assert_equal_s(
|
||||||
"<<<<<<< HEAD\n" \
|
"a\n" \
|
||||||
"=======\n" \
|
"<<<<<<< HEAD\n" \
|
||||||
"a\n" \
|
"=======\n" \
|
||||||
">>>>>>> parent of 97e52d5... Revert me\n" \
|
"a\n" \
|
||||||
"a\n" \
|
">>>>>>> parent of 97e52d5... Revert me\n" \
|
||||||
"a\n" \
|
"a\n" \
|
||||||
"a\n" \
|
"a\n" \
|
||||||
"a\n" \
|
"a\n" \
|
||||||
"ab\n") == 0);
|
"a\n" \
|
||||||
|
"ab",
|
||||||
|
diff_buf.ptr);
|
||||||
|
|
||||||
git_commit_free(revert_commit);
|
git_commit_free(revert_commit);
|
||||||
git_commit_free(head_commit);
|
git_commit_free(head_commit);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "clar_libgit2.h"
|
#include "clar_libgit2.h"
|
||||||
#include "submodule_helpers.h"
|
#include "submodule_helpers.h"
|
||||||
#include "git2/sys/repository.h"
|
#include "git2/sys/repository.h"
|
||||||
|
#include "repository.h"
|
||||||
#include "fileops.h"
|
#include "fileops.h"
|
||||||
|
|
||||||
static git_repository *g_repo = NULL;
|
static git_repository *g_repo = NULL;
|
||||||
@ -103,10 +104,27 @@ static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
|
|||||||
|
|
||||||
void test_submodule_lookup__foreach(void)
|
void test_submodule_lookup__foreach(void)
|
||||||
{
|
{
|
||||||
|
git_config *cfg;
|
||||||
sm_lookup_data data;
|
sm_lookup_data data;
|
||||||
|
|
||||||
memset(&data, 0, sizeof(data));
|
memset(&data, 0, sizeof(data));
|
||||||
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
|
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
|
||||||
cl_assert_equal_i(8, data.count);
|
cl_assert_equal_i(8, data.count);
|
||||||
|
|
||||||
|
memset(&data, 0, sizeof(data));
|
||||||
|
|
||||||
|
/* Change the path for a submodule so it doesn't match the name */
|
||||||
|
cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
|
||||||
|
|
||||||
|
cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index"));
|
||||||
|
cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target"));
|
||||||
|
cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path"));
|
||||||
|
cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url"));
|
||||||
|
|
||||||
|
git_config_free(cfg);
|
||||||
|
|
||||||
|
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
|
||||||
|
cl_assert_equal_i(8, data.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_submodule_lookup__lookup_even_with_unborn_head(void)
|
void test_submodule_lookup__lookup_even_with_unborn_head(void)
|
||||||
@ -269,3 +287,26 @@ void test_submodule_lookup__just_added(void)
|
|||||||
refute_submodule_exists(g_repo, "sm_just_added_head", GIT_EEXISTS);
|
refute_submodule_exists(g_repo, "sm_just_added_head", GIT_EEXISTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test_App and Test_App2 are fairly similar names, make sure we load the right one */
|
||||||
|
void test_submodule_lookup__prefix_name(void)
|
||||||
|
{
|
||||||
|
git_submodule *sm;
|
||||||
|
|
||||||
|
cl_git_rewritefile("submod2/.gitmodules",
|
||||||
|
"[submodule \"Test_App\"]\n"
|
||||||
|
" path = Test_App\n"
|
||||||
|
" url = ../Test_App\n"
|
||||||
|
"[submodule \"Test_App2\"]\n"
|
||||||
|
" path = Test_App2\n"
|
||||||
|
" url = ../Test_App\n");
|
||||||
|
|
||||||
|
cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App"));
|
||||||
|
cl_assert_equal_s("Test_App", git_submodule_name(sm));
|
||||||
|
|
||||||
|
git_submodule_free(sm);
|
||||||
|
|
||||||
|
cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App2"));
|
||||||
|
cl_assert_equal_s("Test_App2", git_submodule_name(sm));
|
||||||
|
|
||||||
|
git_submodule_free(sm);
|
||||||
|
}
|
||||||
|
@ -71,8 +71,6 @@ void test_submodule_nosubs__add_and_delete(void)
|
|||||||
git_submodule *sm;
|
git_submodule *sm;
|
||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
|
|
||||||
/* note lack of calls to git_submodule_reload_all - this *should* work */
|
|
||||||
|
|
||||||
cl_git_fail(git_submodule_lookup(NULL, repo, "libgit2"));
|
cl_git_fail(git_submodule_lookup(NULL, repo, "libgit2"));
|
||||||
cl_git_fail(git_submodule_lookup(NULL, repo, "submodules/libgit2"));
|
cl_git_fail(git_submodule_lookup(NULL, repo, "submodules/libgit2"));
|
||||||
|
|
||||||
|
151
tests/trace/windows/stacktrace.c
Normal file
151
tests/trace/windows/stacktrace.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
#include "clar_libgit2.h"
|
||||||
|
#include "win32/w32_stack.h"
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
static void a(void)
|
||||||
|
{
|
||||||
|
char buf[10000];
|
||||||
|
|
||||||
|
cl_assert(git_win32__stack(buf, sizeof(buf), 0, NULL, NULL) == 0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
fprintf(stderr, "Stacktrace from [%s:%d]:\n%s\n", __FILE__, __LINE__, buf);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void b(void)
|
||||||
|
{
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void c(void)
|
||||||
|
{
|
||||||
|
b();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void test_trace_windows_stacktrace__basic(void)
|
||||||
|
{
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
c();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void test_trace_windows_stacktrace__leaks(void)
|
||||||
|
{
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
void * p1;
|
||||||
|
void * p2;
|
||||||
|
void * p3;
|
||||||
|
void * p4;
|
||||||
|
int before, after;
|
||||||
|
int leaks;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/* remember outstanding leaks due to set setup
|
||||||
|
* and set mark/checkpoint.
|
||||||
|
*/
|
||||||
|
before = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
p1 = git__malloc(5);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"p1");
|
||||||
|
cl_assert((leaks == 1));
|
||||||
|
|
||||||
|
p2 = git__malloc(5);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"p1,p2");
|
||||||
|
cl_assert((leaks == 2));
|
||||||
|
|
||||||
|
p3 = git__malloc(5);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"p1,p2,p3");
|
||||||
|
cl_assert((leaks == 3));
|
||||||
|
|
||||||
|
git__free(p2);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"p1,p3");
|
||||||
|
cl_assert((leaks == 2));
|
||||||
|
|
||||||
|
/* move the mark. only new leaks should appear afterwards */
|
||||||
|
error = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
|
||||||
|
NULL);
|
||||||
|
cl_assert((error == 0));
|
||||||
|
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"not_p1,not_p3");
|
||||||
|
cl_assert((leaks == 0));
|
||||||
|
|
||||||
|
p4 = git__malloc(5);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"p4,not_p1,not_p3");
|
||||||
|
cl_assert((leaks == 1));
|
||||||
|
|
||||||
|
git__free(p1);
|
||||||
|
git__free(p3);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"p4");
|
||||||
|
cl_assert((leaks == 1));
|
||||||
|
|
||||||
|
git__free(p4);
|
||||||
|
leaks = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
|
||||||
|
"end");
|
||||||
|
cl_assert((leaks == 0));
|
||||||
|
|
||||||
|
/* confirm current absolute leaks count matches beginning value. */
|
||||||
|
after = git_win32__crtdbg_stacktrace__dump(
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__QUIET |
|
||||||
|
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
|
||||||
|
"total");
|
||||||
|
cl_assert((before == after));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
static void aux_cb_alloc__1(unsigned int *aux_id)
|
||||||
|
{
|
||||||
|
static unsigned int aux_counter = 0;
|
||||||
|
|
||||||
|
*aux_id = aux_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void aux_cb_lookup__1(unsigned int aux_id, char *aux_msg, unsigned int aux_msg_len)
|
||||||
|
{
|
||||||
|
p_snprintf(aux_msg, aux_msg_len, "\tQQ%08x\n", aux_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void test_trace_windows_stacktrace__aux1(void)
|
||||||
|
{
|
||||||
|
#if defined(GIT_MSVC_CRTDBG)
|
||||||
|
git_win32__stack__set_aux_cb(aux_cb_alloc__1, aux_cb_lookup__1);
|
||||||
|
c();
|
||||||
|
c();
|
||||||
|
c();
|
||||||
|
c();
|
||||||
|
git_win32__stack__set_aux_cb(NULL, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user