-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve git_status_list (git status) #11
base: libgit-next
Are you sure you want to change the base?
Changes from all commits
b45219f
7a76a33
07493cc
e1a1eaa
dc4e588
0739689
27f3c80
37caa8d
6b12762
670415a
973d959
62d492d
e4eabb0
caee92e
f683806
eb8c3e5
b58e905
a9eac6a
1f39aac
23c24f8
1f5e7f9
6da6a10
30d5c08
4b193b1
fe44f25
e78ee33
013d416
3ad710a
4c98283
8254d2e
45f0e26
a4c112d
110e29c
8bbbfab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* 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_sys_custom_tls_h__ | ||
#define INCLUDE_sys_custom_tls_h__ | ||
|
||
#include "git2/common.h" | ||
|
||
GIT_BEGIN_DECL | ||
|
||
/** | ||
* Used to retrieve a pointer from a user of the library to pass to a newly | ||
* created internal libgit2 thread. This should allow users of the library to | ||
* establish a context that spans an internally threaded operation. This can | ||
* useful for libraries that leverage callbacks used in an internally threaded | ||
* routine. | ||
*/ | ||
typedef void *GIT_CALLBACK(git_retrieve_tls_for_internal_thread_cb)(void); | ||
|
||
/** | ||
* This callback will be called when a thread is exiting so that a user | ||
* of the library can clean up their thread local storage. | ||
*/ | ||
typedef void GIT_CALLBACK(git_set_tls_on_internal_thread_cb)(void *payload); | ||
|
||
/** | ||
* This callback will be called when a thread is exiting so that a user | ||
* of the library can clean up their thread local storage. | ||
*/ | ||
typedef void GIT_CALLBACK(git_teardown_tls_on_internal_thread_cb)(void); | ||
|
||
/** | ||
* Sets the callbacks for custom thread local storage used by internally | ||
* created libgit2 threads. This allows users of the library an opportunity | ||
* to set thread local storage for internal threads based on the creating | ||
* thread. | ||
* | ||
* @param retrieve_storage_for_internal_thread Used to retrieve a pointer on | ||
* a thread before spawning child | ||
* threads. This pointer will be | ||
* passed to set_storage_on_thread | ||
* in the newly spawned threads. | ||
* @param set_storage_on_thread When a thread is spawned internally in libgit2, | ||
* whatever pointer was retrieved in the calling | ||
* thread by retrieve_storage_for_internal_thread | ||
* will be passed to this callback in the newly | ||
* spawned thread. | ||
* @param teardown_storage_on_thread Before an internally spawned thread exits, | ||
* this method will be called allowing a user | ||
* of the library an opportunity to clean up | ||
* any thread local storage they set up on | ||
* the internal thread. | ||
* @return 0 on success, or an error code. (use git_error_last for information | ||
* about the error) | ||
*/ | ||
GIT_EXTERN(int) git_custom_tls_set_callbacks( | ||
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread, | ||
git_set_tls_on_internal_thread_cb set_storage_on_thread, | ||
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread); | ||
|
||
GIT_END_DECL | ||
|
||
#endif |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
#include "common.h" | ||
#include "custom_tls.h" | ||
#include "runtime.h" | ||
|
||
#ifdef GIT_THREADS | ||
|
||
#ifdef GIT_WIN32 | ||
# include "win32/thread.h" | ||
#else | ||
# include "unix/pthread.h" | ||
#endif | ||
|
||
struct git_custom_tls_callbacks { | ||
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread; | ||
|
||
git_set_tls_on_internal_thread_cb set_storage_on_thread; | ||
|
||
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread; | ||
|
||
git_rwlock lock; | ||
}; | ||
|
||
struct git_custom_tls_callbacks git__custom_tls = { 0, 0, 0 }; | ||
|
||
static void git_custom_tls_global_shutdown(void) | ||
{ | ||
if (git_rwlock_wrlock(&git__custom_tls.lock) < 0) | ||
return; | ||
|
||
git__custom_tls.retrieve_storage_for_internal_thread = 0; | ||
git__custom_tls.set_storage_on_thread = 0; | ||
git__custom_tls.teardown_storage_on_thread = 0; | ||
|
||
git_rwlock_wrunlock(&git__custom_tls.lock); | ||
git_rwlock_free(&git__custom_tls.lock); | ||
} | ||
|
||
int git_custom_tls__global_init(void) | ||
{ | ||
if (git_rwlock_init(&git__custom_tls.lock) < 0) | ||
return -1; | ||
|
||
return git_runtime_shutdown_register(git_custom_tls_global_shutdown); | ||
} | ||
|
||
int git_custom_tls_set_callbacks( | ||
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread, | ||
git_set_tls_on_internal_thread_cb set_storage_on_thread, | ||
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread) | ||
{ | ||
/* We want to ensure that all callbacks are set or not set in totality. | ||
* It does not make sense to have a subset of callbacks set. | ||
*/ | ||
assert((retrieve_storage_for_internal_thread && set_storage_on_thread && | ||
teardown_storage_on_thread) || !(retrieve_storage_for_internal_thread && | ||
set_storage_on_thread && teardown_storage_on_thread)); | ||
|
||
if (git_rwlock_wrlock(&git__custom_tls.lock) < 0) { | ||
git_error_set(GIT_ERROR_OS, "failed to lock custom thread local storage"); | ||
return -1; | ||
} | ||
|
||
git__custom_tls.retrieve_storage_for_internal_thread = | ||
retrieve_storage_for_internal_thread; | ||
git__custom_tls.set_storage_on_thread = | ||
set_storage_on_thread; | ||
git__custom_tls.teardown_storage_on_thread = | ||
teardown_storage_on_thread; | ||
|
||
git_rwlock_wrunlock(&git__custom_tls.lock); | ||
return 0; | ||
} | ||
|
||
int git_custom_tls__init(git_custom_tls *tls) | ||
{ | ||
if (git_rwlock_rdlock(&git__custom_tls.lock) < 0) { | ||
git_error_set(GIT_ERROR_OS, "failed to lock custom thread local storage"); | ||
return -1; | ||
} | ||
|
||
/* We try to ensure that all 3 callbacks must be set or not set. | ||
* It would not make sense to have a subset of the callbacks set. | ||
*/ | ||
if (!git__custom_tls.retrieve_storage_for_internal_thread) { | ||
tls->set_storage_on_thread = NULL; | ||
tls->teardown_storage_on_thread = NULL; | ||
tls->payload = NULL; | ||
} else { | ||
/* We set these on a struct so that if for whatever reason the opts are changed | ||
* at least the opts will remain consistent for any given thread already in | ||
* motion. | ||
*/ | ||
tls->set_storage_on_thread = git__custom_tls.set_storage_on_thread; | ||
tls->teardown_storage_on_thread = git__custom_tls.teardown_storage_on_thread; | ||
tls->payload = git__custom_tls.retrieve_storage_for_internal_thread(); | ||
} | ||
|
||
git_rwlock_rdunlock(&git__custom_tls.lock); | ||
return 0; | ||
} | ||
|
||
#else | ||
|
||
int git_custom_tls__global_init(void) | ||
{ | ||
return 0; | ||
} | ||
|
||
int git_custom_tls_set_callbacks( | ||
git_retrieve_tls_for_internal_thread_cb retrieve_storage_for_internal_thread, | ||
git_set_tls_on_internal_thread_cb set_storage_on_thread, | ||
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread) | ||
{ | ||
return 0; | ||
} | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* 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_custom_tls_h__ | ||
#define INCLUDE_custom_tls_h__ | ||
|
||
#include "common.h" | ||
#include "git2/sys/custom_tls.h" | ||
|
||
int git_custom_tls__global_init(void); | ||
|
||
#ifdef GIT_THREADS | ||
|
||
typedef struct { | ||
git_set_tls_on_internal_thread_cb set_storage_on_thread; | ||
|
||
git_teardown_tls_on_internal_thread_cb teardown_storage_on_thread; | ||
|
||
/** | ||
* payload should be set on the thread that is spawning the child thread. | ||
* This payload will be passed to set_storage_on_thread | ||
*/ | ||
void *payload; | ||
} git_custom_tls; | ||
|
||
int git_custom_tls__init(git_custom_tls *tls); | ||
|
||
#endif | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -370,13 +370,40 @@ int git_ignore__for_path( | |
return error; | ||
} | ||
|
||
char * concat_path_ign_file(git_buf *path) | ||
{ | ||
char *path_file = NULL; | ||
|
||
path_file = git__malloc(path->size + 11); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better replace 11 with a definition, like #define GIT_IGNORE_FILE_NAMELENGTH 11 |
||
if (path_file == NULL) | ||
return NULL; | ||
|
||
memcpy(path_file, path->ptr, path->size); | ||
memcpy(path_file + path->size, GIT_IGNORE_FILE, 11); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also here, replace 11 with GIT_IGNORE_FILE_NAMELENGTH |
||
|
||
return path_file; | ||
} | ||
|
||
int git_ignore__push_dir(git_ignores *ign, const char *dir) | ||
{ | ||
char *file_ign = NULL; | ||
struct stat st; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing asserts:
|
||
if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0) | ||
return -1; | ||
|
||
ign->depth++; | ||
|
||
// Do not add the .gitignore file if it not existing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe extend a bit the explanation in this comment, like: |
||
file_ign = concat_path_ign_file(&ign->dir); | ||
if (file_ign == NULL) | ||
return -1; | ||
if (p_stat(file_ign, &st) < 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested checking
But the times went up a bit. At the moment caller of |
||
free(file_ign); | ||
return 0; | ||
} | ||
free(file_ign); | ||
Comment on lines
+402
to
+405
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we reserve with |
||
|
||
return push_ignore_file( | ||
ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1012,6 +1012,7 @@ typedef struct { | |
size_t path_len; | ||
iterator_pathlist_search_t match; | ||
git_oid id; | ||
char *basename; | ||
char path[GIT_FLEX_ARRAY]; | ||
} filesystem_iterator_entry; | ||
|
||
|
@@ -1073,15 +1074,15 @@ static int filesystem_iterator_entry_cmp(const void *_a, const void *_b) | |
const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a; | ||
const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b; | ||
|
||
return git__strcmp(a->path, b->path); | ||
return git__strcmp(a->basename, b->basename); | ||
} | ||
|
||
static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b) | ||
{ | ||
const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a; | ||
const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b; | ||
|
||
return git__strcasecmp(a->path, b->path); | ||
return git__strcasecmp(a->basename, b->basename); | ||
} | ||
|
||
#define FILESYSTEM_MAX_DEPTH 100 | ||
|
@@ -1136,14 +1137,14 @@ static int filesystem_iterator_is_submodule( | |
|
||
static void filesystem_iterator_frame_push_ignores( | ||
filesystem_iterator *iter, | ||
filesystem_iterator_entry *frame_entry, | ||
filesystem_iterator_frame *previous_frame, | ||
filesystem_iterator_frame *new_frame) | ||
{ | ||
filesystem_iterator_frame *previous_frame; | ||
const char *path = frame_entry ? frame_entry->path : ""; | ||
|
||
if (!iterator__honor_ignores(&iter->base)) | ||
return; | ||
const char* path = ""; | ||
if (previous_frame) { | ||
path = filesystem_iterator_current_entry(previous_frame)->path; | ||
assert(path && *path); | ||
} | ||
|
||
if (git_ignore__lookup(&new_frame->is_ignored, | ||
&iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) { | ||
|
@@ -1152,27 +1153,32 @@ static void filesystem_iterator_frame_push_ignores( | |
} | ||
|
||
/* if this is not the top level directory... */ | ||
if (frame_entry) { | ||
if (previous_frame) { | ||
const char *relative_path; | ||
|
||
previous_frame = filesystem_iterator_parent_frame(iter); | ||
|
||
/* push new ignores for files in this directory */ | ||
relative_path = frame_entry->path + previous_frame->path_len; | ||
relative_path = path + previous_frame->path_len; | ||
|
||
/* inherit ignored from parent if no rule specified */ | ||
if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND) | ||
new_frame->is_ignored = previous_frame->is_ignored; | ||
|
||
git_ignore__push_dir(&iter->ignores, relative_path); | ||
} | ||
assert((size_t)iter->ignores.depth <= iter->frames.size); | ||
} | ||
|
||
static void filesystem_iterator_frame_pop_ignores( | ||
filesystem_iterator *iter) | ||
{ | ||
if (iterator__honor_ignores(&iter->base)) | ||
git_ignore__pop_dir(&iter->ignores); | ||
if (iterator__honor_ignores(&iter->base)) { | ||
assert((size_t)iter->ignores.depth <= iter->frames.size + 1); | ||
if ((size_t)iter->ignores.depth == iter->frames.size + 1) { | ||
git_ignore__pop_dir(&iter->ignores); | ||
} | ||
} | ||
} | ||
|
||
GIT_INLINE(bool) filesystem_iterator_examine_path( | ||
|
@@ -1292,6 +1298,7 @@ static int filesystem_iterator_entry_init( | |
filesystem_iterator_frame *frame, | ||
const char *path, | ||
size_t path_len, | ||
size_t basename_len, | ||
struct stat *statbuf, | ||
iterator_pathlist_search_t pathlist_match) | ||
{ | ||
|
@@ -1315,6 +1322,7 @@ static int filesystem_iterator_entry_init( | |
entry->match = pathlist_match; | ||
memcpy(entry->path, path, path_len); | ||
memcpy(&entry->st, statbuf, sizeof(struct stat)); | ||
entry->basename = entry->path + (path_len - basename_len); | ||
|
||
/* Suffix directory paths with a '/' */ | ||
if (S_ISDIR(entry->st.st_mode)) | ||
|
@@ -1342,7 +1350,9 @@ static int filesystem_iterator_frame_push( | |
filesystem_iterator_entry *entry; | ||
struct stat statbuf; | ||
size_t path_len; | ||
size_t basename_len; | ||
int error; | ||
bool submodule = false; | ||
|
||
if (iter->frames.size == FILESYSTEM_MAX_DEPTH) { | ||
git_error_set(GIT_ERROR_REPOSITORY, | ||
|
@@ -1354,6 +1364,7 @@ static int filesystem_iterator_frame_push( | |
GIT_ERROR_CHECK_ALLOC(new_frame); | ||
|
||
memset(new_frame, 0, sizeof(filesystem_iterator_frame)); | ||
new_frame->is_ignored = GIT_IGNORE_UNCHECKED; | ||
|
||
if (frame_entry) | ||
git_buf_joinpath(&root, iter->root, frame_entry->path); | ||
|
@@ -1384,9 +1395,6 @@ static int filesystem_iterator_frame_push( | |
if ((error = git_pool_init(&new_frame->entry_pool, 1)) < 0) | ||
goto done; | ||
|
||
/* check if this directory is ignored */ | ||
filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame); | ||
|
||
while ((error = git_path_diriter_next(&diriter)) == 0) { | ||
iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL; | ||
bool dir_expected = false; | ||
|
@@ -1410,52 +1418,62 @@ static int filesystem_iterator_frame_push( | |
iter, frame_entry, path, path_len)) | ||
continue; | ||
|
||
if (filesystem_iterator_is_dot_git(iter, path, path_len)) | ||
continue; | ||
|
||
/* TODO: don't need to stat if assume unchanged for this path and | ||
* we have an index, we can just copy the data out of it. | ||
*/ | ||
|
||
if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) { | ||
/* file was removed between readdir and lstat */ | ||
if (error == GIT_ENOTFOUND) | ||
continue; | ||
|
||
/* treat the file as unreadable */ | ||
memset(&statbuf, 0, sizeof(statbuf)); | ||
statbuf.st_mode = GIT_FILEMODE_UNREADABLE; | ||
|
||
error = 0; | ||
} | ||
if (diriter.d_type == DT_DIR && | ||
!(error = filesystem_iterator_is_submodule(&submodule, iter, path, path_len)) && | ||
!submodule) { | ||
// It's a directory, no need to lstat it. | ||
statbuf.st_mode = S_IFDIR; | ||
} else if (error < 0) { | ||
goto done; | ||
} else { | ||
if ((error = git_path_diriter_stat(&statbuf, &diriter)) < 0) { | ||
/* file was removed between readdir and lstat */ | ||
if (error == GIT_ENOTFOUND) | ||
continue; | ||
|
||
iter->base.stat_calls++; | ||
/* treat the file as unreadable */ | ||
memset(&statbuf, 0, sizeof(statbuf)); | ||
statbuf.st_mode = GIT_FILEMODE_UNREADABLE; | ||
|
||
/* Ignore wacky things in the filesystem */ | ||
if (!S_ISDIR(statbuf.st_mode) && | ||
!S_ISREG(statbuf.st_mode) && | ||
!S_ISLNK(statbuf.st_mode) && | ||
statbuf.st_mode != GIT_FILEMODE_UNREADABLE) | ||
continue; | ||
error = 0; | ||
} | ||
|
||
if (filesystem_iterator_is_dot_git(iter, path, path_len)) | ||
continue; | ||
iter->base.stat_calls++; | ||
|
||
/* convert submodules to GITLINK and remove trailing slashes */ | ||
if (S_ISDIR(statbuf.st_mode)) { | ||
bool submodule = false; | ||
/* Ignore wacky things in the filesystem */ | ||
if (!S_ISDIR(statbuf.st_mode) && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check again if it's a directory? Can a submodule not be a directory? |
||
!S_ISREG(statbuf.st_mode) && | ||
!S_ISLNK(statbuf.st_mode) && | ||
statbuf.st_mode != GIT_FILEMODE_UNREADABLE) | ||
continue; | ||
|
||
if ((error = filesystem_iterator_is_submodule(&submodule, | ||
iter, path, path_len)) < 0) | ||
goto done; | ||
/* Ensure that the pathlist entry lines up with what we expected */ | ||
if (dir_expected && !S_ISDIR(statbuf.st_mode)) | ||
continue; | ||
|
||
if (submodule) | ||
statbuf.st_mode = GIT_FILEMODE_COMMIT; | ||
/* convert submodules to GITLINK and remove trailing slashes */ | ||
if (S_ISDIR(statbuf.st_mode)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check again if it's a directory? Can a submodule not be a directory? |
||
if (!submodule) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. matching end of |
||
if ((error = filesystem_iterator_is_submodule(&submodule, | ||
iter, path, path_len)) < 0) | ||
goto done; | ||
} | ||
if (submodule) | ||
statbuf.st_mode = GIT_FILEMODE_COMMIT; | ||
} | ||
} | ||
|
||
/* Ensure that the pathlist entry lines up with what we expected */ | ||
else if (dir_expected) | ||
continue; | ||
basename_len = diriter.path.size - diriter.parent_len - 1; | ||
|
||
if ((error = filesystem_iterator_entry_init(&entry, | ||
iter, new_frame, path, path_len, &statbuf, pathlist_match)) < 0) | ||
iter, new_frame, path, path_len, basename_len, &statbuf, pathlist_match)) < 0) | ||
goto done; | ||
|
||
git_vector_insert(&new_frame->entries, entry); | ||
|
@@ -1708,9 +1726,21 @@ GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry) | |
|
||
static void filesystem_iterator_update_ignored(filesystem_iterator *iter) | ||
{ | ||
size_t i; | ||
filesystem_iterator_frame *frame; | ||
git_dir_flag dir_flag = entry_dir_flag(&iter->entry); | ||
|
||
for (i = iter->frames.size; | ||
i && iter->frames.ptr[i - 1].is_ignored == GIT_IGNORE_UNCHECKED; | ||
--i) { | ||
// empty body | ||
} | ||
|
||
for (; i != iter->frames.size; ++i) { | ||
frame = iter->frames.ptr + i; | ||
filesystem_iterator_frame_push_ignores(iter, i ? frame - 1 : NULL, frame); | ||
} | ||
|
||
if (git_ignore__lookup(&iter->current_is_ignored, | ||
&iter->ignores, iter->entry.path, dir_flag) < 0) { | ||
git_error_clear(); | ||
|
@@ -2027,6 +2057,8 @@ typedef struct { | |
git_buf tree_buf; | ||
bool skip_tree; | ||
|
||
size_t end_idx; | ||
|
||
const git_index_entry *entry; | ||
} index_iterator; | ||
|
||
|
@@ -2106,6 +2138,31 @@ static int index_iterator_advance( | |
|
||
iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS; | ||
|
||
if (iter->end_idx == (size_t)-1) { | ||
git_index_entry key; | ||
|
||
if (iter->base.start_len && !iterator__include_trees(&iter->base)) { | ||
git_buf buf = GIT_BUF_INIT; | ||
memset(&key, 0, sizeof(key)); | ||
if (iter->base.start[iter->base.start_len - 1] == '/') { | ||
git_buf_puts(&buf, iter->base.start); | ||
buf.ptr[buf.size - 1] = 0; | ||
key.path = buf.ptr; | ||
} else { | ||
key.path = iter->base.start; | ||
} | ||
git_vector_bsearch(&iter->next_idx, &iter->entries, &key); | ||
git_buf_dispose(&buf); | ||
} | ||
|
||
if (iter->base.end_len) { | ||
key.path = iter->base.end; | ||
if (!git_vector_bsearch(&iter->end_idx, &iter->entries, &key)) ++iter->end_idx; | ||
} else { | ||
iter->end_idx = iter->entries.length; | ||
} | ||
} | ||
|
||
while (true) { | ||
if (iter->next_idx >= iter->entries.length) { | ||
error = GIT_ITEROVER; | ||
|
@@ -2126,7 +2183,7 @@ static int index_iterator_advance( | |
continue; | ||
} | ||
|
||
if (iterator_has_ended(&iter->base, entry->path)) { | ||
if (iter->next_idx >= iter->end_idx && iterator_has_ended(&iter->base, entry->path)) { | ||
error = GIT_ITEROVER; | ||
break; | ||
} | ||
|
@@ -2214,6 +2271,7 @@ static int index_iterator_init(index_iterator *iter) | |
iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; | ||
iter->next_idx = 0; | ||
iter->skip_tree = false; | ||
iter->end_idx = -1; | ||
return 0; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -482,6 +482,7 @@ struct git_path_diriter | |
size_t parent_len; | ||
|
||
unsigned int flags; | ||
unsigned char d_type; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems |
||
|
||
DIR *dir; | ||
|
||
|
@@ -722,16 +723,36 @@ int git_path_normalize_slashes(git_buf *out, const char *path); | |
|
||
bool git_path_supports_symlinks(const char *dir); | ||
|
||
typedef enum { | ||
GIT_PATH_MOCK_OWNER_NONE = 0, /* do filesystem lookups as normal */ | ||
GIT_PATH_MOCK_OWNER_SYSTEM = 1, | ||
GIT_PATH_MOCK_OWNER_CURRENT_USER = 2, | ||
GIT_PATH_MOCK_OWNER_OTHER = 3 | ||
} git_path__mock_owner_t; | ||
|
||
/** | ||
* Sets the mock ownership for files; subsequent calls to | ||
* `git_path_owner_is_*` functions will return this data until cleared | ||
* with `GIT_PATH_MOCK_OWNER_NONE`. | ||
*/ | ||
void git_path__set_owner(git_path__mock_owner_t owner); | ||
|
||
/** | ||
* Validate a system file's ownership | ||
* | ||
* Verify that the file in question is owned by an administrator or system | ||
* account, or at least by the current user. | ||
* | ||
* This function returns 0 if successful. If the file is not owned by any of | ||
* these, or any other if there have been problems determining the file | ||
* ownership, it returns -1. | ||
* account. | ||
*/ | ||
int git_path_owner_is_system(bool *out, const char *path); | ||
|
||
/** | ||
* Verify that the file in question is owned by the current user; | ||
*/ | ||
|
||
int git_path_owner_is_current_user(bool *out, const char *path); | ||
|
||
/** | ||
* Verify that the file in question is owned by an administrator or system | ||
* account _or_ the current user; | ||
*/ | ||
int git_path_validate_system_file_ownership(const char *path); | ||
int git_path_owner_is_system_or_current_user(bool *out, const char *path); | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
#include "pthread.h" | ||
#include "thread.h" | ||
#include "runtime.h" | ||
|
||
git_tlsdata_key thread_handle; | ||
|
||
static void git_threads_global_shutdown(void) { | ||
git_tlsdata_dispose(thread_handle); | ||
} | ||
|
||
int git_threads_global_init(void) { | ||
int error = git_tlsdata_init(&thread_handle, NULL); | ||
if (error != 0) { | ||
return error; | ||
} | ||
|
||
return git_runtime_shutdown_register(git_threads_global_shutdown); | ||
} | ||
|
||
static void *git_unix__threadproc(void *arg) | ||
{ | ||
void *result; | ||
int error; | ||
git_thread *thread = arg; | ||
|
||
error = git_tlsdata_set(thread_handle, thread); | ||
if (error != 0) { | ||
return NULL; | ||
} | ||
|
||
if (thread->tls.set_storage_on_thread) { | ||
thread->tls.set_storage_on_thread(thread->tls.payload); | ||
} | ||
|
||
result = thread->proc(thread->param); | ||
|
||
if (thread->tls.teardown_storage_on_thread) { | ||
thread->tls.teardown_storage_on_thread(); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
int git_thread_create( | ||
git_thread *thread, | ||
void *(*start_routine)(void*), | ||
void *arg) | ||
{ | ||
|
||
thread->proc = start_routine; | ||
thread->param = arg; | ||
if (git_custom_tls__init(&thread->tls) < 0) | ||
return -1; | ||
|
||
return pthread_create(&thread->thread, NULL, git_unix__threadproc, thread); | ||
} | ||
|
||
void git_thread_exit(void *value) | ||
{ | ||
git_thread *thread = git_tlsdata_get(thread_handle); | ||
|
||
if (thread && thread->tls.teardown_storage_on_thread) | ||
thread->tls.teardown_storage_on_thread(); | ||
|
||
return pthread_exit(value); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,48 @@ | ||
|
||
#define FETCH_HEAD_WILDCARD_DATA_LOCAL \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" | ||
|
||
#define FETCH_HEAD_WILDCARD_DATA \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n" | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of https://github.com/libgit2/TestGitRepository\n" | ||
|
||
#define FETCH_HEAD_WILDCARD_DATA2 \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \ | ||
|
||
#define FETCH_HEAD_NO_MERGE_DATA \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n" | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of https://github.com/libgit2/TestGitRepository\n" | ||
|
||
#define FETCH_HEAD_NO_MERGE_DATA2 \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \ | ||
|
||
#define FETCH_HEAD_NO_MERGE_DATA3 \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \ | ||
"8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" \ | ||
|
||
#define FETCH_HEAD_EXPLICIT_DATA \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" | ||
|
||
#define FETCH_HEAD_QUOTE_DATA \ | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first's-merge' of git://github.com/libgit2/TestGitRepository\n" | ||
"0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first's-merge' of https://github.com/libgit2/TestGitRepository\n" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
#include "clar_libgit2.h" | ||
|
||
#include "thread_helpers.h" | ||
#include "alloc.h" | ||
#include "common.h" | ||
#include "git2/sys/custom_tls.h" | ||
|
||
static int *test[2] = { NULL, NULL }; | ||
static int num_threads_spawned = 0; | ||
|
||
#if defined(GIT_THREADS) && defined(GIT_WIN32) | ||
static DWORD _fls_index; | ||
|
||
int init_thread_local_storage(void) | ||
{ | ||
if ((_fls_index = FlsAlloc(NULL)) == FLS_OUT_OF_INDEXES) | ||
return -1; | ||
|
||
return 0; | ||
} | ||
|
||
void cleanup_thread_local_storage(void) | ||
{ | ||
FlsFree(_fls_index); | ||
} | ||
|
||
void *init_local_storage(void) { | ||
test[num_threads_spawned] = git__calloc(1, sizeof(int)); | ||
return test[num_threads_spawned++]; | ||
} | ||
|
||
void init_tls(void *payload) { | ||
int *i = payload; | ||
(*i)++; | ||
FlsSetValue(_fls_index, i); | ||
} | ||
|
||
void teardown_tls(void) { | ||
int *i = FlsGetValue(_fls_index); | ||
(*i)++; | ||
} | ||
|
||
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS) | ||
static pthread_key_t _tls_key; | ||
|
||
int init_thread_local_storage(void) | ||
{ | ||
return pthread_key_create(&_tls_key, NULL); | ||
} | ||
|
||
void cleanup_thread_local_storage(void) | ||
{ | ||
pthread_key_delete(_tls_key); | ||
} | ||
|
||
void *init_local_storage(void) { | ||
test[num_threads_spawned] = git__calloc(1, sizeof(int)); | ||
return test[num_threads_spawned++]; | ||
} | ||
|
||
void init_tls(void *payload) { | ||
int *i = payload; | ||
(*i)++; | ||
pthread_setspecific(_tls_key, i); | ||
} | ||
|
||
void teardown_tls(void) { | ||
int *i = pthread_getspecific(_tls_key); | ||
(*i)++; | ||
} | ||
|
||
#endif | ||
|
||
void test_threads_custom_tls__initialize(void) | ||
{ | ||
#ifdef GIT_THREADS | ||
cl_git_pass(init_thread_local_storage()); | ||
cl_git_pass(git_custom_tls_set_callbacks(init_local_storage, init_tls, teardown_tls)); | ||
test[0] = NULL; | ||
test[1] = NULL; | ||
num_threads_spawned = 0; | ||
#endif | ||
} | ||
|
||
void test_threads_custom_tls__cleanup(void) | ||
{ | ||
#ifdef GIT_THREADS | ||
cleanup_thread_local_storage(); | ||
git_custom_tls_set_callbacks(NULL, NULL, NULL); | ||
|
||
git__free(test[0]); | ||
test[0] = NULL; | ||
|
||
git__free(test[1]); | ||
test[1] = NULL; | ||
#endif | ||
} | ||
|
||
#ifdef GIT_THREADS | ||
static void *return_normally(void *param) | ||
{ | ||
return param; | ||
} | ||
#endif | ||
|
||
void test_threads_custom_tls__multiple_clean_exit(void) | ||
{ | ||
#ifndef GIT_THREADS | ||
clar__skip(); | ||
#else | ||
git_thread thread1, thread2; | ||
void *result; | ||
|
||
cl_git_pass(git_thread_create(&thread1, return_normally, (void *)424242)); | ||
cl_git_pass(git_thread_create(&thread2, return_normally, (void *)232323)); | ||
|
||
cl_git_pass(git_thread_join(&thread1, &result)); | ||
cl_assert_equal_sz(424242, (size_t)result); | ||
cl_git_pass(git_thread_join(&thread2, &result)); | ||
cl_assert_equal_sz(232323, (size_t)result); | ||
|
||
cl_assert_equal_i(2, *(test[0])); | ||
cl_assert_equal_i(2, *(test[1])); | ||
#endif | ||
} | ||
|
||
#ifdef GIT_THREADS | ||
static void *return_early(void *param) | ||
{ | ||
git_thread_exit(param); | ||
assert(false); | ||
return param; | ||
} | ||
#endif | ||
|
||
void test_threads_custom_tls__multiple_threads_use_exit(void) | ||
{ | ||
#ifndef GIT_THREADS | ||
clar__skip(); | ||
#else | ||
git_thread thread1, thread2; | ||
void *result; | ||
|
||
cl_git_pass(git_thread_create(&thread1, return_early, (void *)424242)); | ||
cl_git_pass(git_thread_create(&thread2, return_early, (void *)232323)); | ||
|
||
cl_git_pass(git_thread_join(&thread1, &result)); | ||
cl_assert_equal_sz(424242, (size_t)result); | ||
cl_git_pass(git_thread_join(&thread2, &result)); | ||
cl_assert_equal_sz(232323, (size_t)result); | ||
|
||
cl_assert_equal_i(2, *(test[0])); | ||
cl_assert_equal_i(2, *(test[1])); | ||
#endif | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we are not using
git_buf
andgit_buf_joinpath
(orgit_str
andgit_str_joinpath
after libgit2 1.4) for efficiency?