summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md9
-rw-r--r--CONTRIBUTING.md64
-rw-r--r--PROJECTS.md44
-rw-r--r--include/git2/tree.h6
-rw-r--r--src/filebuf.c2
-rw-r--r--src/indexer.c9
-rw-r--r--src/mwindow.c124
-rw-r--r--src/mwindow.h10
-rw-r--r--src/odb_pack.c9
-rw-r--r--src/pack.c19
-rw-r--r--src/pack.h3
-rw-r--r--src/tree.c115
-rw-r--r--src/tree.h5
-rw-r--r--src/win32/posix_w32.c30
-rw-r--r--tests/object/tree/write.c8
-rw-r--r--tests/pack/sharing.c42
16 files changed, 386 insertions, 113 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..51650ed7c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+v0.21 + 1
+------
+
+* File unlocks are atomic again via rename. Read-only files on Windows are
+ made read-write if necessary.
+
+* Share open packfiles across repositories to share descriptors and mmaps.
+
+* Use a map for the treebuilder, making insertion O(1)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4efe28ed3..8ebb99154 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -22,17 +22,25 @@ Also, feel free to open an
about any concerns you have. We like to use Issues for that so there is an
easily accessible permanent record of the conversation.
+## Libgit2 Versions
+
+The `master` branch is the main branch where development happens.
+Releases are tagged
+(e.g. [v0.21.0](https://github.com/libgit2/libgit2/releases/tag/v0.21.0) )
+and when a critical bug fix needs to be backported, it will be done on a
+`<tag>-maint` maintenance branch.
+
## Reporting Bugs
First, know which version of libgit2 your problem is in and include it in
your bug report. This can either be a tag (e.g.
-[v0.17.0](https://github.com/libgit2/libgit2/tree/v0.17.0) ) or a commit
-SHA (e.g.
+[v0.17.0](https://github.com/libgit2/libgit2/releases/tag/v0.17.0) ) or a
+commit SHA (e.g.
[01be7863](https://github.com/libgit2/libgit2/commit/01be786319238fd6507a08316d1c265c1a89407f)
-). Using [`git describe`](http://git-scm.com/docs/git-describe) is a great
-way to tell us what version you're working with.
+). Using [`git describe`](http://git-scm.com/docs/git-describe) is a
+great way to tell us what version you're working with.
-If you're not running against the latest `development` branch version,
+If you're not running against the latest `master` branch version,
please compile and test against that to avoid re-reporting an issue that's
already been fixed.
@@ -44,25 +52,33 @@ out a way to help you.
## Pull Requests
-Our work flow is a typical GitHub flow, where contributors fork the
-[libgit2 repository](https://github.com/libgit2/libgit2), make their changes
-on branch, and submit a
-[Pull Request](https://help.github.com/articles/using-pull-requests)
-(a.k.a. "PR").
+Our work flow is a [typical GitHub flow](https://guides.github.com/introduction/flow/index.html),
+where contributors fork the [libgit2 repository](https://github.com/libgit2/libgit2),
+make their changes on branch, and submit a
+[Pull Request](https://help.github.com/articles/using-pull-requests) (a.k.a. "PR").
+Pull requests should usually be targeted at the `master` branch.
Life will be a lot easier for you (and us) if you follow this pattern
-(i.e. fork, named branch, submit PR). If you use your fork's `development`
-branch, things can get messy.
+(i.e. fork, named branch, submit PR). If you use your fork's `master`
+branch directly, things can get messy.
+
+Please include a nice description of your changes when you submit your PR;
+if we have to read the whole diff to figure out why you're contributing
+in the first place, you're less likely to get feedback and have your change
+merged in.
+
+If you are starting to work on a particular area, feel free to submit a PR
+that highlights your work in progress (and note in the PR title that it's
+not ready to merge). These early PRs are welcome and will help in getting
+visibility for your fix, allow others to comment early on the changes and
+also let others know that you are currently working on something.
-Please include a nice description of your changes with your PR; if we have
-to read the whole diff to figure out why you're contributing in the first
-place, you're less likely to get feedback and have your change merged in.
+Before wrapping up a PR, you should be sure to:
-If you are working on a particular area then feel free to submit a PR that
-highlights your work in progress (and flag in the PR title that it's not
-ready to merge). This will help in getting visibility for your fix, allow
-others to comment early on the changes and also let others know that you
-are currently working on something.
+* Write tests to cover any functional changes (ideally tests that would
+ have failed before the PR and now pass)
+* Update documentation for any changed public APIs
+* Add to the [`CHANGELOG.md`](CHANGELOG.md) file describing any major changes
## Porting Code From Other Open-Source Projects
@@ -80,10 +96,10 @@ you're porting code *from* to see what you need to do. As a general rule,
MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0
license typically doesn't work due to GPL incompatibility.
-If you are pulling in code from core Git, another project or code you've
-pulled from a forum / Stack Overflow then please flag this in your PR and
-also make sure you've given proper credit to the original author in the
-code snippet.
+If your pull request uses code from core Git, another project, or code
+from a forum / Stack Overflow, then *please* flag this in your PR and make
+sure you've given proper credit to the original author in the code
+snippet.
## Style Guide
diff --git a/PROJECTS.md b/PROJECTS.md
index d17214471..5164d95b6 100644
--- a/PROJECTS.md
+++ b/PROJECTS.md
@@ -10,10 +10,11 @@ ideas that no one is actively working on.
## Before You Start
-Please start by reading the README.md, CONTRIBUTING.md, and CONVENTIONS.md
-files before diving into one of these projects. Those will explain our
-work flow and coding conventions to help ensure that your work will be
-easily integrated into libgit2.
+Please start by reading the [README.md](README.md),
+[CONTRIBUTING.md](CONTRIBUTING.md), and [CONVENTIONS.md](CONVENTIONS.md)
+files before diving into one of these projects. Those explain our work
+flow and coding conventions to help ensure that your work will be easily
+integrated into libgit2.
Next, work through the build instructions and make sure you can clone the
repository, compile it, and run the tests successfully. That will make
@@ -27,7 +28,7 @@ These are good small projects to get started with libgit2.
* Look at the `examples/` programs, find an existing one that mirrors a
core Git command and add a missing command-line option. There are many
gaps right now and this helps demonstrate how to use the library. Here
- are some specific ideas:
+ are some specific ideas (though there are many more):
* Fix the `examples/diff.c` implementation of the `-B`
(a.k.a. `--break-rewrites`) command line option to actually look for
the optional `[<n>][/<m>]` configuration values. There is an
@@ -67,19 +68,44 @@ into one of these as a first project for libgit2 - we'd rather get to
know you first by successfully shipping your work on one of the smaller
projects above.
+Some of these projects are broken down into subprojects and/or have
+some incremental steps listed towards the larger goal. Those steps
+might make good smaller projects by themselves.
+
* Port part of the Git test suite to run against the command line emulation
in examples/
+ * Pick a Git command that is emulated in our examples/ area
+ * Extract the Git tests that exercise that command
+ * Convert the tests to call our emulation
+ * These tests could go in examples/tests/...
* Fix symlink support for files in the .git directory (i.e. don't overwrite
the symlinks when writing the file contents back out)
* Implement a 'git describe' like API
* Add hooks API to enumerate and manage hooks (not run them at this point)
+ * Enumeration of available hooks
+ * Lookup API to see which hooks have a script and get the script
+ * Read/write API to load a hook script and write a hook script
+ * Eventually, callback API to invoke a hook callback when libgit2
+ executes the action in question
* Isolate logic of ignore evaluation into a standalone API
* Upgrade internal libxdiff code to latest from core Git
-* Add a hashtable lookup for files in the index instead of binary search
- every time
+* Improve index internals with hashtable lookup for files instead of
+ using binary search every time
* Make the index write the cache out to disk (with tests to gain
confidence that the caching invalidation works correctly)
-* Have the tree builder use a hash table when building instead of a
- list.
+* Tree builder improvements:
+ * Use a hash table when building instead of a list
+ * Extend to allow building a tree hierarchy
* Move the tagopt mechanism to the newer git 1.9 interpretation of
--tags [#2120](https://github.com/libgit2/libgit2/issues/2120)
+* Apply-patch API
+* Add a patch editing API to enable "git add -p" type operations
+* Textconv API to filter binary data before generating diffs (something
+ like the current Filter API, probably).
+* Performance profiling and improvement
+* Build in handling of "empty tree" and "empty blob" SHAs
+* Support "git replace" ref replacements
+* Include conflicts in diff results and in status
+ * GIT_DELTA_CONFLICT for items in conflict (with multiple files)
+ * Appropriate flags for status
+* Support sparse checkout (i.e. "core.sparsecheckout" and ".git/info/sparse-checkout")
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 56922d40b..42b68193e 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -301,8 +301,10 @@ GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(
* If an entry named `filename` already exists, its attributes
* will be updated with the given ones.
*
- * The optional pointer `out` can be used to retrieve a pointer to
- * the newly created/updated entry. Pass NULL if you do not need it.
+ * The optional pointer `out` can be used to retrieve a pointer to the
+ * newly created/updated entry. Pass NULL if you do not need it. The
+ * pointer may not be valid past the next operation in this
+ * builder. Duplicate the entry if you want to keep it.
*
* No attempt is being made to ensure that the provided oid points
* to an existing git object in the object database, nor that the
diff --git a/src/filebuf.c b/src/filebuf.c
index d23bcc11c..25f6e52ef 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -334,8 +334,6 @@ int git_filebuf_commit(git_filebuf *file)
file->fd = -1;
- p_unlink(file->path_original);
-
if (p_rename(file->path_lock, file->path_original) < 0) {
giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
goto on_error;
diff --git a/src/indexer.c b/src/indexer.c
index 25c3d0537..eb8b23e92 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -18,6 +18,8 @@
#include "oidmap.h"
#include "zstream.h"
+extern git_mutex git__mwindow_mutex;
+
#define UINT31_MAX (0x7FFFFFFF)
struct entry {
@@ -1044,6 +1046,11 @@ void git_indexer_free(git_indexer *idx)
}
git_vector_free_deep(&idx->deltas);
- git_packfile_free(idx->pack);
+
+ if (!git_mutex_lock(&git__mwindow_mutex)) {
+ git_packfile_free(idx->pack);
+ git_mutex_unlock(&git__mwindow_mutex);
+ }
+
git__free(idx);
}
diff --git a/src/mwindow.c b/src/mwindow.c
index 7e5fcdfbc..0d6535056 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -11,6 +11,10 @@
#include "fileops.h"
#include "map.h"
#include "global.h"
+#include "strmap.h"
+#include "pack.h"
+
+GIT__USE_STRMAP;
#define DEFAULT_WINDOW_SIZE \
(sizeof(void*) >= 8 \
@@ -26,20 +30,126 @@ size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
/* Whenever you want to read or modify this, grab git__mwindow_mutex */
static git_mwindow_ctl mem_ctl;
-/*
- * Free all the windows in a sequence, typically because we're done
- * with the file
+/* Global list of mwindow files, to open packs once across repos */
+git_strmap *git__pack_cache = NULL;
+
+/**
+ * Run under mwindow lock
*/
-void git_mwindow_free_all(git_mwindow_file *mwf)
+int git_mwindow_files_init(void)
{
- git_mwindow_ctl *ctl = &mem_ctl;
- size_t i;
+ if (git__pack_cache)
+ return 0;
+
+ return git_strmap_alloc(&git__pack_cache);
+}
+
+void git_mwindow_files_free(void)
+{
+ git_strmap *tmp = git__pack_cache;
+
+ git__pack_cache = NULL;
+ git_strmap_free(tmp);
+}
+
+int git_mwindow_get_pack(struct git_pack_file **out, const char *path)
+{
+ int error;
+ char *packname;
+ git_strmap_iter pos;
+ struct git_pack_file *pack;
+
+ if ((error = git_packfile__name(&packname, path)) < 0)
+ return error;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0)
+ return -1;
+
+ if (git_mwindow_files_init() < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return -1;
+ }
+
+ pos = git_strmap_lookup_index(git__pack_cache, packname);
+ git__free(packname);
+
+ if (git_strmap_valid_index(git__pack_cache, pos)) {
+ pack = git_strmap_value_at(git__pack_cache, pos);
+ git_atomic_inc(&pack->refcount);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ *out = pack;
+ return 0;
+ }
+
+ /* If we didn't find it, we need to create it */
+ if ((error = git_packfile_alloc(&pack, path)) < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return error;
+ }
+
+ git_atomic_inc(&pack->refcount);
+
+ git_strmap_insert(git__pack_cache, pack->pack_name, pack, error);
+ git_mutex_unlock(&git__mwindow_mutex);
+
+ if (error < 0)
+ return -1;
+ *out = pack;
+ return 0;
+}
+
+int git_mwindow_put_pack(struct git_pack_file *pack)
+{
+ int count;
+ git_strmap_iter pos;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0)
+ return -1;
+
+ if (git_mwindow_files_init() < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return -1;
+ }
+
+ pos = git_strmap_lookup_index(git__pack_cache, pack->pack_name);
+ if (!git_strmap_valid_index(git__pack_cache, pos)) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return GIT_ENOTFOUND;
+ }
+
+ count = git_atomic_dec(&pack->refcount);
+ if (count == 0) {
+ git_strmap_delete_at(git__pack_cache, pos);
+ git_packfile_free(pack);
+ }
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ return 0;
+}
+
+void git_mwindow_free_all(git_mwindow_file *mwf)
+{
if (git_mutex_lock(&git__mwindow_mutex)) {
giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
return;
}
+ git_mwindow_free_all_locked(mwf);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+}
+
+/*
+ * Free all the windows in a sequence, typically because we're done
+ * with the file
+ */
+void git_mwindow_free_all_locked(git_mwindow_file *mwf)
+{
+ git_mwindow_ctl *ctl = &mem_ctl;
+ size_t i;
+
/*
* Remove these windows from the global list
*/
@@ -67,8 +177,6 @@ void git_mwindow_free_all(git_mwindow_file *mwf)
mwf->windows = w->next;
git__free(w);
}
-
- git_mutex_unlock(&git__mwindow_mutex);
}
/*
diff --git a/src/mwindow.h b/src/mwindow.h
index 0018ebbf0..57fabae70 100644
--- a/src/mwindow.h
+++ b/src/mwindow.h
@@ -36,10 +36,18 @@ typedef struct git_mwindow_ctl {
} git_mwindow_ctl;
int git_mwindow_contains(git_mwindow *win, git_off_t offset);
-void git_mwindow_free_all(git_mwindow_file *mwf);
+void git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
+void git_mwindow_free_all_locked(git_mwindow_file *mwf); /* run under lock */
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);
int git_mwindow_file_register(git_mwindow_file *mwf);
void git_mwindow_file_deregister(git_mwindow_file *mwf);
void git_mwindow_close(git_mwindow **w_cursor);
+int git_mwindow_files_init(void);
+void git_mwindow_files_free(void);
+
+struct git_pack_file; /* just declaration to avoid cyclical includes */
+int git_mwindow_get_pack(struct git_pack_file **out, const char *path);
+int git_mwindow_put_pack(struct git_pack_file *pack);
+
#endif
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 3750da37f..1757cf920 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -210,7 +210,7 @@ static int packfile_load__cb(void *data, git_buf *path)
return 0;
}
- error = git_packfile_alloc(&pack, path->ptr);
+ error = git_mwindow_get_pack(&pack, path->ptr);
/* ignore missing .pack file as git does */
if (error == GIT_ENOTFOUND) {
@@ -605,7 +605,7 @@ static void pack_backend__free(git_odb_backend *_backend)
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i);
- git_packfile_free(p);
+ git_mwindow_put_pack(p);
}
git_vector_free(&backend->packs);
@@ -647,7 +647,7 @@ int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
if (pack_backend__alloc(&backend, 1) < 0)
return -1;
- if (git_packfile_alloc(&packfile, idx) < 0 ||
+ if (git_mwindow_get_pack(&packfile, idx) < 0 ||
git_vector_insert(&backend->packs, packfile) < 0)
{
pack_backend__free((git_odb_backend *)backend);
@@ -664,6 +664,9 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
struct pack_backend *backend = NULL;
git_buf path = GIT_BUF_INIT;
+ if (git_mwindow_files_init() < 0)
+ return -1;
+
if (pack_backend__alloc(&backend, 8) < 0)
return -1;
diff --git a/src/pack.c b/src/pack.c
index ace7abb58..767efb6c3 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -968,7 +968,7 @@ void git_packfile_free(struct git_pack_file *p)
cache_free(&p->bases);
- git_mwindow_free_all(&p->mwf);
+ git_mwindow_free_all_locked(&p->mwf);
if (p->mwf.fd >= 0)
p_close(p->mwf.fd);
@@ -1063,6 +1063,23 @@ cleanup:
return -1;
}
+int git_packfile__name(char **out, const char *path)
+{
+ size_t path_len;
+ git_buf buf = GIT_BUF_INIT;
+
+ path_len = strlen(path);
+
+ if (path_len < strlen(".idx"))
+ return git_odb__error_notfound("invalid packfile path", NULL);
+
+ if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
+ return -1;
+
+ *out = git_buf_detach(&buf);
+ return 0;
+}
+
int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
{
struct stat st;
diff --git a/src/pack.h b/src/pack.h
index 610e70c18..34d37d907 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -90,6 +90,7 @@ struct git_pack_file {
git_mwindow_file mwf;
git_map index_map;
git_mutex lock; /* protect updates to mwf and index_map */
+ git_atomic refcount;
uint32_t num_objects;
uint32_t num_bad_objects;
@@ -123,6 +124,8 @@ typedef struct git_packfile_stream {
size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type);
+int git_packfile__name(char **out, const char *path);
+
int git_packfile_unpack_header(
size_t *size_p,
git_otype *type_p,
diff --git a/src/tree.c b/src/tree.c
index b64efe460..e0e2dbebf 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -17,6 +17,8 @@
#define DEFAULT_TREE_SIZE 16
#define MAX_FILEMODE_BYTES 6
+GIT__USE_STRMAP;
+
static bool valid_filemode(const int filemode)
{
return (filemode == GIT_FILEMODE_TREE
@@ -365,7 +367,8 @@ size_t git_tree_entrycount(const git_tree *tree)
unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
{
assert(bld);
- return (unsigned int)bld->entrycount;
+
+ return git_strmap_num_entries(bld->map);
}
static int tree_error(const char *str, const char *path)
@@ -450,6 +453,7 @@ static int append_entry(
git_filemode_t filemode)
{
git_tree_entry *entry;
+ int error = 0;
if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
@@ -460,12 +464,12 @@ static int append_entry(
git_oid_cpy(&entry->oid, id);
entry->attr = (uint16_t)filemode;
- if (git_vector_insert_sorted(&bld->entries, entry, NULL) < 0) {
- git__free(entry);
+ git_strmap_insert(bld->map, entry->filename, entry, error);
+ if (error < 0) {
+ giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename);
return -1;
}
- bld->entrycount++;
return 0;
}
@@ -610,18 +614,16 @@ int git_tree__write_index(
int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
{
git_treebuilder *bld;
- size_t i, source_entries = DEFAULT_TREE_SIZE;
+ size_t i;
assert(builder_p);
bld = git__calloc(1, sizeof(git_treebuilder));
GITERR_CHECK_ALLOC(bld);
- if (source != NULL)
- source_entries = source->entries.length;
-
- if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)
- goto on_error;
+ if (git_strmap_alloc(&bld->map) < 0) {
+ return -1;
+ }
if (source != NULL) {
git_tree_entry *entry_src;
@@ -651,7 +653,8 @@ int git_treebuilder_insert(
git_filemode_t filemode)
{
git_tree_entry *entry;
- size_t pos;
+ int error;
+ git_strmap_iter pos;
assert(bld && id && filename);
@@ -661,22 +664,20 @@ int git_treebuilder_insert(
if (!valid_entry_name(filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
- if (!tree_key_search(&pos, &bld->entries, filename, strlen(filename))) {
- entry = git_vector_get(&bld->entries, pos);
- if (entry->removed) {
- entry->removed = 0;
- bld->entrycount++;
- }
+ pos = git_strmap_lookup_index(bld->map, filename);
+ if (git_strmap_valid_index(bld->map, pos)) {
+ entry = git_strmap_value_at(bld->map, pos);
} else {
entry = alloc_entry(filename);
GITERR_CHECK_ALLOC(entry);
- if (git_vector_insert_sorted(&bld->entries, entry, NULL) < 0) {
- git__free(entry);
+ git_strmap_insert(bld->map, entry->filename, entry, error);
+
+ if (error < 0) {
+ git_tree_entry_free(entry);
+ giterr_set(GITERR_TREE, "failed to insert %s", filename);
return -1;
}
-
- bld->entrycount++;
}
git_oid_cpy(&entry->oid, id);
@@ -690,17 +691,14 @@ int git_treebuilder_insert(
static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
{
- size_t idx;
- git_tree_entry *entry;
+ git_tree_entry *entry = NULL;
+ git_strmap_iter pos;
assert(bld && filename);
- if (tree_key_search(&idx, &bld->entries, filename, strlen(filename)) < 0)
- return NULL;
-
- entry = git_vector_get(&bld->entries, idx);
- if (entry->removed)
- return NULL;
+ pos = git_strmap_lookup_index(bld->map, filename);
+ if (git_strmap_valid_index(bld->map, pos))
+ entry = git_strmap_value_at(bld->map, pos);
return entry;
}
@@ -712,35 +710,44 @@ const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *file
int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
{
- git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
+ git_tree_entry *entry = treebuilder_get(bld, filename);
- if (remove_ptr == NULL || remove_ptr->removed)
+ if (entry == NULL)
return tree_error("Failed to remove entry. File isn't in the tree", filename);
- remove_ptr->removed = 1;
- bld->entrycount--;
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+
return 0;
}
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
{
int error = 0;
- size_t i;
+ size_t i, entrycount;
git_buf tree = GIT_BUF_INIT;
git_odb *odb;
+ git_tree_entry *entry;
+ git_vector entries;
assert(bld);
- git_vector_sort(&bld->entries);
+ entrycount = git_strmap_num_entries(bld->map);
+ if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
+ return -1;
- /* Grow the buffer beforehand to an estimated size */
- error = git_buf_grow(&tree, bld->entries.length * 72);
+ git_strmap_foreach_value(bld->map, entry, {
+ if (git_vector_insert(&entries, entry) < 0)
+ return -1;
+ });
- for (i = 0; i < bld->entries.length && !error; ++i) {
- git_tree_entry *entry = git_vector_get(&bld->entries, i);
+ git_vector_sort(&entries);
+
+ /* Grow the buffer beforehand to an estimated size */
+ error = git_buf_grow(&tree, entrycount * 72);
- if (entry->removed)
- continue;
+ for (i = 0; i < entries.length && !error; ++i) {
+ git_tree_entry *entry = git_vector_get(&entries, i);
git_buf_printf(&tree, "%o ", entry->attr);
git_buf_put(&tree, entry->filename, entry->filename_len + 1);
@@ -750,6 +757,8 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
error = -1;
}
+ git_vector_free(&entries);
+
if (!error &&
!(error = git_repository_odb__weakptr(&odb, repo)))
error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
@@ -763,31 +772,27 @@ void git_treebuilder_filter(
git_treebuilder_filter_cb filter,
void *payload)
{
- size_t i;
+ const char *filename;
git_tree_entry *entry;
assert(bld && filter);
- git_vector_foreach(&bld->entries, i, entry) {
- if (!entry->removed && filter(entry, payload)) {
- entry->removed = 1;
- bld->entrycount--;
- }
- }
+ git_strmap_foreach(bld->map, filename, entry, {
+ if (filter(entry, payload)) {
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+ }
+ });
}
void git_treebuilder_clear(git_treebuilder *bld)
{
- size_t i;
git_tree_entry *e;
assert(bld);
- git_vector_foreach(&bld->entries, i, e)
- git_tree_entry_free(e);
-
- git_vector_clear(&bld->entries);
- bld->entrycount = 0;
+ git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e));
+ git_strmap_clear(bld->map);
}
void git_treebuilder_free(git_treebuilder *bld)
@@ -796,7 +801,7 @@ void git_treebuilder_free(git_treebuilder *bld)
return;
git_treebuilder_clear(bld);
- git_vector_free(&bld->entries);
+ git_strmap_free(bld->map);
git__free(bld);
}
diff --git a/src/tree.h b/src/tree.h
index f07039a07..5d27eb7c9 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -11,9 +11,9 @@
#include "repository.h"
#include "odb.h"
#include "vector.h"
+#include "strmap.h"
struct git_tree_entry {
- uint16_t removed;
uint16_t attr;
git_oid oid;
size_t filename_len;
@@ -26,8 +26,7 @@ struct git_tree {
};
struct git_treebuilder {
- git_vector entries;
- size_t entrycount; /* vector may contain "removed" entries */
+ git_strmap *map;
};
GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 34938431a..fbadb1c9e 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -591,6 +591,31 @@ int p_access(const char* path, mode_t mode)
return _waccess(buf, mode);
}
+static int ensure_writable(wchar_t *fpath)
+{
+ DWORD attrs;
+
+ attrs = GetFileAttributesW(fpath);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND)
+ return 0;
+
+ giterr_set(GITERR_OS, "failed to get attributes");
+ return -1;
+ }
+
+ if (!(attrs & FILE_ATTRIBUTE_READONLY))
+ return 0;
+
+ attrs &= ~FILE_ATTRIBUTE_READONLY;
+ if (!SetFileAttributesW(fpath, attrs)) {
+ giterr_set(GITERR_OS, "failed to set attributes");
+ return -1;
+ }
+
+ return 0;
+}
+
int p_rename(const char *from, const char *to)
{
git_win32_path wfrom;
@@ -602,12 +627,13 @@ int p_rename(const char *from, const char *to)
if (utf8_to_16_with_errno(wfrom, from) < 0 ||
utf8_to_16_with_errno(wto, to) < 0)
return -1;
-
+
/* wait up to 50ms if file is locked by another thread or process */
rename_tries = 0;
rename_succeeded = 0;
while (rename_tries < 10) {
- if (MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
+ if (ensure_writable(wto) == 0 &&
+ MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
rename_succeeded = 1;
break;
}
diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c
index 45356e807..ddb62e278 100644
--- a/tests/object/tree/write.c
+++ b/tests/object/tree/write.c
@@ -104,6 +104,7 @@ void test_object_tree_write__subtree(void)
void test_object_tree_write__sorted_subtrees(void)
{
git_treebuilder *builder;
+ git_tree *tree;
unsigned int i;
int position_c = -1, position_cake = -1, position_config = -1;
@@ -143,8 +144,9 @@ void test_object_tree_write__sorted_subtrees(void)
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
- for (i = 0; i < builder->entries.length; ++i) {
- git_tree_entry *entry = git_vector_get(&builder->entries, i);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+ for (i = 0; i < git_tree_entrycount(tree); i++) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
if (strcmp(entry->filename, "c") == 0)
position_c = i;
@@ -156,6 +158,8 @@ void test_object_tree_write__sorted_subtrees(void)
position_config = i;
}
+ git_tree_free(tree);
+
cl_assert(position_c != -1);
cl_assert(position_cake != -1);
cl_assert(position_config != -1);
diff --git a/tests/pack/sharing.c b/tests/pack/sharing.c
new file mode 100644
index 000000000..a67d65588
--- /dev/null
+++ b/tests/pack/sharing.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include <git2.h>
+#include "strmap.h"
+#include "mwindow.h"
+#include "pack.h"
+
+extern git_strmap *git__pack_cache;
+
+void test_pack_sharing__open_two_repos(void)
+{
+ git_repository *repo1, *repo2;
+ git_object *obj1, *obj2;
+ git_oid id;
+ git_strmap_iter pos;
+ void *data;
+ int error;
+
+ cl_git_pass(git_repository_open(&repo1, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git")));
+
+ git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ cl_git_pass(git_object_lookup(&obj1, repo1, &id, GIT_OBJ_ANY));
+ cl_git_pass(git_object_lookup(&obj2, repo2, &id, GIT_OBJ_ANY));
+
+ pos = 0;
+ while ((error = git_strmap_next(&data, &pos, git__pack_cache)) == 0) {
+ struct git_pack_file *pack = (struct git_pack_file *) data;
+
+ cl_assert_equal_i(2, pack->refcount.val);
+ }
+
+ cl_assert_equal_i(3, git_strmap_num_entries(git__pack_cache));
+
+ git_object_free(obj1);
+ git_object_free(obj2);
+ git_repository_free(repo1);
+ git_repository_free(repo2);
+
+ /* we don't want to keep the packs open after the repos go away */
+ cl_assert_equal_i(0, git_strmap_num_entries(git__pack_cache));
+}