mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-01 18:47:31 +00:00
Merge remote-tracking branch 'origin/development' into ssh_transport
This commit is contained in:
commit
00e43380a0
15
.mailmap
15
.mailmap
@ -1,3 +1,18 @@
|
||||
Vicent Martí <vicent@github.com> Vicent Marti <tanoku@gmail.com>
|
||||
Vicent Martí <vicent@github.com> Vicent Martí <tanoku@gmail.com>
|
||||
Michael Schubert <schu@schu.io> schu <schu-github@schulog.org>
|
||||
Ben Straub <bs@github.com> Ben Straub <ben@straubnet.net>
|
||||
Ben Straub <bs@github.com> Ben Straub <bstraub@github.com>
|
||||
Carlos Martín Nieto <cmn@dwim.me> <carlos@cmartin.tk>
|
||||
Carlos Martín Nieto <cmn@dwim.me> <cmn@elego.de>
|
||||
nulltoken <emeric.fermas@gmail.com> <emeric.fermas@gmail.com>
|
||||
Scott J. Goldman <scottjg@github.com> <scottjgo@gmail.com>
|
||||
Martin Woodward <martin.woodward@microsoft.com> <martinwo@microsoft.com>
|
||||
Peter Drahoš <drahosp@gmail.com> <drahosp@gmail.com>
|
||||
Adam Roben <adam@github.com> <adam@roben.org>
|
||||
Adam Roben <adam@github.com> <adam@github.com>
|
||||
Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.ca>
|
||||
Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk>
|
||||
Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
|
||||
Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
|
||||
Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
|
||||
|
@ -25,6 +25,10 @@ install:
|
||||
|
||||
# Run the Build script
|
||||
script:
|
||||
- mkdir _temp
|
||||
- git init --bare _temp/test.git
|
||||
- git daemon --listen=localhost --export-all --enable=receive-pack --base-path=_temp _temp 2>/dev/null &
|
||||
- export GITTEST_REMOTE_URL="git://localhost/test.git"
|
||||
- mkdir _build
|
||||
- cd _build
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS
|
||||
|
@ -288,6 +288,15 @@ ELSE()
|
||||
ENDIF()
|
||||
FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c)
|
||||
|
||||
# Determine architecture of the machine
|
||||
IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
ADD_DEFINITIONS(-DGIT_ARCH_64)
|
||||
ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
ADD_DEFINITIONS(-DGIT_ARCH_32)
|
||||
ELSE()
|
||||
message(FATAL_ERROR "Unsupported architecture")
|
||||
ENDIF()
|
||||
|
||||
# Compile and link libgit2
|
||||
ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
|
||||
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
|
||||
@ -296,7 +305,7 @@ TARGET_OS_LIBRARIES(git2)
|
||||
|
||||
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
|
||||
# Win64+MSVC+static libs = linker error
|
||||
IF(MSVC AND NOT BUILD_SHARED_LIBS AND (${CMAKE_SIZEOF_VOID_P} MATCHES "8") )
|
||||
IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
|
||||
SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
|
||||
ENDIF()
|
||||
|
||||
|
13
README.md
13
README.md
@ -19,6 +19,7 @@ release its source code.
|
||||
* Archives: <http://librelist.com/browser/libgit2/>
|
||||
* Website: <http://libgit2.github.com>
|
||||
* API documentation: <http://libgit2.github.com/libgit2>
|
||||
* IRC: #libgit2 on irc.freenode.net.
|
||||
|
||||
What It Can Do
|
||||
==================================
|
||||
@ -139,7 +140,7 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* Parrot Virtual Machine
|
||||
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
|
||||
* Perl
|
||||
* git-xs-pm <https://github.com/ingydotnet/git-xs-pm>
|
||||
* Git-Raw <https://github.com/ghedo/p5-Git-Raw>
|
||||
* PHP
|
||||
* php-git <https://github.com/libgit2/php-git>
|
||||
* Python
|
||||
@ -155,15 +156,7 @@ we can add it to the list.
|
||||
How Can I Contribute?
|
||||
==================================
|
||||
|
||||
Fork libgit2/libgit2 on GitHub, add your improvement, push it to a branch
|
||||
in your fork named for the topic, send a pull request. If you change the
|
||||
API or make other large changes, make a note of it in docs/rel-notes/ in a
|
||||
file named after the next release.
|
||||
|
||||
You can also file bugs or feature requests under the libgit2 project on
|
||||
GitHub, or join us on the mailing list by sending an email to:
|
||||
|
||||
libgit2@librelist.com
|
||||
Check the [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
|
||||
License
|
||||
|
41
docs/merge-df_conflicts.txt
Normal file
41
docs/merge-df_conflicts.txt
Normal file
@ -0,0 +1,41 @@
|
||||
Anc / Our / Thr represent the ancestor / ours / theirs side of a merge
|
||||
from branch "branch" into HEAD. Workdir represents the expected files in
|
||||
the working directory. Index represents the expected files in the index,
|
||||
with stage markers.
|
||||
|
||||
Anc Our Thr Workdir Index
|
||||
1 D D
|
||||
D/F D/F D/F [0]
|
||||
|
||||
2 D D+ D~HEAD (mod/del) D/F [0]
|
||||
D/F D/F D [1]
|
||||
D [2]
|
||||
|
||||
3 D D D/F D/F [0]
|
||||
D/F
|
||||
|
||||
4 D D+ D~branch (mod/del) D/F [0]
|
||||
D/F D/F D [1]
|
||||
D [3]
|
||||
|
||||
5 D D/F (add/add) D/F [2]
|
||||
D/F D/F [3]
|
||||
D/F
|
||||
|
||||
6 D/F D/F D D [0]
|
||||
D
|
||||
|
||||
7 D/F D/F+ D/F (mod/del) D/F [1]
|
||||
D D~branch (fil/dir) D/F [2]
|
||||
D [3]
|
||||
|
||||
8 D/F D/F D D [0]
|
||||
D
|
||||
|
||||
9 D/F D/F+ D/F (mod/del) D/F [1]
|
||||
D D~HEAD (fil/dir) D [2]
|
||||
D/F [3]
|
||||
|
||||
10 D/F D/F (fil/dir) D/F [0]
|
||||
D D~HEAD D [2]
|
||||
D
|
@ -54,6 +54,8 @@ int main(int argc, char **argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
git_threads_init();
|
||||
|
||||
for (i = 0; commands[i].name != NULL; ++i) {
|
||||
if (!strcmp(argv[1], commands[i].name))
|
||||
return run_command(commands[i].fn, --argc, ++argv);
|
||||
|
@ -228,7 +228,7 @@ GIT_EXTERN(void) git_attr_cache_flush(
|
||||
* function allows you to add others. For example, to add the default
|
||||
* macro, you would call:
|
||||
*
|
||||
* git_attr_add_macro(repo, "binary", "-diff -crlf");
|
||||
* git_attr_add_macro(repo, "binary", "-diff -crlf");
|
||||
*/
|
||||
GIT_EXTERN(int) git_attr_add_macro(
|
||||
git_repository *repo,
|
||||
|
@ -29,10 +29,7 @@ GIT_BEGIN_DECL
|
||||
* @param id identity of the blob to locate.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB);
|
||||
}
|
||||
GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Lookup a blob object from a repository,
|
||||
@ -46,10 +43,7 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
|
||||
* @param len the length of the short identifier
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB);
|
||||
}
|
||||
GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* Close an open blob
|
||||
@ -62,11 +56,7 @@ GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, co
|
||||
*
|
||||
* @param blob the blob to close
|
||||
*/
|
||||
|
||||
GIT_INLINE(void) git_blob_free(git_blob *blob)
|
||||
{
|
||||
git_object_free((git_object *) blob);
|
||||
}
|
||||
GIT_EXTERN(void) git_blob_free(git_blob *blob);
|
||||
|
||||
/**
|
||||
* Get the id of a blob.
|
||||
@ -74,11 +64,15 @@ GIT_INLINE(void) git_blob_free(git_blob *blob)
|
||||
* @param blob a previously loaded blob.
|
||||
* @return SHA1 hash for this blob.
|
||||
*/
|
||||
GIT_INLINE(const git_oid *) git_blob_id(const git_blob *blob)
|
||||
{
|
||||
return git_object_id((const git_object *)blob);
|
||||
}
|
||||
GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *blob);
|
||||
|
||||
/**
|
||||
* Get the repository that contains the blob.
|
||||
*
|
||||
* @param blob A previously loaded blob.
|
||||
* @return Repository that contains this blob.
|
||||
*/
|
||||
GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob);
|
||||
|
||||
/**
|
||||
* Get a read-only buffer with the raw content of a blob.
|
||||
|
@ -30,10 +30,7 @@ GIT_BEGIN_DECL
|
||||
* an annotated tag it will be peeled back to the commit.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT);
|
||||
}
|
||||
GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Lookup a commit object from a repository,
|
||||
@ -48,10 +45,7 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
|
||||
* @param len the length of the short identifier
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT);
|
||||
}
|
||||
GIT_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* Close an open commit
|
||||
@ -65,10 +59,7 @@ GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *re
|
||||
* @param commit the commit to close
|
||||
*/
|
||||
|
||||
GIT_INLINE(void) git_commit_free(git_commit *commit)
|
||||
{
|
||||
git_object_free((git_object *) commit);
|
||||
}
|
||||
GIT_EXTERN(void) git_commit_free(git_commit *commit);
|
||||
|
||||
/**
|
||||
* Get the id of a commit.
|
||||
@ -76,10 +67,7 @@ GIT_INLINE(void) git_commit_free(git_commit *commit)
|
||||
* @param commit a previously loaded commit.
|
||||
* @return object identity for the commit.
|
||||
*/
|
||||
GIT_INLINE(const git_oid *) git_commit_id(const git_commit *commit)
|
||||
{
|
||||
return git_object_id((const git_object *)commit);
|
||||
}
|
||||
GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit);
|
||||
|
||||
/**
|
||||
* Get the encoding for the message of a commit,
|
||||
@ -201,14 +189,12 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
|
||||
unsigned int n);
|
||||
|
||||
/**
|
||||
* Create a new commit in the repository using `git_object`
|
||||
* instances as parameters.
|
||||
* Create new commit in the repository from a list of `git_object` pointers
|
||||
*
|
||||
* The message will not be cleaned up. This can be achieved
|
||||
* through `git_message_prettify()`.
|
||||
* The message will not be cleaned up automatically. You can do that with
|
||||
* the `git_message_prettify()` function.
|
||||
*
|
||||
* @param id Pointer where to store the OID of the
|
||||
* newly created commit
|
||||
* @param id Pointer in which to store the OID of the newly created commit
|
||||
*
|
||||
* @param repo Repository where to store the commit
|
||||
*
|
||||
@ -219,73 +205,69 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
|
||||
* make it point to this commit. If the reference doesn't
|
||||
* exist yet, it will be created.
|
||||
*
|
||||
* @param author Signature representing the author and the authory
|
||||
* time of this commit
|
||||
* @param author Signature with author and author time of commit
|
||||
*
|
||||
* @param committer Signature representing the committer and the
|
||||
* commit time of this commit
|
||||
* @param committer Signature with committer and * commit time of commit
|
||||
*
|
||||
* @param message_encoding The encoding for the message in the
|
||||
* commit, represented with a standard encoding name.
|
||||
* E.g. "UTF-8". If NULL, no encoding header is written and
|
||||
* UTF-8 is assumed.
|
||||
* commit, represented with a standard encoding name.
|
||||
* E.g. "UTF-8". If NULL, no encoding header is written and
|
||||
* UTF-8 is assumed.
|
||||
*
|
||||
* @param message Full message for this commit
|
||||
*
|
||||
* @param tree An instance of a `git_tree` object that will
|
||||
* be used as the tree for the commit. This tree object must
|
||||
* also be owned by the given `repo`.
|
||||
* be used as the tree for the commit. This tree object must
|
||||
* also be owned by the given `repo`.
|
||||
*
|
||||
* @param parent_count Number of parents for this commit
|
||||
*
|
||||
* @param parents[] Array of `parent_count` pointers to `git_commit`
|
||||
* objects that will be used as the parents for this commit. This
|
||||
* array may be NULL if `parent_count` is 0 (root commit). All the
|
||||
* given commits must be owned by the `repo`.
|
||||
* objects that will be used as the parents for this commit. This
|
||||
* array may be NULL if `parent_count` is 0 (root commit). All the
|
||||
* given commits must be owned by the `repo`.
|
||||
*
|
||||
* @return 0 or an error code
|
||||
* The created commit will be written to the Object Database and
|
||||
* the given reference will be updated to point to it
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_create(
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
const git_commit *parents[]);
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
const git_commit *parents[]);
|
||||
|
||||
/**
|
||||
* Create a new commit in the repository using a variable
|
||||
* argument list.
|
||||
* Create new commit in the repository using a variable argument list.
|
||||
*
|
||||
* The message will be cleaned up from excess whitespace
|
||||
* it will be made sure that the last line ends with a '\n'.
|
||||
* The message will be cleaned up from excess whitespace and it will be made
|
||||
* sure that the last line ends with a '\n'.
|
||||
*
|
||||
* The parents for the commit are specified as a variable
|
||||
* list of pointers to `const git_commit *`. Note that this
|
||||
* is a convenience method which may not be safe to export
|
||||
* for certain languages or compilers
|
||||
* The parents for the commit are specified as a variable list of pointers
|
||||
* to `const git_commit *`. Note that this is a convenience method which may
|
||||
* not be safe to export for certain languages or compilers
|
||||
*
|
||||
* All other parameters remain the same
|
||||
* All other parameters remain the same at `git_commit_create()`.
|
||||
*
|
||||
* @see git_commit_create
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_create_v(
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...);
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
|
@ -131,8 +131,10 @@ enum {
|
||||
GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
|
||||
GIT_OPT_GET_SEARCH_PATH,
|
||||
GIT_OPT_SET_SEARCH_PATH,
|
||||
GIT_OPT_GET_ODB_CACHE_SIZE,
|
||||
GIT_OPT_SET_ODB_CACHE_SIZE,
|
||||
GIT_OPT_SET_CACHE_OBJECT_LIMIT,
|
||||
GIT_OPT_SET_CACHE_MAX_SIZE,
|
||||
GIT_OPT_ENABLE_CACHING,
|
||||
GIT_OPT_GET_CACHED_MEMORY
|
||||
};
|
||||
|
||||
/**
|
||||
@ -140,43 +142,42 @@ enum {
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
|
||||
* Get the maximum mmap window size
|
||||
* * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
|
||||
*
|
||||
* opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
|
||||
* Set the maximum mmap window size
|
||||
* > Get the maximum mmap window size
|
||||
*
|
||||
* opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
|
||||
* Get the maximum memory that will be mapped in total by the library
|
||||
* * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
|
||||
*
|
||||
* opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
|
||||
* Set the maximum amount of memory that can be mapped at any time
|
||||
* > Set the maximum mmap window size
|
||||
*
|
||||
* * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
|
||||
*
|
||||
* > Get the maximum memory that will be mapped in total by the library
|
||||
*
|
||||
* * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
|
||||
*
|
||||
* >Set the maximum amount of memory that can be mapped at any time
|
||||
* by the library
|
||||
*
|
||||
* opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
|
||||
* Get the search path for a given level of config data. "level" must
|
||||
* be one of GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, or
|
||||
* GIT_CONFIG_LEVEL_XDG. The search path is written to the `out`
|
||||
* buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
|
||||
* * opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
|
||||
*
|
||||
* opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
|
||||
* Set the search path for a level of config data. The search path
|
||||
* applied to shared attributes and ignore files, too.
|
||||
* - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
|
||||
* Pass NULL to reset to the default (generally based on environment
|
||||
* variables). Use magic path `$PATH` to include the old value
|
||||
* of the path (if you want to prepend or append, for instance).
|
||||
* - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
|
||||
* or GIT_CONFIG_LEVEL_XDG.
|
||||
* > Get the search path for a given level of config data. "level" must
|
||||
* > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or
|
||||
* > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out`
|
||||
* > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
|
||||
*
|
||||
* opts(GIT_OPT_GET_ODB_CACHE_SIZE):
|
||||
* Get the size of the libgit2 odb cache.
|
||||
* * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
|
||||
*
|
||||
* opts(GIT_OPT_SET_ODB_CACHE_SIZE):
|
||||
* Set the size of the of the libgit2 odb cache. This needs
|
||||
* to be done before git_repository_open is called, since
|
||||
* git_repository_open initializes the odb layer. Defaults
|
||||
* to 128.
|
||||
* > Set the search path for a level of config data. The search path
|
||||
* > applied to shared attributes and ignore files, too.
|
||||
* >
|
||||
* > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
|
||||
* > Pass NULL to reset to the default (generally based on environment
|
||||
* > variables). Use magic path `$PATH` to include the old value
|
||||
* > of the path (if you want to prepend or append, for instance).
|
||||
* >
|
||||
* > - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
|
||||
* > or GIT_CONFIG_LEVEL_XDG.
|
||||
*
|
||||
* @param option Option key
|
||||
* @param ... value to set the option
|
||||
|
@ -43,29 +43,6 @@ typedef struct {
|
||||
|
||||
typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
|
||||
|
||||
|
||||
/**
|
||||
* Generic backend that implements the interface to
|
||||
* access a configuration file
|
||||
*/
|
||||
struct git_config_backend {
|
||||
unsigned int version;
|
||||
struct git_config *cfg;
|
||||
|
||||
/* Open means open the file/database and parse if necessary */
|
||||
int (*open)(struct git_config_backend *, unsigned int level);
|
||||
int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
|
||||
int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
|
||||
int (*set)(struct git_config_backend *, const char *key, const char *value);
|
||||
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
|
||||
int (*del)(struct git_config_backend *, const char *key);
|
||||
int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
|
||||
int (*refresh)(struct git_config_backend *);
|
||||
void (*free)(struct git_config_backend *);
|
||||
};
|
||||
#define GIT_CONFIG_BACKEND_VERSION 1
|
||||
#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION}
|
||||
|
||||
typedef enum {
|
||||
GIT_CVAR_FALSE = 0,
|
||||
GIT_CVAR_TRUE = 1,
|
||||
@ -153,30 +130,6 @@ GIT_EXTERN(int) git_config_open_default(git_config **out);
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_new(git_config **out);
|
||||
|
||||
/**
|
||||
* Add a generic config file instance to an existing config
|
||||
*
|
||||
* Note that the configuration object will free the file
|
||||
* automatically.
|
||||
*
|
||||
* Further queries on this config object will access each
|
||||
* of the config file instances in order (instances with
|
||||
* a higher priority level will be accessed first).
|
||||
*
|
||||
* @param cfg the configuration to add the file to
|
||||
* @param file the configuration file (backend) to add
|
||||
* @param level the priority level of the backend
|
||||
* @param force if a config file already exists for the given
|
||||
* priority level, replace it
|
||||
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
||||
* for a given priority level (and force_replace set to 0), or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_add_backend(
|
||||
git_config *cfg,
|
||||
git_config_backend *file,
|
||||
unsigned int level,
|
||||
int force);
|
||||
|
||||
/**
|
||||
* Add an on-disk config file instance to an existing config
|
||||
*
|
||||
@ -192,10 +145,9 @@ GIT_EXTERN(int) git_config_add_backend(
|
||||
* a higher priority level will be accessed first).
|
||||
*
|
||||
* @param cfg the configuration to add the file to
|
||||
* @param path path to the configuration file (backend) to add
|
||||
* @param path path to the configuration file to add
|
||||
* @param level the priority level of the backend
|
||||
* @param force if a config file already exists for the given
|
||||
* priority level, replace it
|
||||
* @param force replace config file at the given priority level
|
||||
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
||||
* for a given priority level (and force_replace set to 0),
|
||||
* GIT_ENOTFOUND when the file doesn't exist or error code
|
||||
|
@ -30,11 +30,11 @@ typedef struct git_cred_userpass_payload {
|
||||
/**
|
||||
* Stock callback usable as a git_cred_acquire_cb. This calls
|
||||
* git_cred_userpass_plaintext_new unless the protocol has not specified
|
||||
* GIT_CREDTYPE_USERPASS_PLAINTEXT as an allowed type.
|
||||
* `GIT_CREDTYPE_USERPASS_PLAINTEXT` as an allowed type.
|
||||
*
|
||||
* @param cred The newly created credential object.
|
||||
* @param url The resource for which we are demanding a credential.
|
||||
* @param username_from_url The username that was embedded in a "user@host"
|
||||
* @param user_from_url The username that was embedded in a "user@host"
|
||||
* remote url, or NULL if not included.
|
||||
* @param allowed_types A bitmask stating which cred types are OK to return.
|
||||
* @param payload The payload provided when specifying this callback. (This is
|
||||
|
@ -88,42 +88,61 @@ typedef enum {
|
||||
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
|
||||
/** Include unmodified files in the diff list */
|
||||
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked directory
|
||||
* will be marked with only a single entry in the diff list; this flag
|
||||
* adds all files under the directory as UNTRACKED entries, too.
|
||||
*/
|
||||
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
|
||||
|
||||
/** If the pathspec is set in the diff options, this flags means to
|
||||
* apply it as an exact match instead of as an fnmatch pattern.
|
||||
*/
|
||||
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
|
||||
|
||||
/** Use case insensitive filename comparisons */
|
||||
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
|
||||
|
||||
/** When generating patch text, include the content of untracked files */
|
||||
GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
|
||||
|
||||
/** Disable updating of the `binary` flag in delta records. This is
|
||||
* useful when iterating over a diff if you don't need hunk and data
|
||||
* callbacks and want to avoid having to load file completely.
|
||||
*/
|
||||
GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
|
||||
|
||||
/** Normally, a type change between files will be converted into a
|
||||
* DELETED record for the old and an ADDED record for the new; this
|
||||
* options enabled the generation of TYPECHANGE delta records.
|
||||
*/
|
||||
GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
|
||||
* generally show as a DELETED blob. This flag tries to correctly
|
||||
* label blob->tree transitions as TYPECHANGE records with new_file's
|
||||
* mode set to tree. Note: the tree SHA will not be available.
|
||||
*/
|
||||
GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
|
||||
|
||||
/** Ignore file mode changes */
|
||||
GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
|
||||
* will be marked with only a single entry in the diff list; this flag
|
||||
* adds all files under the directory as IGNORED entries, too.
|
||||
*/
|
||||
GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
|
||||
|
||||
/** Core Git scans inside untracked directories, labeling them IGNORED
|
||||
* if they are empty or only contain ignored files; a directory is
|
||||
* consider UNTRACKED only if it has an actual untracked file in it.
|
||||
* This scan is extra work for a case you often don't care about. This
|
||||
* flag makes libgit2 immediately label an untracked directory as
|
||||
* UNTRACKED without looking insde it (which differs from core Git).
|
||||
* Of course, ignore rules are still checked for the directory itself.
|
||||
*/
|
||||
GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19),
|
||||
} git_diff_option_t;
|
||||
|
||||
/**
|
||||
|
@ -84,13 +84,6 @@ typedef struct git_index_entry {
|
||||
char *path;
|
||||
} git_index_entry;
|
||||
|
||||
/** Representation of a resolve undo entry in the index. */
|
||||
typedef struct git_index_reuc_entry {
|
||||
unsigned int mode[3];
|
||||
git_oid oid[3];
|
||||
char *path;
|
||||
} git_index_reuc_entry;
|
||||
|
||||
/** Capabilities of system that affect index actions. */
|
||||
enum {
|
||||
GIT_INDEXCAP_IGNORE_CASE = 1,
|
||||
@ -478,102 +471,6 @@ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @name Resolve Undo (REUC) index entry manipulation.
|
||||
*
|
||||
* These functions work on the Resolve Undo index extension and contains
|
||||
* data about the original files that led to a merge conflict.
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
/**
|
||||
* Get the count of resolve undo entries currently in the index.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return integer of count of current resolve undo entries
|
||||
*/
|
||||
GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index);
|
||||
|
||||
/**
|
||||
* Finds the resolve undo entry that points to the given path in the Git
|
||||
* index.
|
||||
*
|
||||
* @param at_pos the address to which the position of the reuc entry is written (optional)
|
||||
* @param index an existing index object
|
||||
* @param path path to search
|
||||
* @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
|
||||
|
||||
/**
|
||||
* Get a resolve undo entry from the index.
|
||||
*
|
||||
* The returned entry is read-only and should not be modified
|
||||
* or freed by the caller.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param path path to search
|
||||
* @return the resolve undo entry; NULL if not found
|
||||
*/
|
||||
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
|
||||
|
||||
/**
|
||||
* Get a resolve undo entry from the index.
|
||||
*
|
||||
* The returned entry is read-only and should not be modified
|
||||
* or freed by the caller.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param n the position of the entry
|
||||
* @return a pointer to the resolve undo entry; NULL if out of bounds
|
||||
*/
|
||||
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
|
||||
|
||||
/**
|
||||
* Adds a resolve undo entry for a file based on the given parameters.
|
||||
*
|
||||
* The resolve undo entry contains the OIDs of files that were involved
|
||||
* in a merge conflict after the conflict has been resolved. This allows
|
||||
* conflicts to be re-resolved later.
|
||||
*
|
||||
* If there exists a resolve undo entry for the given path in the index,
|
||||
* it will be removed.
|
||||
*
|
||||
* This method will fail in bare index instances.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param path filename to add
|
||||
* @param ancestor_mode mode of the ancestor file
|
||||
* @param ancestor_id oid of the ancestor file
|
||||
* @param our_mode mode of our file
|
||||
* @param our_id oid of our file
|
||||
* @param their_mode mode of their file
|
||||
* @param their_id oid of their file
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
|
||||
int ancestor_mode, git_oid *ancestor_id,
|
||||
int our_mode, git_oid *our_id,
|
||||
int their_mode, git_oid *their_id);
|
||||
|
||||
/**
|
||||
* Remove an resolve undo entry from the index
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param n position of the resolve undo entry to remove
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
|
||||
|
||||
/**
|
||||
* Remove all resolve undo entries from the index
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -8,31 +8,11 @@
|
||||
#define _INCLUDE_git_indexer_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* This is passed as the first argument to the callback to allow the
|
||||
* user to see the progress.
|
||||
*/
|
||||
typedef struct git_transfer_progress {
|
||||
unsigned int total_objects;
|
||||
unsigned int indexed_objects;
|
||||
unsigned int received_objects;
|
||||
size_t received_bytes;
|
||||
} git_transfer_progress;
|
||||
|
||||
|
||||
/**
|
||||
* Type for progress callbacks during indexing. Return a value less than zero
|
||||
* to cancel the transfer.
|
||||
*
|
||||
* @param stats Structure containing information about the state of the transfer
|
||||
* @param payload Payload provided by caller
|
||||
*/
|
||||
typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
|
||||
|
||||
typedef struct git_indexer_stream git_indexer_stream;
|
||||
|
||||
/**
|
||||
|
@ -7,19 +7,64 @@
|
||||
#ifndef INCLUDE_git_merge_h__
|
||||
#define INCLUDE_git_merge_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/checkout.h"
|
||||
#include "git2/index.h"
|
||||
|
||||
/**
|
||||
* @file git2/merge.h
|
||||
* @brief Git merge-base routines
|
||||
* @defgroup git_revwalk Git merge-base routines
|
||||
* @brief Git merge routines
|
||||
* @defgroup git_merge Git merge routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Flags for tree_many diff options. A combination of these flags can be
|
||||
* passed in via the `flags` value in the `git_diff_tree_many_options`.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Detect renames */
|
||||
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
|
||||
} git_merge_tree_flags;
|
||||
|
||||
/**
|
||||
* Automerge options for `git_merge_trees_opts`.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_MERGE_AUTOMERGE_NORMAL = 0,
|
||||
GIT_MERGE_AUTOMERGE_NONE = 1,
|
||||
GIT_MERGE_AUTOMERGE_FAVOR_OURS = 2,
|
||||
GIT_MERGE_AUTOMERGE_FAVOR_THEIRS = 3,
|
||||
} git_merge_automerge_flags;
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
git_merge_tree_flags flags;
|
||||
|
||||
/** Similarity to consider a file renamed (default 50) */
|
||||
unsigned int rename_threshold;
|
||||
|
||||
/** Maximum similarity sources to examine (overrides the
|
||||
* `merge.renameLimit` config) (default 200)
|
||||
*/
|
||||
unsigned int target_limit;
|
||||
|
||||
/** Pluggable similarity metric; pass NULL to use internal metric */
|
||||
git_diff_similarity_metric *metric;
|
||||
|
||||
/** Flags for automerging content. */
|
||||
git_merge_automerge_flags automerge_flags;
|
||||
} git_merge_tree_opts;
|
||||
|
||||
#define GIT_MERGE_TREE_OPTS_VERSION 1
|
||||
#define GIT_MERGE_TREE_OPTS_INIT {GIT_MERGE_TREE_OPTS_VERSION}
|
||||
|
||||
|
||||
/**
|
||||
* Find a merge base between two commits
|
||||
*
|
||||
@ -50,6 +95,28 @@ GIT_EXTERN(int) git_merge_base_many(
|
||||
const git_oid input_array[],
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* Merge two trees, producing a `git_index` that reflects the result of
|
||||
* the merge.
|
||||
*
|
||||
* The returned index must be freed explicitly with `git_index_free`.
|
||||
*
|
||||
* @param out pointer to store the index result in
|
||||
* @param repo repository that contains the given trees
|
||||
* @param ancestor_tree the common ancestor between the trees (or null if none)
|
||||
* @param our_tree the tree that reflects the destination tree
|
||||
* @param their_tree the tree to merge in to `our_tree`
|
||||
* @param opts the merge tree options (or null for defaults)
|
||||
* @return zero on success, -1 on failure.
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge_trees(
|
||||
git_index **out,
|
||||
git_repository *repo,
|
||||
const git_tree *ancestor_tree,
|
||||
const git_tree *our_tree,
|
||||
const git_tree *their_tree,
|
||||
const git_merge_tree_opts *opts);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "odb_backend.h"
|
||||
#include "indexer.h"
|
||||
|
||||
/**
|
||||
* @file git2/odb.h
|
||||
@ -22,6 +20,11 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Function type for callbacks from git_odb_foreach.
|
||||
*/
|
||||
typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload);
|
||||
|
||||
/**
|
||||
* Create a new object database with no backends.
|
||||
*
|
||||
@ -52,42 +55,6 @@ GIT_EXTERN(int) git_odb_new(git_odb **out);
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
|
||||
|
||||
/**
|
||||
* Add a custom backend to an existing Object DB
|
||||
*
|
||||
* The backends are checked in relative ordering, based on the
|
||||
* value of the `priority` parameter.
|
||||
*
|
||||
* Read <odb_backends.h> for more information.
|
||||
*
|
||||
* @param odb database to add the backend to
|
||||
* @param backend pointer to a git_odb_backend instance
|
||||
* @param priority Value for ordering the backends queue
|
||||
* @return 0 on success; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
|
||||
|
||||
/**
|
||||
* Add a custom backend to an existing Object DB; this
|
||||
* backend will work as an alternate.
|
||||
*
|
||||
* Alternate backends are always checked for objects *after*
|
||||
* all the main backends have been exhausted.
|
||||
*
|
||||
* The backends are checked in relative ordering, based on the
|
||||
* value of the `priority` parameter.
|
||||
*
|
||||
* Writing is disabled on alternate backends.
|
||||
*
|
||||
* Read <odb_backends.h> for more information.
|
||||
*
|
||||
* @param odb database to add the backend to
|
||||
* @param backend pointer to a git_odb_backend instance
|
||||
* @param priority Value for ordering the backends queue
|
||||
* @return 0 on success; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
|
||||
|
||||
/**
|
||||
* Add an on-disk alternate to an existing Object DB.
|
||||
*
|
||||
@ -406,6 +373,60 @@ GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object);
|
||||
*/
|
||||
GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object);
|
||||
|
||||
/**
|
||||
* Add a custom backend to an existing Object DB
|
||||
*
|
||||
* The backends are checked in relative ordering, based on the
|
||||
* value of the `priority` parameter.
|
||||
*
|
||||
* Read <odb_backends.h> for more information.
|
||||
*
|
||||
* @param odb database to add the backend to
|
||||
* @param backend pointer to a git_odb_backend instance
|
||||
* @param priority Value for ordering the backends queue
|
||||
* @return 0 on success; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
|
||||
|
||||
/**
|
||||
* Add a custom backend to an existing Object DB; this
|
||||
* backend will work as an alternate.
|
||||
*
|
||||
* Alternate backends are always checked for objects *after*
|
||||
* all the main backends have been exhausted.
|
||||
*
|
||||
* The backends are checked in relative ordering, based on the
|
||||
* value of the `priority` parameter.
|
||||
*
|
||||
* Writing is disabled on alternate backends.
|
||||
*
|
||||
* Read <odb_backends.h> for more information.
|
||||
*
|
||||
* @param odb database to add the backend to
|
||||
* @param backend pointer to a git_odb_backend instance
|
||||
* @param priority Value for ordering the backends queue
|
||||
* @return 0 on success; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
|
||||
|
||||
/**
|
||||
* Get the number of ODB backend objects
|
||||
*
|
||||
* @param odb object database
|
||||
* @return number of backends in the ODB
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_odb_num_backends(git_odb *odb);
|
||||
|
||||
/**
|
||||
* Lookup an ODB backend object by index
|
||||
*
|
||||
* @param out output pointer to ODB backend at pos
|
||||
* @param odb object database
|
||||
* @param pos index into object database backend list
|
||||
* @return 0 on success; GIT_ENOTFOUND if pos is invalid; other errors < 0
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -7,106 +7,56 @@
|
||||
#ifndef INCLUDE_git_odb_backend_h__
|
||||
#define INCLUDE_git_odb_backend_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "indexer.h"
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
|
||||
/**
|
||||
* @file git2/backend.h
|
||||
* @brief Git custom backend functions
|
||||
* @defgroup git_backend Git custom backend API
|
||||
* @defgroup git_odb Git object database routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
struct git_odb_stream;
|
||||
struct git_odb_writepack;
|
||||
/*
|
||||
* Constructors for in-box ODB backends.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function type for callbacks from git_odb_foreach.
|
||||
* Create a backend for the packfiles.
|
||||
*
|
||||
* @param out location to store the odb backend pointer
|
||||
* @param objects_dir the Git repository's objects directory
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload);
|
||||
GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
|
||||
|
||||
/**
|
||||
* An instance for a custom backend
|
||||
* Create a backend for loose objects
|
||||
*
|
||||
* @param out location to store the odb backend pointer
|
||||
* @param objects_dir the Git repository's objects directory
|
||||
* @param compression_level zlib compression level to use
|
||||
* @param do_fsync whether to do an fsync() after writing (currently ignored)
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
struct git_odb_backend {
|
||||
unsigned int version;
|
||||
git_odb *odb;
|
||||
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
|
||||
|
||||
/* read and read_prefix each return to libgit2 a buffer which
|
||||
* will be freed later. The buffer should be allocated using
|
||||
* the function git_odb_backend_malloc to ensure that it can
|
||||
* be safely freed later. */
|
||||
int (* read)(
|
||||
void **, size_t *, git_otype *,
|
||||
struct git_odb_backend *,
|
||||
const git_oid *);
|
||||
|
||||
/* To find a unique object given a prefix
|
||||
* of its oid.
|
||||
* The oid given must be so that the
|
||||
* remaining (GIT_OID_HEXSZ - len)*4 bits
|
||||
* are 0s.
|
||||
*/
|
||||
int (* read_prefix)(
|
||||
git_oid *,
|
||||
void **, size_t *, git_otype *,
|
||||
struct git_odb_backend *,
|
||||
const git_oid *,
|
||||
size_t);
|
||||
|
||||
int (* read_header)(
|
||||
size_t *, git_otype *,
|
||||
struct git_odb_backend *,
|
||||
const git_oid *);
|
||||
|
||||
/* The writer may assume that the object
|
||||
* has already been hashed and is passed
|
||||
* in the first parameter.
|
||||
*/
|
||||
int (* write)(
|
||||
git_oid *,
|
||||
struct git_odb_backend *,
|
||||
const void *,
|
||||
size_t,
|
||||
git_otype);
|
||||
|
||||
int (* writestream)(
|
||||
struct git_odb_stream **,
|
||||
struct git_odb_backend *,
|
||||
size_t,
|
||||
git_otype);
|
||||
|
||||
int (* readstream)(
|
||||
struct git_odb_stream **,
|
||||
struct git_odb_backend *,
|
||||
const git_oid *);
|
||||
|
||||
int (* exists)(
|
||||
struct git_odb_backend *,
|
||||
const git_oid *);
|
||||
|
||||
int (* refresh)(struct git_odb_backend *);
|
||||
|
||||
int (* foreach)(
|
||||
struct git_odb_backend *,
|
||||
git_odb_foreach_cb cb,
|
||||
void *payload);
|
||||
|
||||
int (* writepack)(
|
||||
struct git_odb_writepack **,
|
||||
struct git_odb_backend *,
|
||||
git_transfer_progress_callback progress_cb,
|
||||
void *progress_payload);
|
||||
|
||||
void (* free)(struct git_odb_backend *);
|
||||
};
|
||||
|
||||
#define GIT_ODB_BACKEND_VERSION 1
|
||||
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
||||
/**
|
||||
* Create a backend out of a single packfile
|
||||
*
|
||||
* This can be useful for inspecting the contents of a single
|
||||
* packfile.
|
||||
*
|
||||
* @param out location to store the odb backend pointer
|
||||
* @param index_file path to the packfile's .idx file
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
|
||||
|
||||
/** Streaming mode */
|
||||
enum {
|
||||
@ -117,33 +67,24 @@ enum {
|
||||
|
||||
/** A stream to read/write from a backend */
|
||||
struct git_odb_stream {
|
||||
struct git_odb_backend *backend;
|
||||
git_odb_backend *backend;
|
||||
unsigned int mode;
|
||||
|
||||
int (*read)(struct git_odb_stream *stream, char *buffer, size_t len);
|
||||
int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len);
|
||||
int (*finalize_write)(git_oid *oid_p, struct git_odb_stream *stream);
|
||||
void (*free)(struct git_odb_stream *stream);
|
||||
int (*read)(git_odb_stream *stream, char *buffer, size_t len);
|
||||
int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
|
||||
int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream);
|
||||
void (*free)(git_odb_stream *stream);
|
||||
};
|
||||
|
||||
/** A stream to write a pack file to the ODB */
|
||||
struct git_odb_writepack {
|
||||
struct git_odb_backend *backend;
|
||||
git_odb_backend *backend;
|
||||
|
||||
int (*add)(struct git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
|
||||
int (*commit)(struct git_odb_writepack *writepack, git_transfer_progress *stats);
|
||||
void (*free)(struct git_odb_writepack *writepack);
|
||||
int (*add)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
|
||||
int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats);
|
||||
void (*free)(git_odb_writepack *writepack);
|
||||
};
|
||||
|
||||
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
|
||||
|
||||
/**
|
||||
* Constructors for in-box ODB backends.
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
|
||||
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
|
||||
GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
|
||||
|
||||
GIT_END_DECL
|
||||
|
||||
#endif
|
||||
|
@ -145,19 +145,7 @@ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src);
|
||||
* @param b second oid structure.
|
||||
* @return <0, 0, >0 if a < b, a == b, a > b.
|
||||
*/
|
||||
GIT_INLINE(int) git_oid_cmp(const git_oid *a, const git_oid *b)
|
||||
{
|
||||
const unsigned char *sha1 = a->id;
|
||||
const unsigned char *sha2 = b->id;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
|
||||
if (*sha1 != *sha2)
|
||||
return *sha1 - *sha2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
|
||||
|
||||
/**
|
||||
* Compare two oid structures for equality
|
||||
@ -192,6 +180,16 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
|
||||
|
||||
/**
|
||||
* Compare an oid to an hex formatted object id.
|
||||
*
|
||||
* @param id oid structure.
|
||||
* @param str input hex string of an object id.
|
||||
* @return -1 if str is not valid, <0 if id sorts before str,
|
||||
* 0 if id matches str, >0 if id sorts after str.
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_strcmp(const git_oid *id, const char *str);
|
||||
|
||||
/**
|
||||
* Check is an oid is all zeros.
|
||||
*
|
||||
|
@ -94,6 +94,18 @@ GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, c
|
||||
*/
|
||||
GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Insert a commit object
|
||||
*
|
||||
* This will add a commit as well as the completed referenced tree.
|
||||
*
|
||||
* @param pb The packbuilder
|
||||
* @param id The oid of the commit
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Write the new pack and the corresponding index to path
|
||||
*
|
||||
|
@ -21,22 +21,6 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Create a new reference. Either an oid or a symbolic target must be
|
||||
* specified.
|
||||
*
|
||||
* @param refdb the reference database to associate with this reference
|
||||
* @param name the reference name
|
||||
* @param oid the object id for a direct reference
|
||||
* @param symbolic the target for a symbolic reference
|
||||
* @return the created git_reference or NULL on error
|
||||
*/
|
||||
GIT_EXTERN(git_reference *) git_reference__alloc(
|
||||
git_refdb *refdb,
|
||||
const char *name,
|
||||
const git_oid *oid,
|
||||
const char *symbolic);
|
||||
|
||||
/**
|
||||
* Create a new reference database with no backends.
|
||||
*
|
||||
@ -78,20 +62,6 @@ GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb);
|
||||
*/
|
||||
GIT_EXTERN(void) git_refdb_free(git_refdb *refdb);
|
||||
|
||||
/**
|
||||
* Sets the custom backend to an existing reference DB
|
||||
*
|
||||
* Read <refdb_backends.h> for more information.
|
||||
*
|
||||
* @param refdb database to add the backend to
|
||||
* @param backend pointer to a git_refdb_backend instance
|
||||
* @param priority Value for ordering the backends queue
|
||||
* @return 0 on success; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_refdb_set_backend(
|
||||
git_refdb *refdb,
|
||||
git_refdb_backend *backend);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
|
||||
|
@ -132,6 +132,17 @@ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo,
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref);
|
||||
|
||||
/**
|
||||
* Return the peeled OID target of this reference.
|
||||
*
|
||||
* This peeled OID only applies to direct references that point to
|
||||
* a hard Tag object: it is the result of peeling such Tag.
|
||||
*
|
||||
* @param ref The reference
|
||||
* @return a pointer to the oid if available, NULL otherwise
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_reference_target_peel(const git_reference *ref);
|
||||
|
||||
/**
|
||||
* Get full name to the reference pointed to by a symbolic reference.
|
||||
*
|
||||
@ -411,6 +422,13 @@ typedef enum {
|
||||
* (e.g., foo/<star>/bar but not foo/bar<star>).
|
||||
*/
|
||||
GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
|
||||
|
||||
/**
|
||||
* Interpret the name as part of a refspec in shorthand form
|
||||
* so the `ONELEVEL` naming rules aren't enforced and 'master'
|
||||
* becomes a valid name.
|
||||
*/
|
||||
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
|
||||
} git_reference_normalize_t;
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "net.h"
|
||||
|
||||
/**
|
||||
* @file git2/refspec.h
|
||||
@ -35,6 +36,14 @@ GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
|
||||
|
||||
/**
|
||||
* Get the refspec's string
|
||||
*
|
||||
* @param refspec the refspec
|
||||
* @returns the refspec's original string
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec);
|
||||
|
||||
/**
|
||||
* Get the force update setting
|
||||
*
|
||||
@ -43,6 +52,14 @@ GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
|
||||
*/
|
||||
GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
|
||||
|
||||
/**
|
||||
* Get the refspec's direction.
|
||||
*
|
||||
* @param the refspec
|
||||
* @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
|
||||
*/
|
||||
GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec);
|
||||
|
||||
/**
|
||||
* Check if a refspec's source descriptor matches a reference
|
||||
*
|
||||
|
@ -142,39 +142,79 @@ GIT_EXTERN(int) git_remote_set_url(git_remote *remote, const char* url);
|
||||
GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
|
||||
|
||||
/**
|
||||
* Set the remote's fetch refspec
|
||||
* Add a fetch refspec to the remote
|
||||
*
|
||||
* @param remote the remote
|
||||
* @apram spec the new fetch refspec
|
||||
* @apram refspec the new fetch refspec
|
||||
* @return 0 or an error value
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_set_fetchspec(git_remote *remote, const char *spec);
|
||||
GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
|
||||
|
||||
/**
|
||||
* Get the fetch refspec
|
||||
* Get the remote's list of fetch refspecs
|
||||
*
|
||||
* @param remote the remote
|
||||
* @return a pointer to the fetch refspec or NULL if it doesn't exist
|
||||
* The memory is owned by the user and should be freed with
|
||||
* `git_strarray_free`.
|
||||
*
|
||||
* @param array pointer to the array in which to store the strings
|
||||
* @param remote the remote to query
|
||||
*/
|
||||
GIT_EXTERN(const git_refspec *) git_remote_fetchspec(const git_remote *remote);
|
||||
GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
|
||||
|
||||
/**
|
||||
* Set the remote's push refspec
|
||||
* Add a push refspec to the remote
|
||||
*
|
||||
* @param remote the remote
|
||||
* @param spec the new push refspec
|
||||
* @param refspec the new push refspec
|
||||
* @return 0 or an error value
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec);
|
||||
GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec);
|
||||
|
||||
/**
|
||||
* Get the push refspec
|
||||
* Get the remote's list of push refspecs
|
||||
*
|
||||
* The memory is owned by the user and should be freed with
|
||||
* `git_strarray_free`.
|
||||
*
|
||||
* @param array pointer to the array in which to store the strings
|
||||
* @param remote the remote to query
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
|
||||
|
||||
/**
|
||||
* Clear the refspecs
|
||||
*
|
||||
* Remove all configured fetch and push refspecs from the remote.
|
||||
*
|
||||
* @param remote the remote
|
||||
* @return a pointer to the push refspec or NULL if it doesn't exist
|
||||
*/
|
||||
GIT_EXTERN(void) git_remote_clear_refspecs(git_remote *remote);
|
||||
|
||||
GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote);
|
||||
/**
|
||||
* Get the number of refspecs for a remote
|
||||
*
|
||||
* @param remote the remote
|
||||
* @return the amount of refspecs configured in this remote
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote);
|
||||
|
||||
/**
|
||||
* Get a refspec from the remote
|
||||
*
|
||||
* @param remote the remote to query
|
||||
* @param n the refspec to get
|
||||
* @return the nth refspec
|
||||
*/
|
||||
GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n);
|
||||
|
||||
/**
|
||||
* Remove a refspec from the remote
|
||||
*
|
||||
* @param remote the remote to query
|
||||
* @param n the refspec to remove
|
||||
* @return 0 or GIT_ENOTFOUND
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n);
|
||||
|
||||
/**
|
||||
* Open a connection to a remote
|
||||
@ -184,7 +224,8 @@ GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote);
|
||||
* starts up a specific binary which can only do the one or the other.
|
||||
*
|
||||
* @param remote the remote to connect to
|
||||
* @param direction whether you want to receive or send data
|
||||
* @param direction GIT_DIRECTION_FETCH if you want to fetch or
|
||||
* GIT_DIRECTION_PUSH if you want to push
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
|
||||
|
@ -123,6 +123,19 @@ GIT_EXTERN(int) git_repository_open_ext(
|
||||
unsigned int flags,
|
||||
const char *ceiling_dirs);
|
||||
|
||||
/**
|
||||
* Open a bare repository on the serverside.
|
||||
*
|
||||
* This is a fast open for bare repositories that will come in handy
|
||||
* if you're e.g. hosting git repositories and need to access them
|
||||
* efficiently
|
||||
*
|
||||
* @param out Pointer to the repo which will be opened.
|
||||
* @param bare_path Direct path to the bare repository
|
||||
* @return 0 on success, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path);
|
||||
|
||||
/**
|
||||
* Free a previously allocated repository
|
||||
*
|
||||
@ -387,21 +400,6 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Set the configuration file for this repository
|
||||
*
|
||||
* This configuration file will be used for all configuration
|
||||
* queries involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the config file;
|
||||
* the user must still free the config after setting it
|
||||
* to the repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param config A Config object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config);
|
||||
|
||||
/**
|
||||
* Get the Object Database for this repository.
|
||||
*
|
||||
@ -418,21 +416,6 @@ GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *con
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Set the Object Database for this repository
|
||||
*
|
||||
* The ODB will be used for all object-related operations
|
||||
* involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the ODB; the user
|
||||
* must still free the ODB object after setting it to the
|
||||
* repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param odb An ODB object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
||||
|
||||
/**
|
||||
* Get the Reference Database Backend for this repository.
|
||||
*
|
||||
@ -449,23 +432,6 @@ GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Set the Reference Database Backend for this repository
|
||||
*
|
||||
* The refdb will be used for all reference related operations
|
||||
* involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the refdb; the user
|
||||
* must still free the refdb object after setting it to the
|
||||
* repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param refdb An refdb object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_refdb(
|
||||
git_repository *repo,
|
||||
git_refdb *refdb);
|
||||
|
||||
/**
|
||||
* Get the Index file for this repository.
|
||||
*
|
||||
@ -482,21 +448,6 @@ GIT_EXTERN(void) git_repository_set_refdb(
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Set the index file for this repository
|
||||
*
|
||||
* This index will be used for all index-related operations
|
||||
* involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the index file;
|
||||
* the user must still free the index after setting it
|
||||
* to the repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param index An index object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
|
||||
|
||||
/**
|
||||
* Retrieve git's prepared message
|
||||
*
|
||||
@ -675,6 +626,28 @@ typedef enum {
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_state(git_repository *repo);
|
||||
|
||||
/**
|
||||
* Sets the active namespace for this Git Repository
|
||||
*
|
||||
* This namespace affects all reference operations for the repo.
|
||||
* See `man gitnamespaces`
|
||||
*
|
||||
* @param repo The repo
|
||||
* @param nmspace The namespace. This should not include the refs
|
||||
* folder, e.g. to namespace all references under `refs/namespaces/foo/`,
|
||||
* use `foo` as the namespace.
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *nmspace);
|
||||
|
||||
/**
|
||||
* Get the currently active namespace for this repository
|
||||
*
|
||||
* @param repo The repo
|
||||
* @return the active namespace, or NULL if there isn't one
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -103,20 +103,20 @@ typedef enum {
|
||||
* * WD_UNTRACKED - wd contains untracked files
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
} git_submodule_status_t;
|
||||
|
||||
#define GIT_SUBMODULE_STATUS__IN_FLAGS \
|
||||
|
45
include/git2/sys/commit.h
Normal file
45
include/git2/sys/commit.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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_git_commit_h__
|
||||
#define INCLUDE_sys_git_commit_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
/**
|
||||
* @file git2/sys/commit.h
|
||||
* @brief Low-level Git commit creation
|
||||
* @defgroup git_backend Git custom backend APIs
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Create new commit in the repository from a list of `git_oid` values
|
||||
*
|
||||
* See documentation for `git_commit_create()` for information about the
|
||||
* parameters, as the meaning is identical excepting that `tree` and
|
||||
* `parents` now take `git_oid`. This is a dangerous API in that the
|
||||
* `parents` list of `git_oid`s in not checked for validity.
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_create_from_oids(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_oid *tree,
|
||||
int parent_count,
|
||||
const git_oid *parents[]);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
71
include/git2/sys/config.h
Normal file
71
include/git2/sys/config.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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_git_config_backend_h__
|
||||
#define INCLUDE_sys_git_config_backend_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/config.h"
|
||||
|
||||
/**
|
||||
* @file git2/sys/config.h
|
||||
* @brief Git config backend routines
|
||||
* @defgroup git_backend Git custom backend APIs
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Generic backend that implements the interface to
|
||||
* access a configuration file
|
||||
*/
|
||||
struct git_config_backend {
|
||||
unsigned int version;
|
||||
struct git_config *cfg;
|
||||
|
||||
/* Open means open the file/database and parse if necessary */
|
||||
int (*open)(struct git_config_backend *, unsigned int level);
|
||||
int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
|
||||
int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
|
||||
int (*set)(struct git_config_backend *, const char *key, const char *value);
|
||||
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
|
||||
int (*del)(struct git_config_backend *, const char *key);
|
||||
int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
|
||||
int (*refresh)(struct git_config_backend *);
|
||||
void (*free)(struct git_config_backend *);
|
||||
};
|
||||
#define GIT_CONFIG_BACKEND_VERSION 1
|
||||
#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION}
|
||||
|
||||
/**
|
||||
* Add a generic config file instance to an existing config
|
||||
*
|
||||
* Note that the configuration object will free the file
|
||||
* automatically.
|
||||
*
|
||||
* Further queries on this config object will access each
|
||||
* of the config file instances in order (instances with
|
||||
* a higher priority level will be accessed first).
|
||||
*
|
||||
* @param cfg the configuration to add the file to
|
||||
* @param file the configuration file (backend) to add
|
||||
* @param level the priority level of the backend
|
||||
* @param force if a config file already exists for the given
|
||||
* priority level, replace it
|
||||
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
||||
* for a given priority level (and force_replace set to 0), or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_add_backend(
|
||||
git_config *cfg,
|
||||
git_config_backend *file,
|
||||
unsigned int level,
|
||||
int force);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
180
include/git2/sys/index.h
Normal file
180
include/git2/sys/index.h
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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_git_index_h__
|
||||
#define INCLUDE_sys_git_index_h__
|
||||
|
||||
/**
|
||||
* @file git2/sys/index.h
|
||||
* @brief Low-level Git index manipulation routines
|
||||
* @defgroup git_backend Git custom backend APIs
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/** Representation of a rename conflict entry in the index. */
|
||||
typedef struct git_index_name_entry {
|
||||
char *ancestor;
|
||||
char *ours;
|
||||
char *theirs;
|
||||
} git_index_name_entry;
|
||||
|
||||
/** Representation of a resolve undo entry in the index. */
|
||||
typedef struct git_index_reuc_entry {
|
||||
unsigned int mode[3];
|
||||
git_oid oid[3];
|
||||
char *path;
|
||||
} git_index_reuc_entry;
|
||||
|
||||
/** @name Conflict Name entry functions
|
||||
*
|
||||
* These functions work on rename conflict entries.
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
/**
|
||||
* Get the count of filename conflict entries currently in the index.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return integer of count of current filename conflict entries
|
||||
*/
|
||||
GIT_EXTERN(unsigned int) git_index_name_entrycount(git_index *index);
|
||||
|
||||
/**
|
||||
* Get a filename conflict entry from the index.
|
||||
*
|
||||
* The returned entry is read-only and should not be modified
|
||||
* or freed by the caller.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param n the position of the entry
|
||||
* @return a pointer to the filename conflict entry; NULL if out of bounds
|
||||
*/
|
||||
GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex(
|
||||
git_index *index, size_t n);
|
||||
|
||||
/**
|
||||
* Record the filenames involved in a rename conflict.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param ancestor the path of the file as it existed in the ancestor
|
||||
* @param ours the path of the file as it existed in our tree
|
||||
* @param theirs the path of the file as it existed in their tree
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_name_add(git_index *index,
|
||||
const char *ancestor, const char *ours, const char *theirs);
|
||||
|
||||
/**
|
||||
* Remove all filename conflict entries.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(void) git_index_name_clear(git_index *index);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @name Resolve Undo (REUC) index entry manipulation.
|
||||
*
|
||||
* These functions work on the Resolve Undo index extension and contains
|
||||
* data about the original files that led to a merge conflict.
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
/**
|
||||
* Get the count of resolve undo entries currently in the index.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return integer of count of current resolve undo entries
|
||||
*/
|
||||
GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index);
|
||||
|
||||
/**
|
||||
* Finds the resolve undo entry that points to the given path in the Git
|
||||
* index.
|
||||
*
|
||||
* @param at_pos the address to which the position of the reuc entry is written (optional)
|
||||
* @param index an existing index object
|
||||
* @param path path to search
|
||||
* @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
|
||||
|
||||
/**
|
||||
* Get a resolve undo entry from the index.
|
||||
*
|
||||
* The returned entry is read-only and should not be modified
|
||||
* or freed by the caller.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param path path to search
|
||||
* @return the resolve undo entry; NULL if not found
|
||||
*/
|
||||
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
|
||||
|
||||
/**
|
||||
* Get a resolve undo entry from the index.
|
||||
*
|
||||
* The returned entry is read-only and should not be modified
|
||||
* or freed by the caller.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param n the position of the entry
|
||||
* @return a pointer to the resolve undo entry; NULL if out of bounds
|
||||
*/
|
||||
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
|
||||
|
||||
/**
|
||||
* Adds a resolve undo entry for a file based on the given parameters.
|
||||
*
|
||||
* The resolve undo entry contains the OIDs of files that were involved
|
||||
* in a merge conflict after the conflict has been resolved. This allows
|
||||
* conflicts to be re-resolved later.
|
||||
*
|
||||
* If there exists a resolve undo entry for the given path in the index,
|
||||
* it will be removed.
|
||||
*
|
||||
* This method will fail in bare index instances.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param path filename to add
|
||||
* @param ancestor_mode mode of the ancestor file
|
||||
* @param ancestor_id oid of the ancestor file
|
||||
* @param our_mode mode of our file
|
||||
* @param our_id oid of our file
|
||||
* @param their_mode mode of their file
|
||||
* @param their_id oid of their file
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
|
||||
int ancestor_mode, const git_oid *ancestor_id,
|
||||
int our_mode, const git_oid *our_id,
|
||||
int their_mode, const git_oid *their_id);
|
||||
|
||||
/**
|
||||
* Remove an resolve undo entry from the index
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param n position of the resolve undo entry to remove
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
|
||||
|
||||
/**
|
||||
* Remove all resolve undo entries from the index
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
86
include/git2/sys/odb_backend.h
Normal file
86
include/git2/sys/odb_backend.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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_git_odb_backend_h__
|
||||
#define INCLUDE_sys_git_odb_backend_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/odb.h"
|
||||
|
||||
/**
|
||||
* @file git2/sys/backend.h
|
||||
* @brief Git custom backend implementors functions
|
||||
* @defgroup git_backend Git custom backend APIs
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* An instance for a custom backend
|
||||
*/
|
||||
struct git_odb_backend {
|
||||
unsigned int version;
|
||||
git_odb *odb;
|
||||
|
||||
/* read and read_prefix each return to libgit2 a buffer which
|
||||
* will be freed later. The buffer should be allocated using
|
||||
* the function git_odb_backend_malloc to ensure that it can
|
||||
* be safely freed later. */
|
||||
int (* read)(
|
||||
void **, size_t *, git_otype *, git_odb_backend *, const git_oid *);
|
||||
|
||||
/* To find a unique object given a prefix
|
||||
* of its oid.
|
||||
* The oid given must be so that the
|
||||
* remaining (GIT_OID_HEXSZ - len)*4 bits
|
||||
* are 0s.
|
||||
*/
|
||||
int (* read_prefix)(
|
||||
git_oid *, void **, size_t *, git_otype *,
|
||||
git_odb_backend *, const git_oid *, size_t);
|
||||
|
||||
int (* read_header)(
|
||||
size_t *, git_otype *, git_odb_backend *, const git_oid *);
|
||||
|
||||
/* The writer may assume that the object
|
||||
* has already been hashed and is passed
|
||||
* in the first parameter.
|
||||
*/
|
||||
int (* write)(
|
||||
git_oid *, git_odb_backend *, const void *, size_t, git_otype);
|
||||
|
||||
int (* writestream)(
|
||||
git_odb_stream **, git_odb_backend *, size_t, git_otype);
|
||||
|
||||
int (* readstream)(
|
||||
git_odb_stream **, git_odb_backend *, const git_oid *);
|
||||
|
||||
int (* exists)(
|
||||
git_odb_backend *, const git_oid *);
|
||||
|
||||
int (* refresh)(git_odb_backend *);
|
||||
|
||||
int (* foreach)(
|
||||
git_odb_backend *, git_odb_foreach_cb cb, void *payload);
|
||||
|
||||
int (* writepack)(
|
||||
git_odb_writepack **, git_odb_backend *,
|
||||
git_transfer_progress_callback progress_cb, void *progress_payload);
|
||||
|
||||
void (* free)(git_odb_backend *);
|
||||
};
|
||||
|
||||
#define GIT_ODB_BACKEND_VERSION 1
|
||||
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
||||
|
||||
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
|
||||
|
||||
GIT_END_DECL
|
||||
|
||||
#endif
|
@ -4,12 +4,12 @@
|
||||
* 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_git_refdb_backend_h__
|
||||
#define INCLUDE_git_refdb_backend_h__
|
||||
#ifndef INCLUDE_sys_git_refdb_backend_h__
|
||||
#define INCLUDE_sys_git_refdb_backend_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
/**
|
||||
* @file git2/refdb_backend.h
|
||||
@ -30,7 +30,7 @@ struct git_refdb_backend {
|
||||
*/
|
||||
int (*exists)(
|
||||
int *exists,
|
||||
struct git_refdb_backend *backend,
|
||||
git_refdb_backend *backend,
|
||||
const char *ref_name);
|
||||
|
||||
/**
|
||||
@ -39,7 +39,7 @@ struct git_refdb_backend {
|
||||
*/
|
||||
int (*lookup)(
|
||||
git_reference **out,
|
||||
struct git_refdb_backend *backend,
|
||||
git_refdb_backend *backend,
|
||||
const char *ref_name);
|
||||
|
||||
/**
|
||||
@ -47,7 +47,7 @@ struct git_refdb_backend {
|
||||
* provide this function.
|
||||
*/
|
||||
int (*foreach)(
|
||||
struct git_refdb_backend *backend,
|
||||
git_refdb_backend *backend,
|
||||
unsigned int list_flags,
|
||||
git_reference_foreach_cb callback,
|
||||
void *payload);
|
||||
@ -59,7 +59,7 @@ struct git_refdb_backend {
|
||||
* against the glob.
|
||||
*/
|
||||
int (*foreach_glob)(
|
||||
struct git_refdb_backend *backend,
|
||||
git_refdb_backend *backend,
|
||||
const char *glob,
|
||||
unsigned int list_flags,
|
||||
git_reference_foreach_cb callback,
|
||||
@ -69,13 +69,13 @@ struct git_refdb_backend {
|
||||
* Writes the given reference to the refdb. A refdb implementation
|
||||
* must provide this function.
|
||||
*/
|
||||
int (*write)(struct git_refdb_backend *backend, const git_reference *ref);
|
||||
int (*write)(git_refdb_backend *backend, const git_reference *ref);
|
||||
|
||||
/**
|
||||
* Deletes the given reference from the refdb. A refdb implementation
|
||||
* must provide this function.
|
||||
*/
|
||||
int (*delete)(struct git_refdb_backend *backend, const git_reference *ref);
|
||||
int (*delete)(git_refdb_backend *backend, const git_reference *ref);
|
||||
|
||||
/**
|
||||
* Suggests that the given refdb compress or optimize its references.
|
||||
@ -84,25 +84,46 @@ struct git_refdb_backend {
|
||||
* implementation may provide this function; if it is not provided,
|
||||
* nothing will be done.
|
||||
*/
|
||||
int (*compress)(struct git_refdb_backend *backend);
|
||||
int (*compress)(git_refdb_backend *backend);
|
||||
|
||||
/**
|
||||
* Frees any resources held by the refdb. A refdb implementation may
|
||||
* provide this function; if it is not provided, nothing will be done.
|
||||
*/
|
||||
void (*free)(struct git_refdb_backend *backend);
|
||||
void (*free)(git_refdb_backend *backend);
|
||||
};
|
||||
|
||||
#define GIT_ODB_BACKEND_VERSION 1
|
||||
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
||||
|
||||
/**
|
||||
* Constructors for default refdb backends.
|
||||
* Constructors for default filesystem-based refdb backend
|
||||
*
|
||||
* Under normal usage, this is called for you when the repository is
|
||||
* opened / created, but you can use this to explicitly construct a
|
||||
* filesystem refdb backend for a repository.
|
||||
*
|
||||
* @param backend_out Output pointer to the git_refdb_backend object
|
||||
* @param repo Git repository to access
|
||||
* @return 0 on success, <0 error code on failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_refdb_backend_fs(
|
||||
struct git_refdb_backend **backend_out,
|
||||
git_repository *repo,
|
||||
git_refdb *refdb);
|
||||
git_refdb_backend **backend_out,
|
||||
git_repository *repo);
|
||||
|
||||
/**
|
||||
* Sets the custom backend to an existing reference DB
|
||||
*
|
||||
* The `git_refdb` will take ownership of the `git_refdb_backend` so you
|
||||
* should NOT free it after calling this function.
|
||||
*
|
||||
* @param refdb database to add the backend to
|
||||
* @param backend pointer to a git_refdb_backend instance
|
||||
* @return 0 on success; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_refdb_set_backend(
|
||||
git_refdb *refdb,
|
||||
git_refdb_backend *backend);
|
||||
|
||||
GIT_END_DECL
|
||||
|
38
include/git2/sys/refs.h
Normal file
38
include/git2/sys/refs.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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_git_refdb_h__
|
||||
#define INCLUDE_sys_git_refdb_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
/**
|
||||
* Create a new direct reference from an OID.
|
||||
*
|
||||
* @param name the reference name
|
||||
* @param oid the object id for a direct reference
|
||||
* @param symbolic the target for a symbolic reference
|
||||
* @return the created git_reference or NULL on error
|
||||
*/
|
||||
GIT_EXTERN(git_reference *) git_reference__alloc(
|
||||
const char *name,
|
||||
const git_oid *oid,
|
||||
const git_oid *peel);
|
||||
|
||||
/**
|
||||
* Create a new symbolic reference.
|
||||
*
|
||||
* @param name the reference name
|
||||
* @param symbolic the target for a symbolic reference
|
||||
* @return the created git_reference or NULL on error
|
||||
*/
|
||||
GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
|
||||
const char *name,
|
||||
const char *target);
|
||||
|
||||
#endif
|
106
include/git2/sys/repository.h
Normal file
106
include/git2/sys/repository.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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_git_repository_h__
|
||||
#define INCLUDE_sys_git_repository_h__
|
||||
|
||||
/**
|
||||
* @file git2/sys/repository.h
|
||||
* @brief Git repository custom implementation routines
|
||||
* @defgroup git_backend Git custom backend APIs
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Create a new repository with neither backends nor config object
|
||||
*
|
||||
* Note that this is only useful if you wish to associate the repository
|
||||
* with a non-filesystem-backed object database and config store.
|
||||
*
|
||||
* @param out The blank repository
|
||||
* @return 0 on success, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_new(git_repository **out);
|
||||
|
||||
|
||||
/**
|
||||
* Reset all the internal state in a repository.
|
||||
*
|
||||
* This will free all the mapped memory and internal objects
|
||||
* of the repository and leave it in a "blank" state.
|
||||
*
|
||||
* There's no need to call this function directly unless you're
|
||||
* trying to aggressively cleanup the repo before its
|
||||
* deallocation. `git_repository_free` already performs this operation
|
||||
* before deallocation the repo.
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
|
||||
|
||||
/**
|
||||
* Set the configuration file for this repository
|
||||
*
|
||||
* This configuration file will be used for all configuration
|
||||
* queries involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the config file;
|
||||
* the user must still free the config after setting it
|
||||
* to the repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param config A Config object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config);
|
||||
|
||||
/**
|
||||
* Set the Object Database for this repository
|
||||
*
|
||||
* The ODB will be used for all object-related operations
|
||||
* involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the ODB; the user
|
||||
* must still free the ODB object after setting it to the
|
||||
* repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param odb An ODB object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
||||
|
||||
/**
|
||||
* Set the Reference Database Backend for this repository
|
||||
*
|
||||
* The refdb will be used for all reference related operations
|
||||
* involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the refdb; the user
|
||||
* must still free the refdb object after setting it to the
|
||||
* repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param refdb An refdb object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_refdb(git_repository *repo, git_refdb *refdb);
|
||||
|
||||
/**
|
||||
* Set the index file for this repository
|
||||
*
|
||||
* This index will be used for all index-related operations
|
||||
* involving this repository.
|
||||
*
|
||||
* The repository will keep a reference to the index file;
|
||||
* the user must still free the index after setting it
|
||||
* to the repository, or it will leak.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param index An index object
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
@ -30,12 +30,8 @@ GIT_BEGIN_DECL
|
||||
* @param id identity of the tag to locate.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tag_lookup(
|
||||
git_tag **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup(
|
||||
(git_object **)out, repo, id, (git_otype)GIT_OBJ_TAG);
|
||||
}
|
||||
GIT_EXTERN(int) git_tag_lookup(
|
||||
git_tag **out, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Lookup a tag object from the repository,
|
||||
@ -49,12 +45,8 @@ GIT_INLINE(int) git_tag_lookup(
|
||||
* @param len the length of the short identifier
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tag_lookup_prefix(
|
||||
git_tag **out, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix(
|
||||
(git_object **)out, repo, id, len, (git_otype)GIT_OBJ_TAG);
|
||||
}
|
||||
GIT_EXTERN(int) git_tag_lookup_prefix(
|
||||
git_tag **out, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* Close an open tag
|
||||
@ -66,12 +58,7 @@ GIT_INLINE(int) git_tag_lookup_prefix(
|
||||
*
|
||||
* @param tag the tag to close
|
||||
*/
|
||||
|
||||
GIT_INLINE(void) git_tag_free(git_tag *tag)
|
||||
{
|
||||
git_object_free((git_object *)tag);
|
||||
}
|
||||
|
||||
GIT_EXTERN(void) git_tag_free(git_tag *tag);
|
||||
|
||||
/**
|
||||
* Get the id of a tag.
|
||||
@ -81,6 +68,14 @@ GIT_INLINE(void) git_tag_free(git_tag *tag)
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag);
|
||||
|
||||
/**
|
||||
* Get the repository that contains the tag.
|
||||
*
|
||||
* @param tag A previously loaded tag.
|
||||
* @return Repository that contains this tag.
|
||||
*/
|
||||
GIT_EXTERN(git_repository *) git_tag_owner(const git_tag *tag);
|
||||
|
||||
/**
|
||||
* Get the tagged object of a tag
|
||||
*
|
||||
|
@ -29,11 +29,8 @@ GIT_BEGIN_DECL
|
||||
* @param id Identity of the tree to locate.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tree_lookup(
|
||||
git_tree **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE);
|
||||
}
|
||||
GIT_EXTERN(int) git_tree_lookup(
|
||||
git_tree **out, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Lookup a tree object from the repository,
|
||||
@ -47,15 +44,11 @@ GIT_INLINE(int) git_tree_lookup(
|
||||
* @param len the length of the short identifier
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tree_lookup_prefix(
|
||||
GIT_EXTERN(int) git_tree_lookup_prefix(
|
||||
git_tree **out,
|
||||
git_repository *repo,
|
||||
const git_oid *id,
|
||||
size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix(
|
||||
(git_object **)out, repo, id, len, GIT_OBJ_TREE);
|
||||
}
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* Close an open tree
|
||||
@ -67,10 +60,7 @@ GIT_INLINE(int) git_tree_lookup_prefix(
|
||||
*
|
||||
* @param tree The tree to close
|
||||
*/
|
||||
GIT_INLINE(void) git_tree_free(git_tree *tree)
|
||||
{
|
||||
git_object_free((git_object *)tree);
|
||||
}
|
||||
GIT_EXTERN(void) git_tree_free(git_tree *tree);
|
||||
|
||||
/**
|
||||
* Get the id of a tree.
|
||||
|
@ -196,6 +196,26 @@ typedef struct git_push git_push;
|
||||
typedef struct git_remote_head git_remote_head;
|
||||
typedef struct git_remote_callbacks git_remote_callbacks;
|
||||
|
||||
/**
|
||||
* This is passed as the first argument to the callback to allow the
|
||||
* user to see the progress.
|
||||
*/
|
||||
typedef struct git_transfer_progress {
|
||||
unsigned int total_objects;
|
||||
unsigned int indexed_objects;
|
||||
unsigned int received_objects;
|
||||
size_t received_bytes;
|
||||
} git_transfer_progress;
|
||||
|
||||
/**
|
||||
* Type for progress callbacks during indexing. Return a value less than zero
|
||||
* to cancel the transfer.
|
||||
*
|
||||
* @param stats Structure containing information about the state of the transfer
|
||||
* @param payload Payload provided by caller
|
||||
*/
|
||||
typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
|
||||
|
@ -312,7 +312,7 @@ static int load_attr_blob_from_index(
|
||||
|
||||
entry = git_index_get_byindex(index, pos);
|
||||
|
||||
if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
|
||||
if (old_oid && git_oid__cmp(old_oid, &entry->oid) == 0)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
|
||||
|
21
src/blob.c
21
src/blob.c
@ -8,6 +8,7 @@
|
||||
#include "git2/common.h"
|
||||
#include "git2/object.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/odb_backend.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "blob.h"
|
||||
@ -17,32 +18,34 @@
|
||||
const void *git_blob_rawcontent(const git_blob *blob)
|
||||
{
|
||||
assert(blob);
|
||||
return blob->odb_object->raw.data;
|
||||
return git_odb_object_data(blob->odb_object);
|
||||
}
|
||||
|
||||
git_off_t git_blob_rawsize(const git_blob *blob)
|
||||
{
|
||||
assert(blob);
|
||||
return (git_off_t)blob->odb_object->raw.len;
|
||||
return (git_off_t)git_odb_object_size(blob->odb_object);
|
||||
}
|
||||
|
||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
|
||||
{
|
||||
return git_buf_set(
|
||||
buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
|
||||
buffer,
|
||||
git_odb_object_data(blob->odb_object),
|
||||
git_odb_object_size(blob->odb_object));
|
||||
}
|
||||
|
||||
void git_blob__free(git_blob *blob)
|
||||
void git_blob__free(void *blob)
|
||||
{
|
||||
git_odb_object_free(blob->odb_object);
|
||||
git_odb_object_free(((git_blob *)blob)->odb_object);
|
||||
git__free(blob);
|
||||
}
|
||||
|
||||
int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
|
||||
int git_blob__parse(void *blob, git_odb_object *odb_obj)
|
||||
{
|
||||
assert(blob);
|
||||
git_cached_obj_incref((git_cached_obj *)odb_obj);
|
||||
blob->odb_object = odb_obj;
|
||||
((git_blob *)blob)->odb_object = odb_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -314,8 +317,8 @@ int git_blob_is_binary(git_blob *blob)
|
||||
|
||||
assert(blob);
|
||||
|
||||
content.ptr = blob->odb_object->raw.data;
|
||||
content.size = min(blob->odb_object->raw.len, 4000);
|
||||
content.ptr = blob->odb_object->buffer;
|
||||
content.size = min(blob->odb_object->cached.size, 4000);
|
||||
|
||||
return git_buf_text_is_binary(&content);
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ struct git_blob {
|
||||
git_odb_object *odb_object;
|
||||
};
|
||||
|
||||
void git_blob__free(git_blob *blob);
|
||||
int git_blob__parse(git_blob *blob, git_odb_object *obj);
|
||||
void git_blob__free(void *blob);
|
||||
int git_blob__parse(void *blob, git_odb_object *obj);
|
||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
|
||||
|
||||
#endif
|
||||
|
24
src/branch.c
24
src/branch.c
@ -11,6 +11,7 @@
|
||||
#include "config.h"
|
||||
#include "refspec.h"
|
||||
#include "refs.h"
|
||||
#include "remote.h"
|
||||
|
||||
#include "git2/branch.h"
|
||||
|
||||
@ -283,12 +284,10 @@ int git_branch_upstream__name(
|
||||
if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
refspec = git_remote_fetchspec(remote);
|
||||
if (refspec == NULL
|
||||
|| refspec->src == NULL
|
||||
|| refspec->dst == NULL) {
|
||||
error = GIT_ENOTFOUND;
|
||||
goto cleanup;
|
||||
refspec = git_remote__matching_refspec(remote, merge_name);
|
||||
if (!refspec) {
|
||||
error = GIT_ENOTFOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
|
||||
@ -333,11 +332,8 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
|
||||
if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
|
||||
continue;
|
||||
|
||||
fetchspec = git_remote_fetchspec(remote);
|
||||
|
||||
/* Defensivly check that we have a fetchspec */
|
||||
if (fetchspec &&
|
||||
git_refspec_dst_matches(fetchspec, canonical_branch_name)) {
|
||||
fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name);
|
||||
if (fetchspec) {
|
||||
/* If we have not already set out yet, then set
|
||||
* it to the matching remote name. Otherwise
|
||||
* multiple remotes match this reference, and it
|
||||
@ -377,7 +373,7 @@ int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo
|
||||
if (buffer)
|
||||
git_buf_copy_cstr(buffer, buffer_len, &buf);
|
||||
|
||||
ret = git_buf_len(&buf) + 1;
|
||||
ret = (int)git_buf_len(&buf) + 1;
|
||||
git_buf_free(&buf);
|
||||
|
||||
return ret;
|
||||
@ -522,9 +518,9 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
|
||||
if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
|
||||
goto on_error;
|
||||
|
||||
fetchspec = git_remote_fetchspec(remote);
|
||||
fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
|
||||
git_buf_clear(&value);
|
||||
if (git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
|
||||
if (!fetchspec || git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_remote_free(remote);
|
||||
|
284
src/cache.c
284
src/cache.c
@ -11,100 +11,262 @@
|
||||
#include "thread-utils.h"
|
||||
#include "util.h"
|
||||
#include "cache.h"
|
||||
#include "odb.h"
|
||||
#include "object.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
|
||||
GIT__USE_OIDMAP
|
||||
|
||||
bool git_cache__enabled = true;
|
||||
ssize_t git_cache__max_storage = (256 * 1024 * 1024);
|
||||
git_atomic_ssize git_cache__current_storage = {0};
|
||||
|
||||
static size_t git_cache__max_object_size[8] = {
|
||||
0, /* GIT_OBJ__EXT1 */
|
||||
4096, /* GIT_OBJ_COMMIT */
|
||||
4096, /* GIT_OBJ_TREE */
|
||||
0, /* GIT_OBJ_BLOB */
|
||||
4096, /* GIT_OBJ_TAG */
|
||||
0, /* GIT_OBJ__EXT2 */
|
||||
0, /* GIT_OBJ_OFS_DELTA */
|
||||
0 /* GIT_OBJ_REF_DELTA */
|
||||
};
|
||||
|
||||
int git_cache_set_max_object_size(git_otype type, size_t size)
|
||||
{
|
||||
if (size < 8)
|
||||
size = 8;
|
||||
size = git__size_t_powerof2(size);
|
||||
if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) {
|
||||
giterr_set(GITERR_INVALID, "type out of range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cache->size_mask = size - 1;
|
||||
cache->lru_count = 0;
|
||||
cache->free_obj = free_ptr;
|
||||
|
||||
git_mutex_init(&cache->lock);
|
||||
|
||||
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
|
||||
GITERR_CHECK_ALLOC(cache->nodes);
|
||||
|
||||
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
|
||||
git_cache__max_object_size[type] = size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_cache_dump_stats(git_cache *cache)
|
||||
{
|
||||
git_cached_obj *object;
|
||||
|
||||
if (kh_size(cache->map) == 0)
|
||||
return;
|
||||
|
||||
printf("Cache %p: %d items cached, %d bytes\n",
|
||||
cache, kh_size(cache->map), (int)cache->used_memory);
|
||||
|
||||
kh_foreach_value(cache->map, object, {
|
||||
char oid_str[9];
|
||||
printf(" %s%c %s (%d)\n",
|
||||
git_object_type2string(object->type),
|
||||
object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
|
||||
git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
|
||||
(int)object->size
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
int git_cache_init(git_cache *cache)
|
||||
{
|
||||
cache->used_memory = 0;
|
||||
cache->map = git_oidmap_alloc();
|
||||
git_mutex_init(&cache->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called with lock */
|
||||
static void clear_cache(git_cache *cache)
|
||||
{
|
||||
git_cached_obj *evict = NULL;
|
||||
|
||||
if (kh_size(cache->map) == 0)
|
||||
return;
|
||||
|
||||
kh_foreach_value(cache->map, evict, {
|
||||
git_cached_obj_decref(evict);
|
||||
});
|
||||
|
||||
kh_clear(oid, cache->map);
|
||||
git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
|
||||
cache->used_memory = 0;
|
||||
}
|
||||
|
||||
void git_cache_clear(git_cache *cache)
|
||||
{
|
||||
if (git_mutex_lock(&cache->lock) < 0)
|
||||
return;
|
||||
|
||||
clear_cache(cache);
|
||||
|
||||
git_mutex_unlock(&cache->lock);
|
||||
}
|
||||
|
||||
void git_cache_free(git_cache *cache)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < (cache->size_mask + 1); ++i) {
|
||||
if (cache->nodes[i] != NULL)
|
||||
git_cached_obj_decref(cache->nodes[i], cache->free_obj);
|
||||
}
|
||||
git_cache_clear(cache);
|
||||
|
||||
git_oidmap_free(cache->map);
|
||||
git_mutex_free(&cache->lock);
|
||||
git__free(cache->nodes);
|
||||
}
|
||||
|
||||
void *git_cache_get(git_cache *cache, const git_oid *oid)
|
||||
/* Called with lock */
|
||||
static void cache_evict_entries(git_cache *cache)
|
||||
{
|
||||
uint32_t hash;
|
||||
git_cached_obj *node = NULL, *result = NULL;
|
||||
uint32_t seed = rand();
|
||||
size_t evict_count = 8;
|
||||
ssize_t evicted_memory = 0;
|
||||
|
||||
memcpy(&hash, oid->id, sizeof(hash));
|
||||
|
||||
if (git_mutex_lock(&cache->lock)) {
|
||||
giterr_set(GITERR_THREAD, "unable to lock cache mutex");
|
||||
return NULL;
|
||||
/* do not infinite loop if there's not enough entries to evict */
|
||||
if (evict_count > kh_size(cache->map)) {
|
||||
clear_cache(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
node = cache->nodes[hash & cache->size_mask];
|
||||
while (evict_count > 0) {
|
||||
khiter_t pos = seed++ % kh_end(cache->map);
|
||||
|
||||
if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {
|
||||
git_cached_obj_incref(node);
|
||||
result = node;
|
||||
if (kh_exist(cache->map, pos)) {
|
||||
git_cached_obj *evict = kh_val(cache->map, pos);
|
||||
|
||||
evict_count--;
|
||||
evicted_memory += evict->size;
|
||||
git_cached_obj_decref(evict);
|
||||
|
||||
kh_del(oid, cache->map, pos);
|
||||
}
|
||||
}
|
||||
git_mutex_unlock(&cache->lock);
|
||||
|
||||
return result;
|
||||
cache->used_memory -= evicted_memory;
|
||||
git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory);
|
||||
}
|
||||
|
||||
void *git_cache_try_store(git_cache *cache, void *_entry)
|
||||
static bool cache_should_store(git_otype object_type, size_t object_size)
|
||||
{
|
||||
git_cached_obj *entry = _entry;
|
||||
uint32_t hash;
|
||||
size_t max_size = git_cache__max_object_size[object_type];
|
||||
return git_cache__enabled && object_size < max_size;
|
||||
}
|
||||
|
||||
memcpy(&hash, &entry->oid, sizeof(uint32_t));
|
||||
static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
|
||||
{
|
||||
khiter_t pos;
|
||||
git_cached_obj *entry = NULL;
|
||||
|
||||
if (git_mutex_lock(&cache->lock)) {
|
||||
giterr_set(GITERR_THREAD, "unable to lock cache mutex");
|
||||
if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
git_cached_obj *node = cache->nodes[hash & cache->size_mask];
|
||||
pos = kh_get(oid, cache->map, oid);
|
||||
if (pos != kh_end(cache->map)) {
|
||||
entry = kh_val(cache->map, pos);
|
||||
|
||||
/* increase the refcount on this object, because
|
||||
* the cache now owns it */
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
if (node == NULL) {
|
||||
cache->nodes[hash & cache->size_mask] = entry;
|
||||
} else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
|
||||
git_cached_obj_decref(entry, cache->free_obj);
|
||||
entry = node;
|
||||
if (flags && entry->flags != flags) {
|
||||
entry = NULL;
|
||||
} else {
|
||||
git_cached_obj_decref(node, cache->free_obj);
|
||||
cache->nodes[hash & cache->size_mask] = entry;
|
||||
git_cached_obj_incref(entry);
|
||||
}
|
||||
|
||||
/* increase the refcount again, because we are
|
||||
* returning it to the user */
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
}
|
||||
|
||||
git_mutex_unlock(&cache->lock);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void *cache_store(git_cache *cache, git_cached_obj *entry)
|
||||
{
|
||||
khiter_t pos;
|
||||
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
if (!cache_should_store(entry->type, entry->size))
|
||||
return entry;
|
||||
|
||||
if (git_mutex_lock(&cache->lock) < 0)
|
||||
return entry;
|
||||
|
||||
/* soften the load on the cache */
|
||||
if (git_cache__current_storage.val > git_cache__max_storage)
|
||||
cache_evict_entries(cache);
|
||||
|
||||
pos = kh_get(oid, cache->map, &entry->oid);
|
||||
|
||||
/* not found */
|
||||
if (pos == kh_end(cache->map)) {
|
||||
int rval;
|
||||
|
||||
pos = kh_put(oid, cache->map, &entry->oid, &rval);
|
||||
if (rval >= 0) {
|
||||
kh_key(cache->map, pos) = &entry->oid;
|
||||
kh_val(cache->map, pos) = entry;
|
||||
git_cached_obj_incref(entry);
|
||||
cache->used_memory += entry->size;
|
||||
git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
|
||||
}
|
||||
}
|
||||
/* found */
|
||||
else {
|
||||
git_cached_obj *stored_entry = kh_val(cache->map, pos);
|
||||
|
||||
if (stored_entry->flags == entry->flags) {
|
||||
git_cached_obj_decref(entry);
|
||||
git_cached_obj_incref(stored_entry);
|
||||
entry = stored_entry;
|
||||
} else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
|
||||
entry->flags == GIT_CACHE_STORE_PARSED) {
|
||||
git_cached_obj_decref(stored_entry);
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
kh_key(cache->map, pos) = &entry->oid;
|
||||
kh_val(cache->map, pos) = entry;
|
||||
} else {
|
||||
/* NO OP */
|
||||
}
|
||||
}
|
||||
|
||||
git_mutex_unlock(&cache->lock);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
|
||||
{
|
||||
entry->cached.flags = GIT_CACHE_STORE_RAW;
|
||||
return cache_store(cache, (git_cached_obj *)entry);
|
||||
}
|
||||
|
||||
void *git_cache_store_parsed(git_cache *cache, git_object *entry)
|
||||
{
|
||||
entry->cached.flags = GIT_CACHE_STORE_PARSED;
|
||||
return cache_store(cache, (git_cached_obj *)entry);
|
||||
}
|
||||
|
||||
git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
|
||||
{
|
||||
return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
|
||||
}
|
||||
|
||||
git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
|
||||
{
|
||||
return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
|
||||
}
|
||||
|
||||
void *git_cache_get_any(git_cache *cache, const git_oid *oid)
|
||||
{
|
||||
return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
|
||||
}
|
||||
|
||||
void git_cached_obj_decref(void *_obj)
|
||||
{
|
||||
git_cached_obj *obj = _obj;
|
||||
|
||||
if (git_atomic_dec(&obj->refcount) == 0) {
|
||||
switch (obj->flags) {
|
||||
case GIT_CACHE_STORE_RAW:
|
||||
git_odb_object__free(_obj);
|
||||
break;
|
||||
|
||||
case GIT_CACHE_STORE_PARSED:
|
||||
git_object__free(_obj);
|
||||
break;
|
||||
|
||||
default:
|
||||
git__free(_obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
55
src/cache.h
55
src/cache.h
@ -12,30 +12,49 @@
|
||||
#include "git2/odb.h"
|
||||
|
||||
#include "thread-utils.h"
|
||||
#include "oidmap.h"
|
||||
|
||||
#define GIT_DEFAULT_CACHE_SIZE 128
|
||||
|
||||
typedef void (*git_cached_obj_freeptr)(void *);
|
||||
enum {
|
||||
GIT_CACHE_STORE_ANY = 0,
|
||||
GIT_CACHE_STORE_RAW = 1,
|
||||
GIT_CACHE_STORE_PARSED = 2
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
git_oid oid;
|
||||
git_oid oid;
|
||||
int16_t type; /* git_otype value */
|
||||
uint16_t flags; /* GIT_CACHE_STORE value */
|
||||
size_t size;
|
||||
git_atomic refcount;
|
||||
} git_cached_obj;
|
||||
|
||||
typedef struct {
|
||||
git_cached_obj **nodes;
|
||||
git_mutex lock;
|
||||
|
||||
unsigned int lru_count;
|
||||
size_t size_mask;
|
||||
git_cached_obj_freeptr free_obj;
|
||||
git_oidmap *map;
|
||||
git_mutex lock;
|
||||
ssize_t used_memory;
|
||||
} git_cache;
|
||||
|
||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);
|
||||
void git_cache_free(git_cache *cache);
|
||||
extern bool git_cache__enabled;
|
||||
extern ssize_t git_cache__max_storage;
|
||||
extern git_atomic_ssize git_cache__current_storage;
|
||||
|
||||
void *git_cache_try_store(git_cache *cache, void *entry);
|
||||
void *git_cache_get(git_cache *cache, const git_oid *oid);
|
||||
int git_cache_set_max_object_size(git_otype type, size_t size);
|
||||
|
||||
int git_cache_init(git_cache *cache);
|
||||
void git_cache_free(git_cache *cache);
|
||||
void git_cache_clear(git_cache *cache);
|
||||
|
||||
void *git_cache_store_raw(git_cache *cache, git_odb_object *entry);
|
||||
void *git_cache_store_parsed(git_cache *cache, git_object *entry);
|
||||
|
||||
git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid);
|
||||
git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid);
|
||||
void *git_cache_get_any(git_cache *cache, const git_oid *oid);
|
||||
|
||||
GIT_INLINE(size_t) git_cache_size(git_cache *cache)
|
||||
{
|
||||
return (size_t)kh_size(cache->map);
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_cached_obj_incref(void *_obj)
|
||||
{
|
||||
@ -43,12 +62,6 @@ GIT_INLINE(void) git_cached_obj_incref(void *_obj)
|
||||
git_atomic_inc(&obj->refcount);
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj)
|
||||
{
|
||||
git_cached_obj *obj = _obj;
|
||||
|
||||
if (git_atomic_dec(&obj->refcount) == 0)
|
||||
free_obj(obj);
|
||||
}
|
||||
void git_cached_obj_decref(void *_obj);
|
||||
|
||||
#endif
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "git2/config.h"
|
||||
#include "git2/diff.h"
|
||||
#include "git2/submodule.h"
|
||||
#include "git2/sys/index.h"
|
||||
|
||||
#include "refs.h"
|
||||
#include "repository.h"
|
||||
@ -119,6 +120,7 @@ static bool checkout_is_workdir_modified(
|
||||
const git_index_entry *wditem)
|
||||
{
|
||||
git_oid oid;
|
||||
const git_index_entry *ie;
|
||||
|
||||
/* handle "modified" submodule */
|
||||
if (wditem->mode == GIT_FILEMODE_COMMIT) {
|
||||
@ -137,7 +139,18 @@ static bool checkout_is_workdir_modified(
|
||||
if (!sm_oid)
|
||||
return false;
|
||||
|
||||
return (git_oid_cmp(&baseitem->oid, sm_oid) != 0);
|
||||
return (git_oid__cmp(&baseitem->oid, sm_oid) != 0);
|
||||
}
|
||||
|
||||
/* Look at the cache to decide if the workdir is modified. If not,
|
||||
* we can simply compare the oid in the cache to the baseitem instead
|
||||
* of hashing the file.
|
||||
*/
|
||||
if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
|
||||
if (wditem->mtime.seconds == ie->mtime.seconds &&
|
||||
wditem->mtime.nanoseconds == ie->mtime.nanoseconds &&
|
||||
wditem->file_size == ie->file_size)
|
||||
return (git_oid__cmp(&baseitem->oid, &ie->oid) != 0);
|
||||
}
|
||||
|
||||
/* depending on where base is coming from, we may or may not know
|
||||
@ -151,7 +164,7 @@ static bool checkout_is_workdir_modified(
|
||||
wditem->file_size, &oid) < 0)
|
||||
return false;
|
||||
|
||||
return (git_oid_cmp(&baseitem->oid, &oid) != 0);
|
||||
return (git_oid__cmp(&baseitem->oid, &oid) != 0);
|
||||
}
|
||||
|
||||
#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
|
||||
@ -454,6 +467,7 @@ static int checkout_action(
|
||||
int cmp = -1, act;
|
||||
int (*strcomp)(const char *, const char *) = data->diff->strcomp;
|
||||
int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
|
||||
int error;
|
||||
|
||||
/* move workdir iterator to follow along with deltas */
|
||||
|
||||
@ -477,8 +491,11 @@ static int checkout_action(
|
||||
if (cmp == 0) {
|
||||
if (wd->mode == GIT_FILEMODE_TREE) {
|
||||
/* case 2 - entry prefixed by workdir tree */
|
||||
if (git_iterator_advance_into(&wd, workdir) < 0)
|
||||
goto fail;
|
||||
if ((error = git_iterator_advance_into(&wd, workdir)) < 0) {
|
||||
if (error != GIT_ENOTFOUND ||
|
||||
git_iterator_advance(&wd, workdir) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*wditem_ptr = wd;
|
||||
continue;
|
||||
@ -698,8 +715,8 @@ static int blob_content_to_file(
|
||||
git_vector filters = GIT_VECTOR_INIT;
|
||||
|
||||
/* Create a fake git_buf from the blob raw data... */
|
||||
filtered.ptr = blob->odb_object->raw.data;
|
||||
filtered.size = blob->odb_object->raw.len;
|
||||
filtered.ptr = (void *)git_blob_rawcontent(blob);
|
||||
filtered.size = (size_t)git_blob_rawsize(blob);
|
||||
/* ... and make sure it doesn't get unexpectedly freed */
|
||||
dont_free_filtered = true;
|
||||
|
||||
@ -1107,7 +1124,6 @@ static int checkout_data_init(
|
||||
git_checkout_opts *proposed)
|
||||
{
|
||||
int error = 0;
|
||||
git_config *cfg;
|
||||
git_repository *repo = git_iterator_owner(target);
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
@ -1120,9 +1136,6 @@ static int checkout_data_init(
|
||||
if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
|
||||
return error;
|
||||
|
||||
data->repo = repo;
|
||||
|
||||
GITERR_CHECK_VERSION(
|
||||
@ -1135,7 +1148,10 @@ static int checkout_data_init(
|
||||
|
||||
/* refresh config and index content unless NO_REFRESH is given */
|
||||
if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
|
||||
if ((error = git_config_refresh(cfg)) < 0)
|
||||
git_config *cfg;
|
||||
|
||||
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
|
||||
(error = git_config_refresh(cfg)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* if we are checking out the index, don't reload,
|
||||
@ -1172,19 +1188,13 @@ static int checkout_data_init(
|
||||
|
||||
data->pfx = git_pathspec_prefix(&data->opts.paths);
|
||||
|
||||
error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks");
|
||||
if (error < 0) {
|
||||
if (error != GIT_ENOTFOUND)
|
||||
goto cleanup;
|
||||
|
||||
/* If "core.symlinks" is not found anywhere, default to true. */
|
||||
data->can_symlink = true;
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
if ((error = git_repository__cvar(
|
||||
&data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!data->opts.baseline) {
|
||||
data->opts_free_baseline = true;
|
||||
|
||||
error = checkout_lookup_head_tree(&data->opts.baseline, repo);
|
||||
|
||||
if (error == GIT_EORPHANEDHEAD) {
|
||||
|
20
src/clone.c
20
src/clone.c
@ -132,7 +132,7 @@ static int reference_matches_remote_head(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (git_oid_cmp(&head_info->remote_head_oid, &oid) == 0) {
|
||||
if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) {
|
||||
/* Determine the local reference name from the remote tracking one */
|
||||
if (git_refspec_transform_l(
|
||||
&head_info->branchname,
|
||||
@ -187,6 +187,7 @@ static int get_head_callback(git_remote_head *head, void *payload)
|
||||
static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
||||
{
|
||||
int retcode = -1;
|
||||
git_refspec dummy_spec;
|
||||
git_remote_head *remote_head;
|
||||
struct head_info head_info;
|
||||
git_buf remote_master_name = GIT_BUF_INIT;
|
||||
@ -211,9 +212,14 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
||||
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
|
||||
git_buf_init(&head_info.branchname, 16);
|
||||
head_info.repo = repo;
|
||||
head_info.refspec = git_remote_fetchspec(remote);
|
||||
head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
|
||||
head_info.found = 0;
|
||||
|
||||
if (head_info.refspec == NULL) {
|
||||
memset(&dummy_spec, 0, sizeof(git_refspec));
|
||||
head_info.refspec = &dummy_spec;
|
||||
}
|
||||
|
||||
/* Determine the remote tracking reference name from the local master */
|
||||
if (git_refspec_transform_r(
|
||||
&remote_master_name,
|
||||
@ -317,12 +323,14 @@ static int create_and_configure_origin(
|
||||
(error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (options->fetch_spec &&
|
||||
(error = git_remote_set_fetchspec(origin, options->fetch_spec)) < 0)
|
||||
goto on_error;
|
||||
if (options->fetch_spec) {
|
||||
git_remote_clear_refspecs(origin);
|
||||
if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (options->push_spec &&
|
||||
(error = git_remote_set_pushspec(origin, options->push_spec)) < 0)
|
||||
(error = git_remote_add_push(origin, options->push_spec)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (options->pushurl &&
|
||||
|
104
src/commit.c
104
src/commit.c
@ -9,6 +9,7 @@
|
||||
#include "git2/object.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/signature.h"
|
||||
#include "git2/sys/commit.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "odb.h"
|
||||
@ -30,8 +31,10 @@ static void clear_parents(git_commit *commit)
|
||||
git_vector_clear(&commit->parent_ids);
|
||||
}
|
||||
|
||||
void git_commit__free(git_commit *commit)
|
||||
void git_commit__free(void *_commit)
|
||||
{
|
||||
git_commit *commit = _commit;
|
||||
|
||||
clear_parents(commit);
|
||||
git_vector_free(&commit->parent_ids);
|
||||
|
||||
@ -44,16 +47,16 @@ void git_commit__free(git_commit *commit)
|
||||
}
|
||||
|
||||
int git_commit_create_v(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...)
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, res;
|
||||
@ -76,30 +79,29 @@ int git_commit_create_v(
|
||||
return res;
|
||||
}
|
||||
|
||||
int git_commit_create(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
const git_commit *parents[])
|
||||
int git_commit_create_from_oids(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_oid *tree,
|
||||
int parent_count,
|
||||
const git_oid *parents[])
|
||||
{
|
||||
git_buf commit = GIT_BUF_INIT;
|
||||
int i;
|
||||
git_odb *odb;
|
||||
|
||||
assert(oid && repo && tree && parent_count >= 0);
|
||||
assert(git_object_owner((const git_object *)tree) == repo);
|
||||
|
||||
git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
|
||||
git_oid__writebuf(&commit, "tree ", tree);
|
||||
|
||||
for (i = 0; i < parent_count; ++i) {
|
||||
assert(git_object_owner((const git_object *)parents[i]) == repo);
|
||||
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
|
||||
}
|
||||
for (i = 0; i < parent_count; ++i)
|
||||
git_oid__writebuf(&commit, "parent ", parents[i]);
|
||||
|
||||
git_signature__writebuf(&commit, "author ", author);
|
||||
git_signature__writebuf(&commit, "committer ", committer);
|
||||
@ -131,10 +133,46 @@ on_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
|
||||
int git_commit_create(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
const git_commit *parents[])
|
||||
{
|
||||
const char *buffer = data;
|
||||
const char *buffer_end = (const char *)data + len;
|
||||
int retval, i;
|
||||
const git_oid **parent_oids;
|
||||
|
||||
assert(parent_count >= 0);
|
||||
|
||||
parent_oids = git__malloc(parent_count * sizeof(git_oid *));
|
||||
GITERR_CHECK_ALLOC(parent_oids);
|
||||
|
||||
for (i = 0; i < parent_count; ++i) {
|
||||
assert(git_object_owner((const git_object *)parents[i]) == repo);
|
||||
parent_oids[i] = git_object_id((const git_object *)parents[i]);
|
||||
}
|
||||
|
||||
retval = git_commit_create_from_oids(
|
||||
oid, repo, update_ref, author, committer,
|
||||
message_encoding, message,
|
||||
git_object_id((const git_object *)tree), parent_count, parent_oids);
|
||||
|
||||
git__free((void *)parent_oids);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int git_commit__parse(void *_commit, git_odb_object *odb_obj)
|
||||
{
|
||||
git_commit *commit = _commit;
|
||||
const char *buffer = git_odb_object_data(odb_obj);
|
||||
const char *buffer_end = buffer + git_odb_object_size(odb_obj);
|
||||
git_oid parent_id;
|
||||
|
||||
if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
|
||||
@ -206,12 +244,6 @@ bad_buffer:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_commit__parse(git_commit *commit, git_odb_object *obj)
|
||||
{
|
||||
assert(commit);
|
||||
return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len);
|
||||
}
|
||||
|
||||
#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
|
||||
_rvalue git_commit_##_name(const git_commit *commit) \
|
||||
{\
|
||||
|
@ -27,8 +27,7 @@ struct git_commit {
|
||||
char *message;
|
||||
};
|
||||
|
||||
void git_commit__free(git_commit *c);
|
||||
int git_commit__parse(git_commit *commit, git_odb_object *obj);
|
||||
void git_commit__free(void *commit);
|
||||
int git_commit__parse(void *commit, git_odb_object *obj);
|
||||
|
||||
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len);
|
||||
#endif
|
||||
|
@ -100,12 +100,15 @@ git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
|
||||
return item;
|
||||
}
|
||||
|
||||
static int commit_quick_parse(git_revwalk *walk, git_commit_list_node *commit, git_rawobj *raw)
|
||||
static int commit_quick_parse(
|
||||
git_revwalk *walk,
|
||||
git_commit_list_node *commit,
|
||||
const uint8_t *buffer,
|
||||
size_t buffer_len)
|
||||
{
|
||||
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
|
||||
unsigned char *buffer = raw->data;
|
||||
unsigned char *buffer_end = buffer + raw->len;
|
||||
unsigned char *parents_start, *committer_start;
|
||||
const uint8_t *buffer_end = buffer + buffer_len;
|
||||
const uint8_t *parents_start, *committer_start;
|
||||
int i, parents = 0;
|
||||
int commit_time;
|
||||
|
||||
@ -124,7 +127,7 @@ static int commit_quick_parse(git_revwalk *walk, git_commit_list_node *commit, g
|
||||
for (i = 0; i < parents; ++i) {
|
||||
git_oid oid;
|
||||
|
||||
if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)
|
||||
if (git_oid_fromstr(&oid, (const char *)buffer + strlen("parent ")) < 0)
|
||||
return -1;
|
||||
|
||||
commit->parents[i] = git_revwalk__commit_lookup(walk, &oid);
|
||||
@ -182,11 +185,14 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
|
||||
if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
|
||||
return error;
|
||||
|
||||
if (obj->raw.type != GIT_OBJ_COMMIT) {
|
||||
if (obj->cached.type != GIT_OBJ_COMMIT) {
|
||||
giterr_set(GITERR_INVALID, "Object is no commit object");
|
||||
error = -1;
|
||||
} else
|
||||
error = commit_quick_parse(walk, commit, &obj->raw);
|
||||
error = commit_quick_parse(
|
||||
walk, commit,
|
||||
(const uint8_t *)git_odb_object_data(obj),
|
||||
git_odb_object_size(obj));
|
||||
|
||||
git_odb_object_free(obj);
|
||||
return error;
|
||||
|
18
src/config.c
18
src/config.c
@ -9,6 +9,7 @@
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "git2/sys/config.h"
|
||||
#include "vector.h"
|
||||
#include "buf_text.h"
|
||||
#include "config_file.h"
|
||||
@ -292,6 +293,9 @@ int git_config_refresh(git_config *cfg)
|
||||
error = file->refresh(file);
|
||||
}
|
||||
|
||||
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
|
||||
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -359,6 +363,7 @@ int git_config_set_bool(git_config *cfg, const char *name, int value)
|
||||
|
||||
int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
||||
{
|
||||
int error;
|
||||
git_config_backend *file;
|
||||
file_internal *internal;
|
||||
|
||||
@ -368,9 +373,20 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
||||
}
|
||||
|
||||
internal = git_vector_get(&cfg->files, 0);
|
||||
if (!internal) {
|
||||
/* Should we auto-vivify .git/config? Tricky from this location */
|
||||
giterr_set(GITERR_CONFIG, "Cannot set value when no config files exist");
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
file = internal->file;
|
||||
|
||||
return file->set(file, name, value);
|
||||
error = file->set(file, name, value);
|
||||
|
||||
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
|
||||
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/***********
|
||||
|
@ -51,9 +51,22 @@ static git_cvar_map _cvar_map_autocrlf[] = {
|
||||
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
|
||||
};
|
||||
|
||||
/*
|
||||
* Generic map for integer values
|
||||
*/
|
||||
static git_cvar_map _cvar_map_int[] = {
|
||||
{GIT_CVAR_INT32, NULL, 0},
|
||||
};
|
||||
|
||||
static struct map_data _cvar_maps[] = {
|
||||
{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
|
||||
{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
|
||||
{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT},
|
||||
{"core.symlinks", NULL, 0, GIT_SYMLINKS_DEFAULT },
|
||||
{"core.ignorecase", NULL, 0, GIT_IGNORECASE_DEFAULT },
|
||||
{"core.filemode", NULL, 0, GIT_FILEMODE_DEFAULT },
|
||||
{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
|
||||
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
|
||||
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
|
||||
};
|
||||
|
||||
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
||||
@ -69,12 +82,16 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = git_config_get_mapped(out,
|
||||
config, data->cvar_name, data->maps, data->map_count);
|
||||
if (data->maps)
|
||||
error = git_config_get_mapped(
|
||||
out, config, data->cvar_name, data->maps, data->map_count);
|
||||
else
|
||||
error = git_config_get_bool(out, config, data->cvar_name);
|
||||
|
||||
if (error == GIT_ENOTFOUND)
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
*out = data->default_value;
|
||||
|
||||
}
|
||||
else if (error < 0)
|
||||
return error;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "buffer.h"
|
||||
#include "buf_text.h"
|
||||
#include "git2/config.h"
|
||||
#include "git2/sys/config.h"
|
||||
#include "git2/types.h"
|
||||
#include "strmap.h"
|
||||
|
||||
@ -481,8 +482,10 @@ static int config_set_multivar(
|
||||
|
||||
pos = git_strmap_lookup_index(b->values, key);
|
||||
if (!git_strmap_valid_index(b->values, pos)) {
|
||||
/* If we don't have it, behave like a normal set */
|
||||
result = config_set(cfg, name, value);
|
||||
git__free(key);
|
||||
return GIT_ENOTFOUND;
|
||||
return result;
|
||||
}
|
||||
|
||||
var = git_strmap_value_at(b->values, pos);
|
||||
|
678
src/diff.c
678
src/diff.c
@ -14,6 +14,8 @@
|
||||
|
||||
#define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
|
||||
#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
|
||||
#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \
|
||||
(VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
|
||||
|
||||
static git_diff_delta *diff_delta__alloc(
|
||||
git_diff_list *diff,
|
||||
@ -194,21 +196,21 @@ static git_diff_delta *diff_delta__last_for_item(
|
||||
switch (delta->status) {
|
||||
case GIT_DELTA_UNMODIFIED:
|
||||
case GIT_DELTA_DELETED:
|
||||
if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0)
|
||||
if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0)
|
||||
return delta;
|
||||
break;
|
||||
case GIT_DELTA_ADDED:
|
||||
if (git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
|
||||
if (git_oid__cmp(&delta->new_file.oid, &item->oid) == 0)
|
||||
return delta;
|
||||
break;
|
||||
case GIT_DELTA_UNTRACKED:
|
||||
if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
|
||||
git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
|
||||
git_oid__cmp(&delta->new_file.oid, &item->oid) == 0)
|
||||
return delta;
|
||||
break;
|
||||
case GIT_DELTA_MODIFIED:
|
||||
if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0 ||
|
||||
git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
|
||||
if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0 ||
|
||||
git_oid__cmp(&delta->new_file.oid, &item->oid) == 0)
|
||||
return delta;
|
||||
break;
|
||||
default:
|
||||
@ -267,67 +269,157 @@ static int config_bool(git_config *cfg, const char *name, int defvalue)
|
||||
return val;
|
||||
}
|
||||
|
||||
static git_diff_list *git_diff_list_alloc(
|
||||
git_repository *repo, const git_diff_options *opts)
|
||||
static int config_int(git_config *cfg, const char *name, int defvalue)
|
||||
{
|
||||
git_config *cfg;
|
||||
int val = defvalue;
|
||||
|
||||
if (git_config_get_int32(&val, cfg, name) < 0)
|
||||
giterr_clear();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static const char *diff_mnemonic_prefix(
|
||||
git_iterator_type_t type, bool left_side)
|
||||
{
|
||||
const char *pfx = "";
|
||||
|
||||
switch (type) {
|
||||
case GIT_ITERATOR_TYPE_EMPTY: pfx = "c"; break;
|
||||
case GIT_ITERATOR_TYPE_TREE: pfx = "c"; break;
|
||||
case GIT_ITERATOR_TYPE_INDEX: pfx = "i"; break;
|
||||
case GIT_ITERATOR_TYPE_WORKDIR: pfx = "w"; break;
|
||||
case GIT_ITERATOR_TYPE_FS: pfx = left_side ? "1" : "2"; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* note: without a deeper look at pathspecs, there is no easy way
|
||||
* to get the (o)bject / (w)ork tree mnemonics working...
|
||||
*/
|
||||
|
||||
return pfx;
|
||||
}
|
||||
|
||||
static git_diff_list *diff_list_alloc(
|
||||
git_repository *repo,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter)
|
||||
{
|
||||
git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
|
||||
if (diff == NULL)
|
||||
if (!diff)
|
||||
return NULL;
|
||||
|
||||
assert(repo && old_iter && new_iter);
|
||||
|
||||
GIT_REFCOUNT_INC(diff);
|
||||
diff->repo = repo;
|
||||
diff->old_src = old_iter->type;
|
||||
diff->new_src = new_iter->type;
|
||||
memcpy(&diff->opts, &dflt, sizeof(diff->opts));
|
||||
|
||||
if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
|
||||
git_pool_init(&diff->pool, 1, 0) < 0)
|
||||
goto fail;
|
||||
git_pool_init(&diff->pool, 1, 0) < 0) {
|
||||
git_diff_list_free(diff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Use case-insensitive compare if either iterator has
|
||||
* the ignore_case bit set */
|
||||
if (!git_iterator_ignore_case(old_iter) &&
|
||||
!git_iterator_ignore_case(new_iter)) {
|
||||
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
|
||||
diff->strcomp = git__strcmp;
|
||||
diff->strncomp = git__strncmp;
|
||||
diff->pfxcomp = git__prefixcmp;
|
||||
diff->entrycomp = git_index_entry__cmp;
|
||||
} else {
|
||||
diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
|
||||
diff->strcomp = git__strcasecmp;
|
||||
diff->strncomp = git__strncasecmp;
|
||||
diff->pfxcomp = git__prefixcmp_icase;
|
||||
diff->entrycomp = git_index_entry__cmp_icase;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
static int diff_list_apply_options(
|
||||
git_diff_list *diff,
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
git_config *cfg;
|
||||
git_repository *repo = diff->repo;
|
||||
git_pool *pool = &diff->pool;
|
||||
int val;
|
||||
|
||||
if (opts) {
|
||||
/* copy user options (except case sensitivity info from iterators) */
|
||||
bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE);
|
||||
memcpy(&diff->opts, opts, sizeof(diff->opts));
|
||||
DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
|
||||
|
||||
/* initialize pathspec from options */
|
||||
if (git_pathspec_init(&diff->pathspec, &opts->pathspec, pool) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
|
||||
diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
|
||||
|
||||
/* load config values that affect diff behavior */
|
||||
if (git_repository_config__weakptr(&cfg, repo) < 0)
|
||||
goto fail;
|
||||
if (config_bool(cfg, "core.symlinks", 1))
|
||||
return -1;
|
||||
|
||||
if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val)
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
|
||||
if (config_bool(cfg, "core.ignorestat", 0))
|
||||
|
||||
if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val)
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
|
||||
if (config_bool(cfg, "core.filemode", 1))
|
||||
|
||||
if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
|
||||
!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val)
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
|
||||
if (config_bool(cfg, "core.trustctime", 1))
|
||||
|
||||
if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val)
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
|
||||
|
||||
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
|
||||
|
||||
/* TODO: there are certain config settings where even if we were
|
||||
* not given an options structure, we need the diff list to have one
|
||||
* so that we can store the altered default values.
|
||||
*
|
||||
* - diff.ignoreSubmodules
|
||||
* - diff.mnemonicprefix
|
||||
* - diff.noprefix
|
||||
*/
|
||||
/* If not given explicit `opts`, check `diff.xyz` configs */
|
||||
if (!opts) {
|
||||
diff->opts.context_lines = config_int(cfg, "diff.context", 3);
|
||||
|
||||
if (opts == NULL) {
|
||||
/* Make sure we default to 3 lines */
|
||||
diff->opts.context_lines = 3;
|
||||
return diff;
|
||||
if (config_bool(cfg, "diff.ignoreSubmodules", 0))
|
||||
diff->opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
|
||||
}
|
||||
|
||||
memcpy(&diff->opts, opts, sizeof(git_diff_options));
|
||||
/* if either prefix is not set, figure out appropriate value */
|
||||
if (!diff->opts.old_prefix || !diff->opts.new_prefix) {
|
||||
const char *use_old = DIFF_OLD_PREFIX_DEFAULT;
|
||||
const char *use_new = DIFF_NEW_PREFIX_DEFAULT;
|
||||
|
||||
if(opts->flags & GIT_DIFF_IGNORE_FILEMODE)
|
||||
diff->diffcaps = diff->diffcaps & ~GIT_DIFFCAPS_TRUST_MODE_BITS;
|
||||
if (config_bool(cfg, "diff.noprefix", 0)) {
|
||||
use_old = use_new = "";
|
||||
} else if (config_bool(cfg, "diff.mnemonicprefix", 0)) {
|
||||
use_old = diff_mnemonic_prefix(diff->old_src, true);
|
||||
use_new = diff_mnemonic_prefix(diff->new_src, false);
|
||||
}
|
||||
|
||||
/* pathspec init will do nothing for empty pathspec */
|
||||
if (git_pathspec_init(&diff->pathspec, &opts->pathspec, &diff->pool) < 0)
|
||||
goto fail;
|
||||
|
||||
/* TODO: handle config diff.mnemonicprefix, diff.noprefix */
|
||||
|
||||
diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
|
||||
opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
|
||||
diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
|
||||
opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
|
||||
if (!diff->opts.old_prefix)
|
||||
diff->opts.old_prefix = use_old;
|
||||
if (!diff->opts.new_prefix)
|
||||
diff->opts.new_prefix = use_new;
|
||||
}
|
||||
|
||||
/* strdup prefix from pool so we're not dependent on external data */
|
||||
diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix);
|
||||
diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix);
|
||||
if (!diff->opts.old_prefix || !diff->opts.new_prefix)
|
||||
goto fail;
|
||||
return -1;
|
||||
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
|
||||
const char *swap = diff->opts.old_prefix;
|
||||
@ -335,15 +427,7 @@ static git_diff_list *git_diff_list_alloc(
|
||||
diff->opts.new_prefix = swap;
|
||||
}
|
||||
|
||||
/* INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
|
||||
diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
|
||||
|
||||
return diff;
|
||||
|
||||
fail:
|
||||
git_diff_list_free(diff);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void diff_list_free(git_diff_list *diff)
|
||||
@ -445,24 +529,30 @@ cleanup:
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_iterator *old_iter;
|
||||
git_iterator *new_iter;
|
||||
const git_index_entry *oitem;
|
||||
const git_index_entry *nitem;
|
||||
git_buf ignore_prefix;
|
||||
} diff_in_progress;
|
||||
|
||||
#define MODE_BITS_MASK 0000777
|
||||
|
||||
static int maybe_modified(
|
||||
git_iterator *old_iter,
|
||||
const git_index_entry *oitem,
|
||||
git_iterator *new_iter,
|
||||
const git_index_entry *nitem,
|
||||
git_diff_list *diff)
|
||||
git_diff_list *diff,
|
||||
diff_in_progress *info)
|
||||
{
|
||||
git_oid noid, *use_noid = NULL;
|
||||
git_delta_t status = GIT_DELTA_MODIFIED;
|
||||
const git_index_entry *oitem = info->oitem;
|
||||
const git_index_entry *nitem = info->nitem;
|
||||
unsigned int omode = oitem->mode;
|
||||
unsigned int nmode = nitem->mode;
|
||||
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
|
||||
bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
|
||||
const char *matched_pathspec;
|
||||
|
||||
GIT_UNUSED(old_iter);
|
||||
|
||||
if (!git_pathspec_match_path(
|
||||
&diff->pathspec, oitem->path,
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
|
||||
@ -607,35 +697,248 @@ static bool entry_is_prefixed(
|
||||
item->path[pathlen] == '/');
|
||||
}
|
||||
|
||||
static int diff_list_init_from_iterators(
|
||||
git_diff_list *diff,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter)
|
||||
static int diff_scan_inside_untracked_dir(
|
||||
git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
|
||||
{
|
||||
diff->old_src = old_iter->type;
|
||||
diff->new_src = new_iter->type;
|
||||
int error = 0;
|
||||
git_buf base = GIT_BUF_INIT;
|
||||
bool is_ignored;
|
||||
|
||||
/* Use case-insensitive compare if either iterator has
|
||||
* the ignore_case bit set */
|
||||
if (!git_iterator_ignore_case(old_iter) &&
|
||||
!git_iterator_ignore_case(new_iter))
|
||||
{
|
||||
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
*delta_type = GIT_DELTA_IGNORED;
|
||||
git_buf_sets(&base, info->nitem->path);
|
||||
|
||||
diff->strcomp = git__strcmp;
|
||||
diff->strncomp = git__strncmp;
|
||||
diff->pfxcomp = git__prefixcmp;
|
||||
diff->entrycomp = git_index_entry__cmp;
|
||||
} else {
|
||||
diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
/* advance into untracked directory */
|
||||
if ((error = git_iterator_advance_into(&info->nitem, info->new_iter)) < 0) {
|
||||
|
||||
diff->strcomp = git__strcasecmp;
|
||||
diff->strncomp = git__strncasecmp;
|
||||
diff->pfxcomp = git__prefixcmp_icase;
|
||||
diff->entrycomp = git_index_entry__cmp_icase;
|
||||
/* skip ahead if empty */
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
error = git_iterator_advance(&info->nitem, info->new_iter);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* look for actual untracked file */
|
||||
while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) {
|
||||
is_ignored = git_iterator_current_is_ignored(info->new_iter);
|
||||
|
||||
/* need to recurse into non-ignored directories */
|
||||
if (!is_ignored && S_ISDIR(info->nitem->mode)) {
|
||||
if ((error = git_iterator_advance_into(
|
||||
&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* found a non-ignored item - treat parent dir as untracked */
|
||||
if (!is_ignored) {
|
||||
*delta_type = GIT_DELTA_UNTRACKED;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* finish off scan */
|
||||
while (!diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) {
|
||||
if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
git_buf_free(&base);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int handle_unmatched_new_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
const git_index_entry *nitem = info->nitem;
|
||||
git_delta_t delta_type = GIT_DELTA_UNTRACKED;
|
||||
bool contains_oitem;
|
||||
|
||||
/* check if this is a prefix of the other side */
|
||||
contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
|
||||
|
||||
/* check if this is contained in an ignored parent directory */
|
||||
if (git_buf_len(&info->ignore_prefix)) {
|
||||
if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
else
|
||||
git_buf_clear(&info->ignore_prefix);
|
||||
}
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
bool recurse_into_dir = contains_oitem;
|
||||
|
||||
/* if not already inside an ignored dir, check if this is ignored */
|
||||
if (delta_type != GIT_DELTA_IGNORED &&
|
||||
git_iterator_current_is_ignored(info->new_iter)) {
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
git_buf_sets(&info->ignore_prefix, nitem->path);
|
||||
}
|
||||
|
||||
/* check if user requests recursion into this type of dir */
|
||||
recurse_into_dir = contains_oitem ||
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
|
||||
(delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
|
||||
|
||||
/* do not advance into directories that contain a .git file */
|
||||
if (recurse_into_dir) {
|
||||
git_buf *full = NULL;
|
||||
if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
|
||||
return -1;
|
||||
if (full && git_path_contains_dir(full, DOT_GIT))
|
||||
recurse_into_dir = false;
|
||||
}
|
||||
|
||||
/* still have to look into untracked directories to match core git -
|
||||
* with no untracked files, directory is treated as ignored
|
||||
*/
|
||||
if (!recurse_into_dir &&
|
||||
delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
|
||||
{
|
||||
git_diff_delta *last;
|
||||
|
||||
/* attempt to insert record for this directory */
|
||||
if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
|
||||
return error;
|
||||
|
||||
/* if delta wasn't created (because of rules), just skip ahead */
|
||||
last = diff_delta__last_for_item(diff, nitem);
|
||||
if (!last)
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
|
||||
/* iterate into dir looking for an actual untracked file */
|
||||
if (diff_scan_inside_untracked_dir(diff, info, &delta_type) < 0)
|
||||
return -1;
|
||||
|
||||
/* it iteration changed delta type, the update the record */
|
||||
if (delta_type == GIT_DELTA_IGNORED) {
|
||||
last->status = GIT_DELTA_IGNORED;
|
||||
|
||||
/* remove the record if we don't want ignored records */
|
||||
if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) {
|
||||
git_vector_pop(&diff->deltas);
|
||||
git__free(last);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* try to advance into directory if necessary */
|
||||
if (recurse_into_dir) {
|
||||
error = git_iterator_advance_into(&info->nitem, info->new_iter);
|
||||
|
||||
/* if real error or no error, proceed with iteration */
|
||||
if (error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
giterr_clear();
|
||||
|
||||
/* if directory is empty, can't advance into it, so either skip
|
||||
* it or ignore it
|
||||
*/
|
||||
if (contains_oitem)
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two checks are effectively reversed --
|
||||
* i.e. when an file contained in an ignored directory is explicitly
|
||||
* ignored, it shows up as an ignored file in the diff list, even though
|
||||
* other untracked files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this seems odd. If the directory is ignored and the file is
|
||||
* untracked, we should skip it consistently, regardless of whether it
|
||||
* happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, reverse the following two if checks
|
||||
* so that individual file ignores are checked before container
|
||||
* directory exclusions are used to skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
|
||||
/* item contained in ignored directory, so skip over it */
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
|
||||
else if (git_iterator_current_is_ignored(info->new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
/* Actually create the record for this item if necessary */
|
||||
if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
|
||||
return error;
|
||||
|
||||
/* If user requested TYPECHANGE records, then check for that instead of
|
||||
* just generating an ADDED/UNTRACKED record
|
||||
*/
|
||||
if (delta_type != GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
contains_oitem)
|
||||
{
|
||||
/* this entry was prefixed with a tree - make TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->old_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
}
|
||||
|
||||
static int handle_unmatched_old_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* if we are generating TYPECHANGE records then check for that
|
||||
* instead of just generating a DELETE record
|
||||
*/
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
entry_is_prefixed(diff, info->nitem, info->oitem))
|
||||
{
|
||||
/* this entry has become a tree! convert to TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->new_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
|
||||
/* If new_iter is a workdir iterator, then this situation
|
||||
* will certainly be followed by a series of untracked items.
|
||||
* Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
|
||||
*/
|
||||
if (S_ISDIR(info->nitem->mode) &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
|
||||
return git_iterator_advance(&info->nitem, info->new_iter);
|
||||
}
|
||||
|
||||
return git_iterator_advance(&info->oitem, info->old_iter);
|
||||
}
|
||||
|
||||
static int handle_matched_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (!(error = maybe_modified(diff, info)) &&
|
||||
!(error = git_iterator_advance(&info->oitem, info->old_iter)))
|
||||
error = git_iterator_advance(&info->nitem, info->new_iter);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff__from_iterators(
|
||||
@ -646,198 +949,59 @@ int git_diff__from_iterators(
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
const git_index_entry *oitem, *nitem;
|
||||
git_buf ignore_prefix = GIT_BUF_INIT;
|
||||
git_diff_list *diff = git_diff_list_alloc(repo, opts);
|
||||
diff_in_progress info;
|
||||
git_diff_list *diff;
|
||||
|
||||
*diff_ptr = NULL;
|
||||
|
||||
if (!diff || diff_list_init_from_iterators(diff, old_iter, new_iter) < 0)
|
||||
goto fail;
|
||||
diff = diff_list_alloc(repo, old_iter, new_iter);
|
||||
GITERR_CHECK_ALLOC(diff);
|
||||
|
||||
info.repo = repo;
|
||||
info.old_iter = old_iter;
|
||||
info.new_iter = new_iter;
|
||||
git_buf_init(&info.ignore_prefix, 0);
|
||||
|
||||
/* make iterators have matching icase behavior */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
|
||||
if (git_iterator_set_ignore_case(old_iter, true) < 0 ||
|
||||
git_iterator_set_ignore_case(new_iter, true) < 0)
|
||||
goto fail;
|
||||
if (!(error = git_iterator_set_ignore_case(old_iter, true)))
|
||||
error = git_iterator_set_ignore_case(new_iter, true);
|
||||
}
|
||||
|
||||
if (git_iterator_current(&oitem, old_iter) < 0 ||
|
||||
git_iterator_current(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
/* finish initialization */
|
||||
if (!error &&
|
||||
!(error = diff_list_apply_options(diff, opts)) &&
|
||||
!(error = git_iterator_current(&info.oitem, old_iter)))
|
||||
error = git_iterator_current(&info.nitem, new_iter);
|
||||
|
||||
/* run iterators building diffs */
|
||||
while (oitem || nitem) {
|
||||
int cmp = oitem ? (nitem ? diff->entrycomp(oitem, nitem) : -1) : 1;
|
||||
while (!error && (info.oitem || info.nitem)) {
|
||||
int cmp = info.oitem ?
|
||||
(info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1;
|
||||
|
||||
/* create DELETED records for old items not matched in new */
|
||||
if (cmp < 0) {
|
||||
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
|
||||
goto fail;
|
||||
|
||||
/* if we are generating TYPECHANGE records then check for that
|
||||
* instead of just generating a DELETE record
|
||||
*/
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
entry_is_prefixed(diff, nitem, oitem))
|
||||
{
|
||||
/* this entry has become a tree! convert to TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->new_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
|
||||
/* If new_iter is a workdir iterator, then this situation
|
||||
* will certainly be followed by a series of untracked items.
|
||||
* Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
|
||||
*/
|
||||
if (S_ISDIR(nitem->mode) &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
|
||||
{
|
||||
if (git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (git_iterator_advance(&oitem, old_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
if (cmp < 0)
|
||||
error = handle_unmatched_old_item(diff, &info);
|
||||
|
||||
/* create ADDED, TRACKED, or IGNORED records for new items not
|
||||
* matched in old (and/or descend into directories as needed)
|
||||
*/
|
||||
else if (cmp > 0) {
|
||||
git_delta_t delta_type = GIT_DELTA_UNTRACKED;
|
||||
bool contains_oitem = entry_is_prefixed(diff, oitem, nitem);
|
||||
|
||||
/* check if contained in ignored parent directory */
|
||||
if (git_buf_len(&ignore_prefix) &&
|
||||
diff->pfxcomp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
/* recurse into directory only if there are tracked items in
|
||||
* it or if the user requested the contents of untracked
|
||||
* directories and it is not under an ignored directory.
|
||||
*/
|
||||
bool recurse_into_dir =
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
|
||||
(delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
|
||||
|
||||
/* do not advance into directories that contain a .git file */
|
||||
if (!contains_oitem && recurse_into_dir) {
|
||||
git_buf *full = NULL;
|
||||
if (git_iterator_current_workdir_path(&full, new_iter) < 0)
|
||||
goto fail;
|
||||
if (git_path_contains_dir(full, DOT_GIT))
|
||||
recurse_into_dir = false;
|
||||
}
|
||||
|
||||
/* if directory is ignored, remember ignore_prefix */
|
||||
if ((contains_oitem || recurse_into_dir) &&
|
||||
delta_type == GIT_DELTA_UNTRACKED &&
|
||||
git_iterator_current_is_ignored(new_iter))
|
||||
{
|
||||
git_buf_sets(&ignore_prefix, nitem->path);
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
/* skip recursion if we've just learned this is ignored */
|
||||
if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
|
||||
recurse_into_dir = false;
|
||||
}
|
||||
|
||||
if (contains_oitem || recurse_into_dir) {
|
||||
/* advance into directory */
|
||||
error = git_iterator_advance_into(&nitem, new_iter);
|
||||
|
||||
/* if directory is empty, can't advance into it, so skip */
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
error = git_iterator_advance(&nitem, new_iter);
|
||||
|
||||
git_buf_clear(&ignore_prefix);
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
goto fail;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two "else if" clauses are effectively
|
||||
* reversed -- i.e. when an untracked file contained in an
|
||||
* ignored directory is individually ignored, it shows up as an
|
||||
* ignored file in the diff list, even though other untracked
|
||||
* files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this is odd. If the directory is ignored and the file
|
||||
* is untracked, we should skip it consistently, regardless of
|
||||
* whether it happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, just reverse the following
|
||||
* two "else if" cases so that individual file ignores are
|
||||
* checked before container directory exclusions are used to
|
||||
* skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) {
|
||||
if (git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
continue; /* ignored parent directory, so skip completely */
|
||||
}
|
||||
|
||||
else if (git_iterator_current_is_ignored(new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
if (diff_delta__from_one(diff, delta_type, nitem) < 0)
|
||||
goto fail;
|
||||
|
||||
/* if we are generating TYPECHANGE records then check for that
|
||||
* instead of just generating an ADDED/UNTRACKED record
|
||||
*/
|
||||
if (delta_type != GIT_DELTA_IGNORED &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
|
||||
contains_oitem)
|
||||
{
|
||||
/* this entry was prefixed with a tree - make TYPECHANGE */
|
||||
git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
|
||||
if (last) {
|
||||
last->status = GIT_DELTA_TYPECHANGE;
|
||||
last->old_file.mode = GIT_FILEMODE_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
if (git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
error = handle_unmatched_new_item(diff, &info);
|
||||
|
||||
/* otherwise item paths match, so create MODIFIED record
|
||||
* (or ADDED and DELETED pair if type changed)
|
||||
*/
|
||||
else {
|
||||
assert(oitem && nitem && cmp == 0);
|
||||
|
||||
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
|
||||
git_iterator_advance(&oitem, old_iter) < 0 ||
|
||||
git_iterator_advance(&nitem, new_iter) < 0)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
error = handle_matched_item(diff, &info);
|
||||
}
|
||||
|
||||
*diff_ptr = diff;
|
||||
|
||||
fail:
|
||||
if (!*diff_ptr) {
|
||||
if (!error)
|
||||
*diff_ptr = diff;
|
||||
else
|
||||
git_diff_list_free(diff);
|
||||
error = -1;
|
||||
}
|
||||
|
||||
git_buf_free(&ignore_prefix);
|
||||
git_buf_free(&info.ignore_prefix);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -859,12 +1023,20 @@ int git_diff_tree_to_tree(
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE;
|
||||
|
||||
assert(diff && repo);
|
||||
|
||||
/* for tree to tree diff, be case sensitive even if the index is
|
||||
* currently case insensitive, unless the user explicitly asked
|
||||
* for case insensitivity
|
||||
*/
|
||||
if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
|
||||
iflag = GIT_ITERATOR_IGNORE_CASE;
|
||||
|
||||
DIFF_FROM_ITERATORS(
|
||||
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
|
||||
git_iterator_for_tree(&b, new_tree, 0, pfx, pfx)
|
||||
git_iterator_for_tree(&a, old_tree, iflag, pfx, pfx),
|
||||
git_iterator_for_tree(&b, new_tree, iflag, pfx, pfx)
|
||||
);
|
||||
|
||||
return error;
|
||||
|
12
src/diff.h
12
src/diff.h
@ -74,5 +74,17 @@ extern int git_diff__from_iterators(
|
||||
git_iterator *new_iter,
|
||||
const git_diff_options *opts);
|
||||
|
||||
int git_diff_find_similar__hashsig_for_file(
|
||||
void **out, const git_diff_file *f, const char *path, void *p);
|
||||
|
||||
int git_diff_find_similar__hashsig_for_buf(
|
||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
||||
|
||||
void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||
|
||||
int git_diff_find_similar__calc_similarity(
|
||||
int *score, void *siga, void *sigb, void *payload);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -101,8 +101,8 @@ static bool diff_delta_is_binary_forced(
|
||||
|
||||
/* make sure files are conceivably mmap-able */
|
||||
if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
|
||||
(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
|
||||
{
|
||||
(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size) {
|
||||
|
||||
delta->old_file.flags |= GIT_DIFF_FLAG_BINARY;
|
||||
delta->new_file.flags |= GIT_DIFF_FLAG_BINARY;
|
||||
delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
@ -232,8 +232,7 @@ static int get_blob_content(
|
||||
if (git_oid_iszero(&file->oid))
|
||||
return 0;
|
||||
|
||||
if (file->mode == GIT_FILEMODE_COMMIT)
|
||||
{
|
||||
if (file->mode == GIT_FILEMODE_COMMIT) {
|
||||
char oidstr[GIT_OID_HEXSZ+1];
|
||||
git_buf content = GIT_BUF_INIT;
|
||||
|
||||
@ -299,8 +298,8 @@ static int get_workdir_sm_content(
|
||||
char oidstr[GIT_OID_HEXSZ+1];
|
||||
|
||||
if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 ||
|
||||
(error = git_submodule_status(&sm_status, sm)) < 0)
|
||||
{
|
||||
(error = git_submodule_status(&sm_status, sm)) < 0) {
|
||||
|
||||
/* GIT_EEXISTS means a "submodule" that has not been git added */
|
||||
if (error == GIT_EEXISTS)
|
||||
error = 0;
|
||||
@ -312,8 +311,8 @@ static int get_workdir_sm_content(
|
||||
const git_oid* sm_head;
|
||||
|
||||
if ((sm_head = git_submodule_wd_id(sm)) != NULL ||
|
||||
(sm_head = git_submodule_head_id(sm)) != NULL)
|
||||
{
|
||||
(sm_head = git_submodule_head_id(sm)) != NULL) {
|
||||
|
||||
git_oid_cpy(&file->oid, sm_head);
|
||||
file->flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
}
|
||||
@ -660,8 +659,8 @@ static int diff_patch_load(
|
||||
*/
|
||||
if (check_if_unmodified &&
|
||||
delta->old_file.mode == delta->new_file.mode &&
|
||||
!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
|
||||
{
|
||||
!git_oid__cmp(&delta->old_file.oid, &delta->new_file.oid)) {
|
||||
|
||||
delta->status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
@ -1049,6 +1048,12 @@ char git_diff_status_char(git_delta_t status)
|
||||
return code;
|
||||
}
|
||||
|
||||
static int callback_error(void)
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
static int print_compact(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
@ -1083,10 +1088,7 @@ static int print_compact(
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1114,11 +1116,20 @@ int git_diff_print_compact(
|
||||
|
||||
static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
||||
{
|
||||
char start_oid[8], end_oid[8];
|
||||
int abbrevlen;
|
||||
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||
|
||||
/* TODO: Determine a good actual OID range to print */
|
||||
git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid);
|
||||
git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid);
|
||||
if (git_repository__cvar(&abbrevlen, pi->diff->repo, GIT_CVAR_ABBREV) < 0)
|
||||
return -1;
|
||||
|
||||
abbrevlen += 1; /* for NUL byte */
|
||||
if (abbrevlen < 2)
|
||||
abbrevlen = 2;
|
||||
else if (abbrevlen > (int)sizeof(start_oid))
|
||||
abbrevlen = (int)sizeof(start_oid);
|
||||
|
||||
git_oid_tostr(start_oid, abbrevlen, &delta->old_file.oid);
|
||||
git_oid_tostr(end_oid, abbrevlen, &delta->new_file.oid);
|
||||
|
||||
/* TODO: Match git diff more closely */
|
||||
if (delta->old_file.mode == delta->new_file.mode) {
|
||||
@ -1191,10 +1202,7 @@ static int print_patch_file(
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
return 0;
|
||||
@ -1208,10 +1216,7 @@ static int print_patch_file(
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1234,10 +1239,7 @@ static int print_patch_hunk(
|
||||
|
||||
if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1269,10 +1271,7 @@ static int print_patch_line(
|
||||
|
||||
if (pi->print_cb(delta, range, line_origin,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
{
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1379,7 +1378,7 @@ static int diff_single_apply(diff_single_data *data)
|
||||
(has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
|
||||
(has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
|
||||
|
||||
if (git_oid_cmp(&delta->new_file.oid, &delta->old_file.oid) == 0)
|
||||
if (git_oid__cmp(&delta->new_file.oid, &delta->old_file.oid) == 0)
|
||||
delta->status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
if ((error = diff_delta_is_binary_by_content(
|
||||
|
@ -170,7 +170,7 @@ int git_diff_merge(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int find_similar__hashsig_for_file(
|
||||
int git_diff_find_similar__hashsig_for_file(
|
||||
void **out, const git_diff_file *f, const char *path, void *p)
|
||||
{
|
||||
git_hashsig_option_t opt = (git_hashsig_option_t)p;
|
||||
@ -187,7 +187,7 @@ static int find_similar__hashsig_for_file(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int find_similar__hashsig_for_buf(
|
||||
int git_diff_find_similar__hashsig_for_buf(
|
||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p)
|
||||
{
|
||||
git_hashsig_option_t opt = (git_hashsig_option_t)p;
|
||||
@ -204,13 +204,13 @@ static int find_similar__hashsig_for_buf(
|
||||
return error;
|
||||
}
|
||||
|
||||
static void find_similar__hashsig_free(void *sig, void *payload)
|
||||
void git_diff_find_similar__hashsig_free(void *sig, void *payload)
|
||||
{
|
||||
GIT_UNUSED(payload);
|
||||
git_hashsig_free(sig);
|
||||
}
|
||||
|
||||
static int find_similar__calc_similarity(
|
||||
int git_diff_find_similar__calc_similarity(
|
||||
int *score, void *siga, void *sigb, void *payload)
|
||||
{
|
||||
GIT_UNUSED(payload);
|
||||
@ -291,10 +291,10 @@ static int normalize_find_opts(
|
||||
opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
|
||||
GITERR_CHECK_ALLOC(opts->metric);
|
||||
|
||||
opts->metric->file_signature = find_similar__hashsig_for_file;
|
||||
opts->metric->buffer_signature = find_similar__hashsig_for_buf;
|
||||
opts->metric->free_signature = find_similar__hashsig_free;
|
||||
opts->metric->similarity = find_similar__calc_similarity;
|
||||
opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
|
||||
opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
|
||||
opts->metric->free_signature = git_diff_find_similar__hashsig_free;
|
||||
opts->metric->similarity = git_diff_find_similar__calc_similarity;
|
||||
|
||||
if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
|
||||
opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
|
||||
@ -429,7 +429,7 @@ static int similarity_measure(
|
||||
if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
|
||||
return 0;
|
||||
|
||||
if (git_oid_cmp(&a_file->oid, &b_file->oid) == 0)
|
||||
if (git_oid__cmp(&a_file->oid, &b_file->oid) == 0)
|
||||
return 100;
|
||||
|
||||
/* update signature cache if needed */
|
||||
|
@ -34,7 +34,7 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
|
||||
|
||||
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
|
||||
p->found_head = 1;
|
||||
else if (git_refspec_src_matches(p->spec, head->name))
|
||||
else if (git_remote__matching_refspec(p->remote, head->name))
|
||||
match = 1;
|
||||
else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL &&
|
||||
git_refspec_src_matches(p->tagspec, head->name))
|
||||
@ -68,7 +68,6 @@ static int filter_wants(git_remote *remote)
|
||||
* not interested in any particular branch but just the remote's
|
||||
* HEAD, which will be stored in FETCH_HEAD after the fetch.
|
||||
*/
|
||||
p.spec = git_remote_fetchspec(remote);
|
||||
p.tagspec = &tagspec;
|
||||
p.found_head = 0;
|
||||
p.remote = remote;
|
||||
|
@ -135,6 +135,12 @@ int git_threads_init(void)
|
||||
|
||||
void git_threads_shutdown(void)
|
||||
{
|
||||
if (_tls_init) {
|
||||
void *ptr = pthread_getspecific(_tls_key);
|
||||
pthread_setspecific(_tls_key, NULL);
|
||||
git__free(ptr);
|
||||
}
|
||||
|
||||
pthread_key_delete(_tls_key);
|
||||
_tls_init = 0;
|
||||
git_mutex_free(&git__mwindow_mutex);
|
||||
|
@ -10,14 +10,6 @@
|
||||
#include "mwindow.h"
|
||||
#include "hash.h"
|
||||
|
||||
#if defined(GIT_THREADS) && defined(_MSC_VER)
|
||||
# define GIT_MEMORY_BARRIER MemoryBarrier()
|
||||
#elif defined(GIT_THREADS)
|
||||
# define GIT_MEMORY_BARRIER __sync_synchronize()
|
||||
#else
|
||||
# define GIT_MEMORY_BARRIER /* noop */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
git_error *last_error;
|
||||
git_error error_t;
|
||||
|
36
src/ignore.c
36
src/ignore.c
@ -15,24 +15,14 @@ static int parse_ignore_file(
|
||||
git_attr_fnmatch *match = NULL;
|
||||
const char *scan = NULL;
|
||||
char *context = NULL;
|
||||
bool ignore_case = false;
|
||||
git_config *cfg = NULL;
|
||||
int val;
|
||||
int ignore_case = false;
|
||||
|
||||
/* Prefer to have the caller pass in a git_ignores as the parsedata object.
|
||||
* If they did not, then we can (much more slowly) find the value of
|
||||
* ignore_case by using the repository object. */
|
||||
if (parsedata != NULL) {
|
||||
/* Prefer to have the caller pass in a git_ignores as the parsedata
|
||||
* object. If they did not, then look up the value of ignore_case */
|
||||
if (parsedata != NULL)
|
||||
ignore_case = ((git_ignores *)parsedata)->ignore_case;
|
||||
} else {
|
||||
if ((error = git_repository_config(&cfg, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
|
||||
ignore_case = (val != 0);
|
||||
|
||||
git_config_free(cfg);
|
||||
}
|
||||
else if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0)
|
||||
return error;
|
||||
|
||||
if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
|
||||
context = ignores->key + 2;
|
||||
@ -109,8 +99,6 @@ int git_ignore__for_path(
|
||||
{
|
||||
int error = 0;
|
||||
const char *workdir = git_repository_workdir(repo);
|
||||
git_config *cfg = NULL;
|
||||
int val;
|
||||
|
||||
assert(ignores);
|
||||
|
||||
@ -118,17 +106,11 @@ int git_ignore__for_path(
|
||||
git_buf_init(&ignores->dir, 0);
|
||||
ignores->ign_internal = NULL;
|
||||
|
||||
/* Set the ignore_case flag appropriately */
|
||||
if ((error = git_repository_config(&cfg, repo)) < 0)
|
||||
/* Read the ignore_case flag */
|
||||
if ((error = git_repository__cvar(
|
||||
&ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
|
||||
ignores->ignore_case = (val != 0);
|
||||
else
|
||||
ignores->ignore_case = 0;
|
||||
|
||||
git_config_free(cfg);
|
||||
|
||||
if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||
|
||||
(error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
|
||||
(error = git_attr_cache__init(repo)) < 0)
|
||||
|
@ -28,7 +28,7 @@ typedef struct {
|
||||
git_attr_file *ign_internal;
|
||||
git_vector ign_path;
|
||||
git_vector ign_global;
|
||||
unsigned int ignore_case:1;
|
||||
int ignore_case;
|
||||
} git_ignores;
|
||||
|
||||
extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
|
||||
|
262
src/index.c
262
src/index.c
@ -19,6 +19,7 @@
|
||||
#include "git2/oid.h"
|
||||
#include "git2/blob.h"
|
||||
#include "git2/config.h"
|
||||
#include "git2/sys/index.h"
|
||||
|
||||
#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
|
||||
#define short_entry_size(len) entry_size(struct entry_short, len)
|
||||
@ -35,6 +36,7 @@ static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
|
||||
static const unsigned int INDEX_HEADER_SIG = 0x44495243;
|
||||
static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
|
||||
static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
|
||||
static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'};
|
||||
|
||||
#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))
|
||||
|
||||
@ -187,6 +189,51 @@ static int index_icmp(const void *a, const void *b)
|
||||
return diff;
|
||||
}
|
||||
|
||||
static int conflict_name_cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_index_name_entry *name_a = a;
|
||||
const git_index_name_entry *name_b = b;
|
||||
|
||||
if (name_a->ancestor && !name_b->ancestor)
|
||||
return 1;
|
||||
|
||||
if (!name_a->ancestor && name_b->ancestor)
|
||||
return -1;
|
||||
|
||||
if (name_a->ancestor)
|
||||
return strcmp(name_a->ancestor, name_b->ancestor);
|
||||
|
||||
if (!name_a->ours || !name_b->ours)
|
||||
return 0;
|
||||
|
||||
return strcmp(name_a->ours, name_b->ours);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: enable this when resolving case insensitive conflicts
|
||||
*/
|
||||
#if 0
|
||||
static int conflict_name_icmp(const void *a, const void *b)
|
||||
{
|
||||
const git_index_name_entry *name_a = a;
|
||||
const git_index_name_entry *name_b = b;
|
||||
|
||||
if (name_a->ancestor && !name_b->ancestor)
|
||||
return 1;
|
||||
|
||||
if (!name_a->ancestor && name_b->ancestor)
|
||||
return -1;
|
||||
|
||||
if (name_a->ancestor)
|
||||
return strcasecmp(name_a->ancestor, name_b->ancestor);
|
||||
|
||||
if (!name_a->ours || !name_b->ours)
|
||||
return 0;
|
||||
|
||||
return strcasecmp(name_a->ours, name_b->ours);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int reuc_srch(const void *key, const void *array_member)
|
||||
{
|
||||
const git_index_reuc_entry *reuc = array_member;
|
||||
@ -278,6 +325,7 @@ int git_index_open(git_index **index_out, const char *index_path)
|
||||
}
|
||||
|
||||
if (git_vector_init(&index->entries, 32, index_cmp) < 0 ||
|
||||
git_vector_init(&index->names, 32, conflict_name_cmp) < 0 ||
|
||||
git_vector_init(&index->reuc, 32, reuc_cmp) < 0)
|
||||
return -1;
|
||||
|
||||
@ -331,6 +379,8 @@ void git_index_clear(git_index *index)
|
||||
|
||||
git_index_reuc_clear(index);
|
||||
|
||||
git_index_name_clear(index);
|
||||
|
||||
git_futils_filestamp_set(&index->stamp, NULL);
|
||||
|
||||
git_tree_cache_free(index->tree);
|
||||
@ -352,19 +402,18 @@ int git_index_set_caps(git_index *index, unsigned int caps)
|
||||
old_ignore_case = index->ignore_case;
|
||||
|
||||
if (caps == GIT_INDEXCAP_FROM_OWNER) {
|
||||
git_config *cfg;
|
||||
git_repository *repo = INDEX_OWNER(index);
|
||||
int val;
|
||||
|
||||
if (INDEX_OWNER(index) == NULL ||
|
||||
git_repository_config__weakptr(&cfg, INDEX_OWNER(index)) < 0)
|
||||
return create_index_error(-1,
|
||||
"Cannot get repository config to set index caps");
|
||||
if (!repo)
|
||||
return create_index_error(
|
||||
-1, "Cannot access repository to set index caps");
|
||||
|
||||
if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
|
||||
if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORECASE))
|
||||
index->ignore_case = (val != 0);
|
||||
if (git_config_get_bool(&val, cfg, "core.filemode") == 0)
|
||||
if (!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE))
|
||||
index->distrust_filemode = (val == 0);
|
||||
if (git_config_get_bool(&val, cfg, "core.symlinks") == 0)
|
||||
if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS))
|
||||
index->no_symlinks = (val == 0);
|
||||
}
|
||||
else {
|
||||
@ -586,8 +635,9 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
|
||||
|
||||
static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
|
||||
const char *path,
|
||||
int ancestor_mode, git_oid *ancestor_oid,
|
||||
int our_mode, git_oid *our_oid, int their_mode, git_oid *their_oid)
|
||||
int ancestor_mode, const git_oid *ancestor_oid,
|
||||
int our_mode, const git_oid *our_oid,
|
||||
int their_mode, const git_oid *their_oid)
|
||||
{
|
||||
git_index_reuc_entry *reuc = NULL;
|
||||
|
||||
@ -693,7 +743,7 @@ static int index_conflict_to_reuc(git_index *index, const char *path)
|
||||
{
|
||||
git_index_entry *conflict_entries[3];
|
||||
int ancestor_mode, our_mode, their_mode;
|
||||
git_oid *ancestor_oid, *our_oid, *their_oid;
|
||||
git_oid const *ancestor_oid, *our_oid, *their_oid;
|
||||
int ret;
|
||||
|
||||
if ((ret = git_index_conflict_get(&conflict_entries[0],
|
||||
@ -947,7 +997,6 @@ int git_index_conflict_get(git_index_entry **ancestor_out,
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
for (posmax = git_index_entrycount(index); pos < posmax; ++pos) {
|
||||
|
||||
conflict_entry = git_vector_get(&index->entries, pos);
|
||||
|
||||
if (index->entries_cmp_path(conflict_entry->path, path) != 0)
|
||||
@ -1043,13 +1092,82 @@ int git_index_has_conflicts(const git_index *index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int git_index_name_entrycount(git_index *index)
|
||||
{
|
||||
assert(index);
|
||||
return (unsigned int)index->names.length;
|
||||
}
|
||||
|
||||
const git_index_name_entry *git_index_name_get_byindex(
|
||||
git_index *index, size_t n)
|
||||
{
|
||||
assert(index);
|
||||
|
||||
git_vector_sort(&index->names);
|
||||
return git_vector_get(&index->names, n);
|
||||
}
|
||||
|
||||
int git_index_name_add(git_index *index,
|
||||
const char *ancestor, const char *ours, const char *theirs)
|
||||
{
|
||||
git_index_name_entry *conflict_name;
|
||||
|
||||
assert ((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
|
||||
|
||||
conflict_name = git__calloc(1, sizeof(git_index_name_entry));
|
||||
GITERR_CHECK_ALLOC(conflict_name);
|
||||
|
||||
if (ancestor) {
|
||||
conflict_name->ancestor = git__strdup(ancestor);
|
||||
GITERR_CHECK_ALLOC(conflict_name->ancestor);
|
||||
}
|
||||
|
||||
if (ours) {
|
||||
conflict_name->ours = git__strdup(ours);
|
||||
GITERR_CHECK_ALLOC(conflict_name->ours);
|
||||
}
|
||||
|
||||
if (theirs) {
|
||||
conflict_name->theirs = git__strdup(theirs);
|
||||
GITERR_CHECK_ALLOC(conflict_name->theirs);
|
||||
}
|
||||
|
||||
return git_vector_insert(&index->names, conflict_name);
|
||||
}
|
||||
|
||||
void git_index_name_clear(git_index *index)
|
||||
{
|
||||
size_t i;
|
||||
git_index_name_entry *conflict_name;
|
||||
|
||||
assert(index);
|
||||
|
||||
git_vector_foreach(&index->names, i, conflict_name) {
|
||||
if (conflict_name->ancestor)
|
||||
git__free(conflict_name->ancestor);
|
||||
|
||||
if (conflict_name->ours)
|
||||
git__free(conflict_name->ours);
|
||||
|
||||
if (conflict_name->theirs)
|
||||
git__free(conflict_name->theirs);
|
||||
|
||||
git__free(conflict_name);
|
||||
}
|
||||
|
||||
git_vector_clear(&index->names);
|
||||
}
|
||||
|
||||
unsigned int git_index_reuc_entrycount(git_index *index)
|
||||
{
|
||||
assert(index);
|
||||
return (unsigned int)index->reuc.length;
|
||||
}
|
||||
|
||||
static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int replace)
|
||||
static int index_reuc_insert(
|
||||
git_index *index,
|
||||
git_index_reuc_entry *reuc,
|
||||
int replace)
|
||||
{
|
||||
git_index_reuc_entry **existing = NULL;
|
||||
size_t position;
|
||||
@ -1071,9 +1189,9 @@ static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int r
|
||||
}
|
||||
|
||||
int git_index_reuc_add(git_index *index, const char *path,
|
||||
int ancestor_mode, git_oid *ancestor_oid,
|
||||
int our_mode, git_oid *our_oid,
|
||||
int their_mode, git_oid *their_oid)
|
||||
int ancestor_mode, const git_oid *ancestor_oid,
|
||||
int our_mode, const git_oid *our_oid,
|
||||
int their_mode, const git_oid *their_oid)
|
||||
{
|
||||
git_index_reuc_entry *reuc = NULL;
|
||||
int error = 0;
|
||||
@ -1226,6 +1344,52 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int read_conflict_names(git_index *index, const char *buffer, size_t size)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
/* This gets called multiple times, the vector might already be initialized */
|
||||
if (index->names._alloc_size == 0 &&
|
||||
git_vector_init(&index->names, 16, conflict_name_cmp) < 0)
|
||||
return -1;
|
||||
|
||||
#define read_conflict_name(ptr) \
|
||||
len = strlen(buffer) + 1; \
|
||||
if (size < len) \
|
||||
return index_error_invalid("reading conflict name entries"); \
|
||||
\
|
||||
if (len == 1) \
|
||||
ptr = NULL; \
|
||||
else { \
|
||||
ptr = git__malloc(len); \
|
||||
GITERR_CHECK_ALLOC(ptr); \
|
||||
memcpy(ptr, buffer, len); \
|
||||
} \
|
||||
\
|
||||
buffer += len; \
|
||||
size -= len;
|
||||
|
||||
while (size) {
|
||||
git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry));
|
||||
GITERR_CHECK_ALLOC(conflict_name);
|
||||
|
||||
read_conflict_name(conflict_name->ancestor);
|
||||
read_conflict_name(conflict_name->ours);
|
||||
read_conflict_name(conflict_name->theirs);
|
||||
|
||||
if (git_vector_insert(&index->names, conflict_name) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#undef read_conflict_name
|
||||
|
||||
/* entries are guaranteed to be sorted on-disk */
|
||||
index->names.sorted = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
|
||||
{
|
||||
size_t path_length, entry_size;
|
||||
@ -1330,6 +1494,9 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
|
||||
} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
|
||||
if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
|
||||
return 0;
|
||||
} else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) {
|
||||
if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0)
|
||||
return 0;
|
||||
}
|
||||
/* else, unsupported extension. We cannot parse this, but we can skip
|
||||
* it by returning `total_size */
|
||||
@ -1345,7 +1512,7 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
|
||||
static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
|
||||
{
|
||||
unsigned int i;
|
||||
struct index_header header;
|
||||
struct index_header header = { 0 };
|
||||
git_oid checksum_calculated, checksum_expected;
|
||||
|
||||
#define seek_forward(_increase) { \
|
||||
@ -1412,7 +1579,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
|
||||
/* 160-bit SHA-1 over the content of the index file before this checksum. */
|
||||
git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
|
||||
|
||||
if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
|
||||
if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0)
|
||||
return index_error_invalid("calculated checksum does not match expected");
|
||||
|
||||
#undef seek_forward
|
||||
@ -1543,6 +1710,61 @@ static int write_extension(git_filebuf *file, struct index_extension *header, gi
|
||||
return error;
|
||||
}
|
||||
|
||||
static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (conflict_name->ancestor == NULL)
|
||||
error = git_buf_put(name_buf, "\0", 1);
|
||||
else
|
||||
error = git_buf_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1);
|
||||
|
||||
if (error != 0)
|
||||
goto on_error;
|
||||
|
||||
if (conflict_name->ours == NULL)
|
||||
error = git_buf_put(name_buf, "\0", 1);
|
||||
else
|
||||
error = git_buf_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1);
|
||||
|
||||
if (error != 0)
|
||||
goto on_error;
|
||||
|
||||
if (conflict_name->theirs == NULL)
|
||||
error = git_buf_put(name_buf, "\0", 1);
|
||||
else
|
||||
error = git_buf_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1);
|
||||
|
||||
on_error:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int write_name_extension(git_index *index, git_filebuf *file)
|
||||
{
|
||||
git_buf name_buf = GIT_BUF_INIT;
|
||||
git_vector *out = &index->names;
|
||||
git_index_name_entry *conflict_name;
|
||||
struct index_extension extension;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
git_vector_foreach(out, i, conflict_name) {
|
||||
if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
memset(&extension, 0x0, sizeof(struct index_extension));
|
||||
memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4);
|
||||
extension.extension_size = (uint32_t)name_buf.size;
|
||||
|
||||
error = write_extension(file, &extension, &name_buf);
|
||||
|
||||
git_buf_free(&name_buf);
|
||||
|
||||
done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc)
|
||||
{
|
||||
int i;
|
||||
@ -1613,6 +1835,10 @@ static int write_index(git_index *index, git_filebuf *file)
|
||||
|
||||
/* TODO: write tree cache extension */
|
||||
|
||||
/* write the rename conflict extension */
|
||||
if (index->names.length > 0 && write_name_extension(index, file) < 0)
|
||||
return -1;
|
||||
|
||||
/* write the reuc extension */
|
||||
if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
|
||||
return -1;
|
||||
|
@ -33,6 +33,7 @@ struct git_index {
|
||||
|
||||
git_tree_cache *tree;
|
||||
|
||||
git_vector names;
|
||||
git_vector reuc;
|
||||
|
||||
git_vector_cmp entries_cmp_path;
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
#include "git2/indexer.h"
|
||||
#include "git2/object.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "pack.h"
|
||||
@ -17,6 +16,7 @@
|
||||
#include "posix.h"
|
||||
#include "pack.h"
|
||||
#include "filebuf.h"
|
||||
#include "oid.h"
|
||||
#include "oidmap.h"
|
||||
|
||||
#define UINT31_MAX (0x7FFFFFFF)
|
||||
@ -60,36 +60,19 @@ const git_oid *git_indexer_stream_hash(const git_indexer_stream *idx)
|
||||
|
||||
static int open_pack(struct git_pack_file **out, const char *filename)
|
||||
{
|
||||
size_t namelen;
|
||||
struct git_pack_file *pack;
|
||||
struct stat st;
|
||||
int fd;
|
||||
|
||||
namelen = strlen(filename);
|
||||
pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1);
|
||||
GITERR_CHECK_ALLOC(pack);
|
||||
if (git_packfile_alloc(&pack, filename) < 0)
|
||||
return -1;
|
||||
|
||||
memcpy(pack->pack_name, filename, namelen + 1);
|
||||
|
||||
if (p_stat(filename, &st) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to stat packfile.");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
|
||||
if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to open packfile.");
|
||||
goto cleanup;
|
||||
git_packfile_free(pack);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pack->mwf.fd = fd;
|
||||
pack->mwf.size = (git_off_t)st.st_size;
|
||||
|
||||
*out = pack;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
git__free(pack);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
|
||||
@ -120,7 +103,7 @@ static int objects_cmp(const void *a, const void *b)
|
||||
const struct entry *entrya = a;
|
||||
const struct entry *entryb = b;
|
||||
|
||||
return git_oid_cmp(&entrya->oid, &entryb->oid);
|
||||
return git_oid__cmp(&entrya->oid, &entryb->oid);
|
||||
}
|
||||
|
||||
int git_indexer_stream_new(
|
||||
@ -404,7 +387,6 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
|
||||
/* Make sure we set the new size of the pack */
|
||||
if (idx->opened_pack) {
|
||||
idx->pack->mwf.size += size;
|
||||
//printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size);
|
||||
} else {
|
||||
if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0)
|
||||
return -1;
|
||||
|
595
src/iterator.c
595
src/iterator.c
@ -26,8 +26,6 @@
|
||||
(GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
|
||||
|
||||
#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
|
||||
(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
|
||||
GITERR_CHECK_ALLOC(P); \
|
||||
(P)->base.type = GIT_ITERATOR_TYPE_ ## NAME_UC; \
|
||||
(P)->base.cb = &(P)->cb; \
|
||||
ITERATOR_SET_CB(P,NAME_LC); \
|
||||
@ -148,7 +146,8 @@ int git_iterator_for_nothing(
|
||||
const char *start,
|
||||
const char *end)
|
||||
{
|
||||
empty_iterator *i;
|
||||
empty_iterator *i = git__calloc(1, sizeof(empty_iterator));
|
||||
GITERR_CHECK_ALLOC(i);
|
||||
|
||||
#define empty_iterator__current empty_iterator__noop
|
||||
#define empty_iterator__advance empty_iterator__noop
|
||||
@ -581,6 +580,9 @@ int git_iterator_for_tree(
|
||||
if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
|
||||
return error;
|
||||
|
||||
ti = git__calloc(1, sizeof(tree_iterator));
|
||||
GITERR_CHECK_ALLOC(ti);
|
||||
|
||||
ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
|
||||
|
||||
if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
|
||||
@ -810,7 +812,8 @@ int git_iterator_for_index(
|
||||
const char *start,
|
||||
const char *end)
|
||||
{
|
||||
index_iterator *ii;
|
||||
index_iterator *ii = git__calloc(1, sizeof(index_iterator));
|
||||
GITERR_CHECK_ALLOC(ii);
|
||||
|
||||
ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
|
||||
|
||||
@ -833,237 +836,221 @@ int git_iterator_for_index(
|
||||
}
|
||||
|
||||
|
||||
#define WORKDIR_MAX_DEPTH 100
|
||||
|
||||
typedef struct workdir_iterator_frame workdir_iterator_frame;
|
||||
struct workdir_iterator_frame {
|
||||
workdir_iterator_frame *next;
|
||||
typedef struct fs_iterator_frame fs_iterator_frame;
|
||||
struct fs_iterator_frame {
|
||||
fs_iterator_frame *next;
|
||||
git_vector entries;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
typedef struct fs_iterator fs_iterator;
|
||||
struct fs_iterator {
|
||||
git_iterator base;
|
||||
git_iterator_callbacks cb;
|
||||
workdir_iterator_frame *stack;
|
||||
git_ignores ignores;
|
||||
fs_iterator_frame *stack;
|
||||
git_index_entry entry;
|
||||
git_buf path;
|
||||
size_t root_len;
|
||||
int is_ignored;
|
||||
int depth;
|
||||
} workdir_iterator;
|
||||
|
||||
GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
|
||||
int (*enter_dir_cb)(fs_iterator *self);
|
||||
int (*leave_dir_cb)(fs_iterator *self);
|
||||
int (*update_entry_cb)(fs_iterator *self);
|
||||
};
|
||||
|
||||
#define FS_MAX_DEPTH 100
|
||||
|
||||
static fs_iterator_frame *fs_iterator__alloc_frame(fs_iterator *fi)
|
||||
{
|
||||
if (!ps)
|
||||
return false;
|
||||
else {
|
||||
const char *path = ps->path;
|
||||
size_t len = ps->path_len;
|
||||
|
||||
if (len < 4)
|
||||
return false;
|
||||
if (path[len - 1] == '/')
|
||||
len--;
|
||||
if (tolower(path[len - 1]) != 't' ||
|
||||
tolower(path[len - 2]) != 'i' ||
|
||||
tolower(path[len - 3]) != 'g' ||
|
||||
tolower(path[len - 4]) != '.')
|
||||
return false;
|
||||
return (len == 4 || path[len - 5] == '/');
|
||||
}
|
||||
}
|
||||
|
||||
static workdir_iterator_frame *workdir_iterator__alloc_frame(
|
||||
workdir_iterator *wi)
|
||||
{
|
||||
workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
|
||||
fs_iterator_frame *ff = git__calloc(1, sizeof(fs_iterator_frame));
|
||||
git_vector_cmp entry_compare = CASESELECT(
|
||||
iterator__ignore_case(wi),
|
||||
iterator__ignore_case(fi),
|
||||
git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
|
||||
|
||||
if (wf == NULL)
|
||||
return NULL;
|
||||
|
||||
if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
|
||||
git__free(wf);
|
||||
return NULL;
|
||||
if (ff && git_vector_init(&ff->entries, 0, entry_compare) < 0) {
|
||||
git__free(ff);
|
||||
ff = NULL;
|
||||
}
|
||||
|
||||
return wf;
|
||||
return ff;
|
||||
}
|
||||
|
||||
static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
|
||||
static void fs_iterator__free_frame(fs_iterator_frame *ff)
|
||||
{
|
||||
unsigned int i;
|
||||
size_t i;
|
||||
git_path_with_stat *path;
|
||||
|
||||
git_vector_foreach(&wf->entries, i, path)
|
||||
git_vector_foreach(&ff->entries, i, path)
|
||||
git__free(path);
|
||||
git_vector_free(&wf->entries);
|
||||
git__free(wf);
|
||||
git_vector_free(&ff->entries);
|
||||
git__free(ff);
|
||||
}
|
||||
|
||||
static int workdir_iterator__update_entry(workdir_iterator *wi);
|
||||
|
||||
static int workdir_iterator__entry_cmp(const void *i, const void *item)
|
||||
static void fs_iterator__pop_frame(
|
||||
fs_iterator *fi, fs_iterator_frame *ff, bool pop_last)
|
||||
{
|
||||
const workdir_iterator *wi = (const workdir_iterator *)i;
|
||||
const git_path_with_stat *ps = item;
|
||||
return wi->base.prefixcomp(wi->base.start, ps->path);
|
||||
}
|
||||
if (fi && fi->stack == ff) {
|
||||
if (!ff->next && !pop_last) {
|
||||
memset(&fi->entry, 0, sizeof(fi->entry));
|
||||
return;
|
||||
}
|
||||
|
||||
static void workdir_iterator__seek_frame_start(
|
||||
workdir_iterator *wi, workdir_iterator_frame *wf)
|
||||
{
|
||||
if (!wf)
|
||||
return;
|
||||
if (fi->leave_dir_cb)
|
||||
(void)fi->leave_dir_cb(fi);
|
||||
|
||||
if (wi->base.start)
|
||||
git_vector_bsearch2(
|
||||
&wf->index, &wf->entries, workdir_iterator__entry_cmp, wi);
|
||||
else
|
||||
wf->index = 0;
|
||||
|
||||
if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
|
||||
wf->index++;
|
||||
}
|
||||
|
||||
static int workdir_iterator__expand_dir(workdir_iterator *wi)
|
||||
{
|
||||
int error;
|
||||
workdir_iterator_frame *wf;
|
||||
|
||||
wf = workdir_iterator__alloc_frame(wi);
|
||||
GITERR_CHECK_ALLOC(wf);
|
||||
|
||||
error = git_path_dirload_with_stat(
|
||||
wi->path.ptr, wi->root_len, iterator__ignore_case(wi),
|
||||
wi->base.start, wi->base.end, &wf->entries);
|
||||
|
||||
if (error < 0 || wf->entries.length == 0) {
|
||||
workdir_iterator__free_frame(wf);
|
||||
return GIT_ENOTFOUND;
|
||||
fi->stack = ff->next;
|
||||
fi->depth--;
|
||||
}
|
||||
|
||||
if (++(wi->depth) > WORKDIR_MAX_DEPTH) {
|
||||
fs_iterator__free_frame(ff);
|
||||
}
|
||||
|
||||
static int fs_iterator__update_entry(fs_iterator *fi);
|
||||
|
||||
static int fs_iterator__entry_cmp(const void *i, const void *item)
|
||||
{
|
||||
const fs_iterator *fi = (const fs_iterator *)i;
|
||||
const git_path_with_stat *ps = item;
|
||||
return fi->base.prefixcomp(fi->base.start, ps->path);
|
||||
}
|
||||
|
||||
static void fs_iterator__seek_frame_start(
|
||||
fs_iterator *fi, fs_iterator_frame *ff)
|
||||
{
|
||||
if (!ff)
|
||||
return;
|
||||
|
||||
if (fi->base.start)
|
||||
git_vector_bsearch2(
|
||||
&ff->index, &ff->entries, fs_iterator__entry_cmp, fi);
|
||||
else
|
||||
ff->index = 0;
|
||||
}
|
||||
|
||||
static int fs_iterator__expand_dir(fs_iterator *fi)
|
||||
{
|
||||
int error;
|
||||
fs_iterator_frame *ff;
|
||||
|
||||
if (fi->depth > FS_MAX_DEPTH) {
|
||||
giterr_set(GITERR_REPOSITORY,
|
||||
"Working directory is too deep (%d)", wi->depth);
|
||||
workdir_iterator__free_frame(wf);
|
||||
"Directory nesting is too deep (%d)", fi->depth);
|
||||
return -1;
|
||||
}
|
||||
|
||||
workdir_iterator__seek_frame_start(wi, wf);
|
||||
ff = fs_iterator__alloc_frame(fi);
|
||||
GITERR_CHECK_ALLOC(ff);
|
||||
|
||||
/* only push new ignores if this is not top level directory */
|
||||
if (wi->stack != NULL) {
|
||||
ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
|
||||
(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
|
||||
error = git_path_dirload_with_stat(
|
||||
fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
|
||||
fi->base.start, fi->base.end, &ff->entries);
|
||||
|
||||
if (error < 0 || ff->entries.length == 0) {
|
||||
fs_iterator__free_frame(ff);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
wf->next = wi->stack;
|
||||
wi->stack = wf;
|
||||
fs_iterator__seek_frame_start(fi, ff);
|
||||
|
||||
return workdir_iterator__update_entry(wi);
|
||||
ff->next = fi->stack;
|
||||
fi->stack = ff;
|
||||
fi->depth++;
|
||||
|
||||
if (fi->enter_dir_cb && (error = fi->enter_dir_cb(fi)) < 0)
|
||||
return error;
|
||||
|
||||
return fs_iterator__update_entry(fi);
|
||||
}
|
||||
|
||||
static int workdir_iterator__current(
|
||||
static int fs_iterator__current(
|
||||
const git_index_entry **entry, git_iterator *self)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)self;
|
||||
fs_iterator *fi = (fs_iterator *)self;
|
||||
if (entry)
|
||||
*entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
|
||||
*entry = (fi->entry.path == NULL) ? NULL : &fi->entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int workdir_iterator__at_end(git_iterator *self)
|
||||
static int fs_iterator__at_end(git_iterator *self)
|
||||
{
|
||||
return (((workdir_iterator *)self)->entry.path == NULL);
|
||||
return (((fs_iterator *)self)->entry.path == NULL);
|
||||
}
|
||||
|
||||
static int workdir_iterator__advance_into(
|
||||
static int fs_iterator__advance_into(
|
||||
const git_index_entry **entry, git_iterator *iter)
|
||||
{
|
||||
int error = 0;
|
||||
workdir_iterator *wi = (workdir_iterator *)iter;
|
||||
fs_iterator *fi = (fs_iterator *)iter;
|
||||
|
||||
iterator__clear_entry(entry);
|
||||
|
||||
/* workdir iterator will allow you to explicitly advance into a
|
||||
* commit/submodule (as well as a tree) to avoid some cases where an
|
||||
* entry is mislabeled as a submodule in the working directory
|
||||
/* Allow you to explicitly advance into a commit/submodule (as well as a
|
||||
* tree) to avoid cases where an entry is mislabeled as a submodule in
|
||||
* the working directory. The fs iterator will never have COMMMIT
|
||||
* entries on it's own, but a wrapper might add them.
|
||||
*/
|
||||
if (wi->entry.path != NULL &&
|
||||
(wi->entry.mode == GIT_FILEMODE_TREE ||
|
||||
wi->entry.mode == GIT_FILEMODE_COMMIT))
|
||||
if (fi->entry.path != NULL &&
|
||||
(fi->entry.mode == GIT_FILEMODE_TREE ||
|
||||
fi->entry.mode == GIT_FILEMODE_COMMIT))
|
||||
/* returns GIT_ENOTFOUND if the directory is empty */
|
||||
error = workdir_iterator__expand_dir(wi);
|
||||
error = fs_iterator__expand_dir(fi);
|
||||
|
||||
if (!error && entry)
|
||||
error = workdir_iterator__current(entry, iter);
|
||||
error = fs_iterator__current(entry, iter);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int workdir_iterator__advance(
|
||||
static int fs_iterator__advance_over(
|
||||
const git_index_entry **entry, git_iterator *self)
|
||||
{
|
||||
int error = 0;
|
||||
workdir_iterator *wi = (workdir_iterator *)self;
|
||||
workdir_iterator_frame *wf;
|
||||
fs_iterator *fi = (fs_iterator *)self;
|
||||
fs_iterator_frame *ff;
|
||||
git_path_with_stat *next;
|
||||
|
||||
/* given include_trees & autoexpand, we might have to go into a tree */
|
||||
if (iterator__do_autoexpand(wi) &&
|
||||
wi->entry.path != NULL &&
|
||||
wi->entry.mode == GIT_FILEMODE_TREE)
|
||||
{
|
||||
error = workdir_iterator__advance_into(entry, self);
|
||||
|
||||
/* continue silently past empty directories if autoexpanding */
|
||||
if (error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if (entry != NULL)
|
||||
*entry = NULL;
|
||||
|
||||
while (wi->entry.path != NULL) {
|
||||
wf = wi->stack;
|
||||
next = git_vector_get(&wf->entries, ++wf->index);
|
||||
while (fi->entry.path != NULL) {
|
||||
ff = fi->stack;
|
||||
next = git_vector_get(&ff->entries, ++ff->index);
|
||||
|
||||
if (next != NULL) {
|
||||
/* match git's behavior of ignoring anything named ".git" */
|
||||
if (path_is_dotgit(next))
|
||||
continue;
|
||||
/* else found a good entry */
|
||||
if (next != NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
/* pop stack if anything is left to pop */
|
||||
if (!wf->next) {
|
||||
memset(&wi->entry, 0, sizeof(wi->entry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
wi->stack = wf->next;
|
||||
wi->depth--;
|
||||
workdir_iterator__free_frame(wf);
|
||||
git_ignore__pop_dir(&wi->ignores);
|
||||
fs_iterator__pop_frame(fi, ff, false);
|
||||
}
|
||||
|
||||
error = workdir_iterator__update_entry(wi);
|
||||
error = fs_iterator__update_entry(fi);
|
||||
|
||||
if (!error && entry != NULL)
|
||||
error = workdir_iterator__current(entry, self);
|
||||
error = fs_iterator__current(entry, self);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int workdir_iterator__seek(git_iterator *self, const char *prefix)
|
||||
static int fs_iterator__advance(
|
||||
const git_index_entry **entry, git_iterator *self)
|
||||
{
|
||||
fs_iterator *fi = (fs_iterator *)self;
|
||||
|
||||
/* given include_trees & autoexpand, we might have to go into a tree */
|
||||
if (iterator__do_autoexpand(fi) &&
|
||||
fi->entry.path != NULL &&
|
||||
fi->entry.mode == GIT_FILEMODE_TREE)
|
||||
{
|
||||
int error = fs_iterator__advance_into(entry, self);
|
||||
if (error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
/* continue silently past empty directories if autoexpanding */
|
||||
giterr_clear();
|
||||
}
|
||||
|
||||
return fs_iterator__advance_over(entry, self);
|
||||
}
|
||||
|
||||
static int fs_iterator__seek(git_iterator *self, const char *prefix)
|
||||
{
|
||||
GIT_UNUSED(self);
|
||||
GIT_UNUSED(prefix);
|
||||
@ -1073,107 +1060,192 @@ static int workdir_iterator__seek(git_iterator *self, const char *prefix)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int workdir_iterator__reset(
|
||||
static int fs_iterator__reset(
|
||||
git_iterator *self, const char *start, const char *end)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)self;
|
||||
fs_iterator *fi = (fs_iterator *)self;
|
||||
|
||||
while (wi->stack != NULL && wi->stack->next != NULL) {
|
||||
workdir_iterator_frame *wf = wi->stack;
|
||||
wi->stack = wf->next;
|
||||
workdir_iterator__free_frame(wf);
|
||||
git_ignore__pop_dir(&wi->ignores);
|
||||
}
|
||||
wi->depth = 0;
|
||||
while (fi->stack != NULL && fi->stack->next != NULL)
|
||||
fs_iterator__pop_frame(fi, fi->stack, false);
|
||||
fi->depth = 0;
|
||||
|
||||
if (iterator__reset_range(self, start, end) < 0)
|
||||
return -1;
|
||||
|
||||
workdir_iterator__seek_frame_start(wi, wi->stack);
|
||||
fs_iterator__seek_frame_start(fi, fi->stack);
|
||||
|
||||
return workdir_iterator__update_entry(wi);
|
||||
return fs_iterator__update_entry(fi);
|
||||
}
|
||||
|
||||
static void fs_iterator__free(git_iterator *self)
|
||||
{
|
||||
fs_iterator *fi = (fs_iterator *)self;
|
||||
|
||||
while (fi->stack != NULL)
|
||||
fs_iterator__pop_frame(fi, fi->stack, true);
|
||||
|
||||
git_buf_free(&fi->path);
|
||||
}
|
||||
|
||||
static int fs_iterator__update_entry(fs_iterator *fi)
|
||||
{
|
||||
git_path_with_stat *ps =
|
||||
git_vector_get(&fi->stack->entries, fi->stack->index);
|
||||
|
||||
git_buf_truncate(&fi->path, fi->root_len);
|
||||
memset(&fi->entry, 0, sizeof(fi->entry));
|
||||
|
||||
if (!ps)
|
||||
return 0;
|
||||
if (git_buf_put(&fi->path, ps->path, ps->path_len) < 0)
|
||||
return -1;
|
||||
if (iterator__past_end(fi, fi->path.ptr + fi->root_len))
|
||||
return 0;
|
||||
|
||||
fi->entry.path = ps->path;
|
||||
git_index_entry__init_from_stat(&fi->entry, &ps->st);
|
||||
|
||||
/* need different mode here to keep directories during iteration */
|
||||
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
|
||||
|
||||
/* allow wrapper to check/update the entry (can force skip) */
|
||||
if (fi->update_entry_cb &&
|
||||
fi->update_entry_cb(fi) == GIT_ENOTFOUND)
|
||||
return fs_iterator__advance_over(NULL, (git_iterator *)fi);
|
||||
|
||||
/* if this is a tree and trees aren't included, then skip */
|
||||
if (fi->entry.mode == GIT_FILEMODE_TREE && !iterator__include_trees(fi))
|
||||
return git_iterator_advance(NULL, (git_iterator *)fi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fs_iterator__initialize(
|
||||
git_iterator **out, fs_iterator *fi, const char *root)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (git_buf_sets(&fi->path, root) < 0 || git_path_to_dir(&fi->path) < 0) {
|
||||
git__free(fi);
|
||||
return -1;
|
||||
}
|
||||
fi->root_len = fi->path.size;
|
||||
|
||||
if ((error = fs_iterator__expand_dir(fi)) == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
if (error) {
|
||||
git_iterator_free((git_iterator *)fi);
|
||||
fi = NULL;
|
||||
}
|
||||
|
||||
*out = (git_iterator *)fi;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_iterator_for_filesystem(
|
||||
git_iterator **out,
|
||||
const char *root,
|
||||
git_iterator_flag_t flags,
|
||||
const char *start,
|
||||
const char *end)
|
||||
{
|
||||
fs_iterator *fi = git__calloc(1, sizeof(fs_iterator));
|
||||
GITERR_CHECK_ALLOC(fi);
|
||||
|
||||
ITERATOR_BASE_INIT(fi, fs, FS, NULL);
|
||||
|
||||
if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
|
||||
fi->base.flags |= GIT_ITERATOR_IGNORE_CASE;
|
||||
|
||||
return fs_iterator__initialize(out, fi, root);
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
fs_iterator fi;
|
||||
git_ignores ignores;
|
||||
int is_ignored;
|
||||
} workdir_iterator;
|
||||
|
||||
GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (!path || (len = path->size) < 4)
|
||||
return false;
|
||||
|
||||
if (path->ptr[len - 1] == '/')
|
||||
len--;
|
||||
|
||||
if (tolower(path->ptr[len - 1]) != 't' ||
|
||||
tolower(path->ptr[len - 2]) != 'i' ||
|
||||
tolower(path->ptr[len - 3]) != 'g' ||
|
||||
tolower(path->ptr[len - 4]) != '.')
|
||||
return false;
|
||||
|
||||
return (len == 4 || path->ptr[len - 5] == '/');
|
||||
}
|
||||
|
||||
static int workdir_iterator__enter_dir(fs_iterator *fi)
|
||||
{
|
||||
/* only push new ignores if this is not top level directory */
|
||||
if (fi->stack->next != NULL) {
|
||||
workdir_iterator *wi = (workdir_iterator *)fi;
|
||||
ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/');
|
||||
|
||||
(void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int workdir_iterator__leave_dir(fs_iterator *fi)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)fi;
|
||||
git_ignore__pop_dir(&wi->ignores);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int workdir_iterator__update_entry(fs_iterator *fi)
|
||||
{
|
||||
int error = 0;
|
||||
workdir_iterator *wi = (workdir_iterator *)fi;
|
||||
|
||||
/* skip over .git entries */
|
||||
if (workdir_path_is_dotgit(&fi->path))
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
/* reset is_ignored since we haven't checked yet */
|
||||
wi->is_ignored = -1;
|
||||
|
||||
/* check if apparent tree entries are actually submodules */
|
||||
if (fi->entry.mode != GIT_FILEMODE_TREE)
|
||||
return 0;
|
||||
|
||||
error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path);
|
||||
if (error < 0)
|
||||
giterr_clear();
|
||||
|
||||
/* mark submodule (or any dir with .git) as GITLINK and remove slash */
|
||||
if (!error || error == GIT_EEXISTS) {
|
||||
fi->entry.mode = S_IFGITLINK;
|
||||
fi->entry.path[strlen(fi->entry.path) - 1] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void workdir_iterator__free(git_iterator *self)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)self;
|
||||
|
||||
while (wi->stack != NULL) {
|
||||
workdir_iterator_frame *wf = wi->stack;
|
||||
wi->stack = wf->next;
|
||||
workdir_iterator__free_frame(wf);
|
||||
}
|
||||
|
||||
fs_iterator__free(self);
|
||||
git_ignore__free(&wi->ignores);
|
||||
git_buf_free(&wi->path);
|
||||
}
|
||||
|
||||
static int workdir_iterator__update_entry(workdir_iterator *wi)
|
||||
{
|
||||
int error = 0;
|
||||
git_path_with_stat *ps =
|
||||
git_vector_get(&wi->stack->entries, wi->stack->index);
|
||||
|
||||
git_buf_truncate(&wi->path, wi->root_len);
|
||||
memset(&wi->entry, 0, sizeof(wi->entry));
|
||||
|
||||
if (!ps)
|
||||
return 0;
|
||||
|
||||
/* skip over .git entries */
|
||||
if (path_is_dotgit(ps))
|
||||
return workdir_iterator__advance(NULL, (git_iterator *)wi);
|
||||
|
||||
if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
|
||||
return -1;
|
||||
|
||||
if (iterator__past_end(wi, wi->path.ptr + wi->root_len))
|
||||
return 0;
|
||||
|
||||
wi->entry.path = ps->path;
|
||||
|
||||
wi->is_ignored = -1;
|
||||
|
||||
git_index_entry__init_from_stat(&wi->entry, &ps->st);
|
||||
|
||||
/* need different mode here to keep directories during iteration */
|
||||
wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
|
||||
|
||||
/* if this is a file type we don't handle, treat as ignored */
|
||||
if (wi->entry.mode == 0) {
|
||||
wi->is_ignored = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if this isn't a tree, then we're done */
|
||||
if (wi->entry.mode != GIT_FILEMODE_TREE)
|
||||
return 0;
|
||||
|
||||
/* detect submodules */
|
||||
error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path);
|
||||
if (error == GIT_ENOTFOUND)
|
||||
giterr_clear();
|
||||
|
||||
if (error == GIT_EEXISTS) /* if contains .git, treat as untracked submod */
|
||||
error = 0;
|
||||
|
||||
/* if submodule, mark as GITLINK and remove trailing slash */
|
||||
if (!error) {
|
||||
size_t len = strlen(wi->entry.path);
|
||||
assert(wi->entry.path[len - 1] == '/');
|
||||
wi->entry.path[len - 1] = '\0';
|
||||
wi->entry.mode = S_IFGITLINK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iterator__include_trees(wi))
|
||||
return 0;
|
||||
|
||||
return workdir_iterator__advance(NULL, (git_iterator *)wi);
|
||||
}
|
||||
|
||||
int git_iterator_for_workdir(
|
||||
git_iterator **iter,
|
||||
git_iterator **out,
|
||||
git_repository *repo,
|
||||
git_iterator_flag_t flags,
|
||||
const char *start,
|
||||
@ -1182,38 +1254,28 @@ int git_iterator_for_workdir(
|
||||
int error;
|
||||
workdir_iterator *wi;
|
||||
|
||||
assert(iter && repo);
|
||||
if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
|
||||
return GIT_EBAREREPO;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(
|
||||
repo, "scan working directory")) < 0)
|
||||
return error;
|
||||
/* initialize as an fs iterator then do overrides */
|
||||
wi = git__calloc(1, sizeof(workdir_iterator));
|
||||
GITERR_CHECK_ALLOC(wi);
|
||||
ITERATOR_BASE_INIT((&wi->fi), fs, FS, repo);
|
||||
|
||||
ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo);
|
||||
wi->fi.base.type = GIT_ITERATOR_TYPE_WORKDIR;
|
||||
wi->fi.cb.free = workdir_iterator__free;
|
||||
wi->fi.enter_dir_cb = workdir_iterator__enter_dir;
|
||||
wi->fi.leave_dir_cb = workdir_iterator__leave_dir;
|
||||
wi->fi.update_entry_cb = workdir_iterator__update_entry;
|
||||
|
||||
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0)
|
||||
goto fail;
|
||||
|
||||
if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
|
||||
git_path_to_dir(&wi->path) < 0 ||
|
||||
git_ignore__for_path(repo, "", &wi->ignores) < 0)
|
||||
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
|
||||
(error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
|
||||
{
|
||||
git__free(wi);
|
||||
return -1;
|
||||
}
|
||||
wi->root_len = wi->path.size;
|
||||
|
||||
if ((error = workdir_iterator__expand_dir(wi)) < 0) {
|
||||
if (error != GIT_ENOTFOUND)
|
||||
goto fail;
|
||||
giterr_clear();
|
||||
git_iterator_free((git_iterator *)wi);
|
||||
return error;
|
||||
}
|
||||
|
||||
*iter = (git_iterator *)wi;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git_iterator_free((git_iterator *)wi);
|
||||
return error;
|
||||
return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo));
|
||||
}
|
||||
|
||||
|
||||
@ -1315,7 +1377,8 @@ bool git_iterator_current_is_ignored(git_iterator *iter)
|
||||
if (wi->is_ignored != -1)
|
||||
return (bool)(wi->is_ignored != 0);
|
||||
|
||||
if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
|
||||
if (git_ignore__lookup(
|
||||
&wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0)
|
||||
wi->is_ignored = true;
|
||||
|
||||
return (bool)wi->is_ignored;
|
||||
@ -1340,10 +1403,10 @@ int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
|
||||
{
|
||||
workdir_iterator *wi = (workdir_iterator *)iter;
|
||||
|
||||
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path)
|
||||
if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->fi.entry.path)
|
||||
*path = NULL;
|
||||
else
|
||||
*path = &wi->path;
|
||||
*path = &wi->fi.path;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ typedef enum {
|
||||
GIT_ITERATOR_TYPE_TREE = 1,
|
||||
GIT_ITERATOR_TYPE_INDEX = 2,
|
||||
GIT_ITERATOR_TYPE_WORKDIR = 3,
|
||||
GIT_ITERATOR_TYPE_FS = 4,
|
||||
} git_iterator_type_t;
|
||||
|
||||
typedef enum {
|
||||
@ -88,6 +89,16 @@ extern int git_iterator_for_workdir(
|
||||
const char *start,
|
||||
const char *end);
|
||||
|
||||
/* for filesystem iterators, you have to explicitly pass in the ignore_case
|
||||
* behavior that you desire
|
||||
*/
|
||||
extern int git_iterator_for_filesystem(
|
||||
git_iterator **out,
|
||||
const char *root,
|
||||
git_iterator_flag_t flags,
|
||||
const char *start,
|
||||
const char *end);
|
||||
|
||||
extern void git_iterator_free(git_iterator *iter);
|
||||
|
||||
/* Return a git_index_entry structure for the current value the iterator
|
||||
|
1348
src/merge.c
1348
src/merge.c
File diff suppressed because it is too large
Load Diff
115
src/merge.h
115
src/merge.h
@ -7,16 +7,121 @@
|
||||
#ifndef INCLUDE_merge_h__
|
||||
#define INCLUDE_merge_h__
|
||||
|
||||
#include "git2/types.h"
|
||||
#include "git2/merge.h"
|
||||
#include "commit_list.h"
|
||||
#include "vector.h"
|
||||
#include "commit_list.h"
|
||||
#include "pool.h"
|
||||
|
||||
#include "git2/merge.h"
|
||||
#include "git2/types.h"
|
||||
|
||||
#define GIT_MERGE_MSG_FILE "MERGE_MSG"
|
||||
#define GIT_MERGE_MODE_FILE "MERGE_MODE"
|
||||
|
||||
#define MERGE_CONFIG_FILE_MODE 0666
|
||||
#define GIT_MERGE_TREE_RENAME_THRESHOLD 50
|
||||
#define GIT_MERGE_TREE_TARGET_LIMIT 1000
|
||||
|
||||
int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos);
|
||||
/** Types of changes when files are merged from branch to branch. */
|
||||
typedef enum {
|
||||
/* No conflict - a change only occurs in one branch. */
|
||||
GIT_MERGE_DIFF_NONE = 0,
|
||||
|
||||
/* Occurs when a file is modified in both branches. */
|
||||
GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0),
|
||||
|
||||
/* Occurs when a file is added in both branches. */
|
||||
GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1),
|
||||
|
||||
/* Occurs when a file is deleted in both branches. */
|
||||
GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2),
|
||||
|
||||
/* Occurs when a file is modified in one branch and deleted in the other. */
|
||||
GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3),
|
||||
|
||||
/* Occurs when a file is renamed in one branch and modified in the other. */
|
||||
GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4),
|
||||
|
||||
/* Occurs when a file is renamed in one branch and deleted in the other. */
|
||||
GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5),
|
||||
|
||||
/* Occurs when a file is renamed in one branch and a file with the same
|
||||
* name is added in the other. Eg, A->B and new file B. Core git calls
|
||||
* this a "rename/delete". */
|
||||
GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6),
|
||||
|
||||
/* Occurs when both a file is renamed to the same name in the ours and
|
||||
* theirs branches. Eg, A->B and A->B in both. Automergeable. */
|
||||
GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7),
|
||||
|
||||
/* Occurs when a file is renamed to different names in the ours and theirs
|
||||
* branches. Eg, A->B and A->C. */
|
||||
GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8),
|
||||
|
||||
/* Occurs when two files are renamed to the same name in the ours and
|
||||
* theirs branches. Eg, A->C and B->C. */
|
||||
GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9),
|
||||
|
||||
/* Occurs when an item at a path in one branch is a directory, and an
|
||||
* item at the same path in a different branch is a file. */
|
||||
GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10),
|
||||
|
||||
/* The child of a folder that is in a directory/file conflict. */
|
||||
GIT_MERGE_DIFF_DF_CHILD = (1 << 11),
|
||||
} git_merge_diff_type_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_pool pool;
|
||||
|
||||
/* Vector of git_index_entry that represent the merged items that
|
||||
* have been staged, either because only one side changed, or because
|
||||
* the two changes were non-conflicting and mergeable. These items
|
||||
* will be written as staged entries in the main index.
|
||||
*/
|
||||
git_vector staged;
|
||||
|
||||
/* Vector of git_merge_diff entries that represent the conflicts that
|
||||
* have not been automerged. These items will be written to high-stage
|
||||
* entries in the main index.
|
||||
*/
|
||||
git_vector conflicts;
|
||||
|
||||
/* Vector of git_merge_diff that have been automerged. These items
|
||||
* will be written to the REUC when the index is produced.
|
||||
*/
|
||||
git_vector resolved;
|
||||
} git_merge_diff_list;
|
||||
|
||||
/**
|
||||
* Description of changes to one file across three trees.
|
||||
*/
|
||||
typedef struct {
|
||||
git_merge_diff_type_t type;
|
||||
|
||||
git_index_entry ancestor_entry;
|
||||
|
||||
git_index_entry our_entry;
|
||||
git_delta_t our_status;
|
||||
|
||||
git_index_entry their_entry;
|
||||
git_delta_t their_status;
|
||||
} git_merge_diff;
|
||||
|
||||
int git_merge__bases_many(
|
||||
git_commit_list **out,
|
||||
git_revwalk *walk,
|
||||
git_commit_list_node *one,
|
||||
git_vector *twos);
|
||||
|
||||
git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo);
|
||||
|
||||
int git_merge_diff_list__find_differences(git_merge_diff_list *merge_diff_list,
|
||||
const git_tree *ancestor_tree,
|
||||
const git_tree *ours_tree,
|
||||
const git_tree *theirs_tree);
|
||||
|
||||
int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_tree_opts *opts);
|
||||
|
||||
void git_merge_diff_list__free(git_merge_diff_list *diff_list);
|
||||
|
||||
#endif
|
||||
|
175
src/merge_file.c
Normal file
175
src/merge_file.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 "repository.h"
|
||||
#include "merge_file.h"
|
||||
|
||||
#include "git2/repository.h"
|
||||
#include "git2/object.h"
|
||||
#include "git2/index.h"
|
||||
|
||||
#include "xdiff/xdiff.h"
|
||||
|
||||
#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
|
||||
|
||||
GIT_INLINE(const char *) merge_file_best_path(
|
||||
const git_merge_file_input *ancestor,
|
||||
const git_merge_file_input *ours,
|
||||
const git_merge_file_input *theirs)
|
||||
{
|
||||
if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
|
||||
if (strcmp(ours->path, theirs->path) == 0)
|
||||
return ours->path;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strcmp(ancestor->path, ours->path) == 0)
|
||||
return theirs->path;
|
||||
else if(strcmp(ancestor->path, theirs->path) == 0)
|
||||
return ours->path;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GIT_INLINE(int) merge_file_best_mode(
|
||||
const git_merge_file_input *ancestor,
|
||||
const git_merge_file_input *ours,
|
||||
const git_merge_file_input *theirs)
|
||||
{
|
||||
/*
|
||||
* If ancestor didn't exist and either ours or theirs is executable,
|
||||
* assume executable. Otherwise, if any mode changed from the ancestor,
|
||||
* use that one.
|
||||
*/
|
||||
if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
|
||||
if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
|
||||
theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
|
||||
return GIT_FILEMODE_BLOB_EXECUTABLE;
|
||||
|
||||
return GIT_FILEMODE_BLOB;
|
||||
}
|
||||
|
||||
if (ancestor->mode == ours->mode)
|
||||
return theirs->mode;
|
||||
else if(ancestor->mode == theirs->mode)
|
||||
return ours->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_merge_file_input_from_index_entry(
|
||||
git_merge_file_input *input,
|
||||
git_repository *repo,
|
||||
const git_index_entry *entry)
|
||||
{
|
||||
git_odb *odb = NULL;
|
||||
int error = 0;
|
||||
|
||||
assert(input && repo && entry);
|
||||
|
||||
if (entry->mode == 0)
|
||||
return 0;
|
||||
|
||||
if ((error = git_repository_odb(&odb, repo)) < 0 ||
|
||||
(error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0)
|
||||
goto done;
|
||||
|
||||
input->mode = entry->mode;
|
||||
input->path = git__strdup(entry->path);
|
||||
input->mmfile.size = git_odb_object_size(input->odb_object);
|
||||
input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
|
||||
|
||||
if (input->label == NULL)
|
||||
input->label = entry->path;
|
||||
|
||||
done:
|
||||
git_odb_free(odb);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_merge_file_input_from_diff_file(
|
||||
git_merge_file_input *input,
|
||||
git_repository *repo,
|
||||
const git_diff_file *file)
|
||||
{
|
||||
git_odb *odb = NULL;
|
||||
int error = 0;
|
||||
|
||||
assert(input && repo && file);
|
||||
|
||||
if (file->mode == 0)
|
||||
return 0;
|
||||
|
||||
if ((error = git_repository_odb(&odb, repo)) < 0 ||
|
||||
(error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0)
|
||||
goto done;
|
||||
|
||||
input->mode = file->mode;
|
||||
input->path = git__strdup(file->path);
|
||||
input->mmfile.size = git_odb_object_size(input->odb_object);
|
||||
input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
|
||||
|
||||
if (input->label == NULL)
|
||||
input->label = file->path;
|
||||
|
||||
done:
|
||||
git_odb_free(odb);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_merge_files(
|
||||
git_merge_file_result *out,
|
||||
git_merge_file_input *ancestor,
|
||||
git_merge_file_input *ours,
|
||||
git_merge_file_input *theirs,
|
||||
git_merge_automerge_flags flags)
|
||||
{
|
||||
xmparam_t xmparam;
|
||||
mmbuffer_t mmbuffer;
|
||||
int xdl_result;
|
||||
int error = 0;
|
||||
|
||||
assert(out && ancestor && ours && theirs);
|
||||
|
||||
memset(out, 0x0, sizeof(git_merge_file_result));
|
||||
|
||||
if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs))
|
||||
return 0;
|
||||
|
||||
memset(&xmparam, 0x0, sizeof(xmparam_t));
|
||||
xmparam.ancestor = ancestor->label;
|
||||
xmparam.file1 = ours->label;
|
||||
xmparam.file2 = theirs->label;
|
||||
|
||||
out->path = merge_file_best_path(ancestor, ours, theirs);
|
||||
out->mode = merge_file_best_mode(ancestor, ours, theirs);
|
||||
|
||||
if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS)
|
||||
xmparam.favor = XDL_MERGE_FAVOR_OURS;
|
||||
|
||||
if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS)
|
||||
xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
|
||||
|
||||
if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
|
||||
&theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
|
||||
giterr_set(GITERR_MERGE, "Failed to merge files.");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
out->automergeable = (xdl_result == 0);
|
||||
out->data = (unsigned char *)mmbuffer.ptr;
|
||||
out->len = mmbuffer.size;
|
||||
|
||||
done:
|
||||
return error;
|
||||
}
|
||||
|
71
src/merge_file.h
Normal file
71
src/merge_file.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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_filediff_h__
|
||||
#define INCLUDE_filediff_h__
|
||||
|
||||
#include "xdiff/xdiff.h"
|
||||
|
||||
#include "git2/merge.h"
|
||||
|
||||
typedef struct {
|
||||
const char *label;
|
||||
char *path;
|
||||
unsigned int mode;
|
||||
mmfile_t mmfile;
|
||||
|
||||
git_odb_object *odb_object;
|
||||
} git_merge_file_input;
|
||||
|
||||
#define GIT_MERGE_FILE_INPUT_INIT {0}
|
||||
|
||||
typedef struct {
|
||||
bool automergeable;
|
||||
|
||||
const char *path;
|
||||
int mode;
|
||||
|
||||
unsigned char *data;
|
||||
size_t len;
|
||||
} git_merge_file_result;
|
||||
|
||||
#define GIT_MERGE_FILE_RESULT_INIT {0}
|
||||
|
||||
int git_merge_file_input_from_index_entry(
|
||||
git_merge_file_input *input,
|
||||
git_repository *repo,
|
||||
const git_index_entry *entry);
|
||||
|
||||
int git_merge_file_input_from_diff_file(
|
||||
git_merge_file_input *input,
|
||||
git_repository *repo,
|
||||
const git_diff_file *file);
|
||||
|
||||
int git_merge_files(
|
||||
git_merge_file_result *out,
|
||||
git_merge_file_input *ancestor,
|
||||
git_merge_file_input *ours,
|
||||
git_merge_file_input *theirs,
|
||||
git_merge_automerge_flags flags);
|
||||
|
||||
GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input)
|
||||
{
|
||||
assert(input);
|
||||
git__free(input->path);
|
||||
git_odb_object_free(input->odb_object);
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_merge_file_result_free(git_merge_file_result *filediff)
|
||||
{
|
||||
if (filediff == NULL)
|
||||
return;
|
||||
|
||||
/* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
|
||||
if (filediff->data != NULL)
|
||||
free(filediff->data);
|
||||
}
|
||||
|
||||
#endif
|
207
src/object.c
207
src/object.c
@ -18,65 +18,38 @@
|
||||
|
||||
static const int OBJECT_BASE_SIZE = 4096;
|
||||
|
||||
static struct {
|
||||
typedef struct {
|
||||
const char *str; /* type name string */
|
||||
int loose; /* valid loose object type flag */
|
||||
size_t size; /* size in bytes of the object structure */
|
||||
} git_objects_table[] = {
|
||||
|
||||
int (*parse)(void *self, git_odb_object *obj);
|
||||
void (*free)(void *self);
|
||||
} git_object_def;
|
||||
|
||||
static git_object_def git_objects_table[] = {
|
||||
/* 0 = GIT_OBJ__EXT1 */
|
||||
{ "", 0, 0},
|
||||
{ "", 0, NULL, NULL },
|
||||
|
||||
/* 1 = GIT_OBJ_COMMIT */
|
||||
{ "commit", 1, sizeof(struct git_commit)},
|
||||
{ "commit", sizeof(git_commit), git_commit__parse, git_commit__free },
|
||||
|
||||
/* 2 = GIT_OBJ_TREE */
|
||||
{ "tree", 1, sizeof(struct git_tree) },
|
||||
{ "tree", sizeof(git_tree), git_tree__parse, git_tree__free },
|
||||
|
||||
/* 3 = GIT_OBJ_BLOB */
|
||||
{ "blob", 1, sizeof(struct git_blob) },
|
||||
{ "blob", sizeof(git_blob), git_blob__parse, git_blob__free },
|
||||
|
||||
/* 4 = GIT_OBJ_TAG */
|
||||
{ "tag", 1, sizeof(struct git_tag) },
|
||||
{ "tag", sizeof(git_tag), git_tag__parse, git_tag__free },
|
||||
|
||||
/* 5 = GIT_OBJ__EXT2 */
|
||||
{ "", 0, 0 },
|
||||
|
||||
{ "", 0, NULL, NULL },
|
||||
/* 6 = GIT_OBJ_OFS_DELTA */
|
||||
{ "OFS_DELTA", 0, 0 },
|
||||
|
||||
{ "OFS_DELTA", 0, NULL, NULL },
|
||||
/* 7 = GIT_OBJ_REF_DELTA */
|
||||
{ "REF_DELTA", 0, 0 }
|
||||
{ "REF_DELTA", 0, NULL, NULL },
|
||||
};
|
||||
|
||||
static int create_object(git_object **object_out, git_otype type)
|
||||
{
|
||||
git_object *object = NULL;
|
||||
|
||||
assert(object_out);
|
||||
|
||||
*object_out = NULL;
|
||||
|
||||
switch (type) {
|
||||
case GIT_OBJ_COMMIT:
|
||||
case GIT_OBJ_TAG:
|
||||
case GIT_OBJ_BLOB:
|
||||
case GIT_OBJ_TREE:
|
||||
object = git__malloc(git_object__size(type));
|
||||
GITERR_CHECK_ALLOC(object);
|
||||
memset(object, 0x0, git_object__size(type));
|
||||
break;
|
||||
|
||||
default:
|
||||
giterr_set(GITERR_INVALID, "The given type is invalid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
object->type = type;
|
||||
|
||||
*object_out = object;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_object__from_odb_object(
|
||||
git_object **object_out,
|
||||
git_repository *repo,
|
||||
@ -84,51 +57,57 @@ int git_object__from_odb_object(
|
||||
git_otype type)
|
||||
{
|
||||
int error;
|
||||
size_t object_size;
|
||||
git_object_def *def;
|
||||
git_object *object = NULL;
|
||||
|
||||
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
|
||||
giterr_set(GITERR_INVALID, "The requested type does not match the type in the ODB");
|
||||
assert(object_out);
|
||||
*object_out = NULL;
|
||||
|
||||
/* Validate type match */
|
||||
if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"The requested type does not match the type in the ODB");
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
type = odb_obj->raw.type;
|
||||
|
||||
if ((error = create_object(&object, type)) < 0)
|
||||
return error;
|
||||
|
||||
/* Initialize parent object */
|
||||
git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
|
||||
object->repo = repo;
|
||||
|
||||
switch (type) {
|
||||
case GIT_OBJ_COMMIT:
|
||||
error = git_commit__parse((git_commit *)object, odb_obj);
|
||||
break;
|
||||
|
||||
case GIT_OBJ_TREE:
|
||||
error = git_tree__parse((git_tree *)object, odb_obj);
|
||||
break;
|
||||
|
||||
case GIT_OBJ_TAG:
|
||||
error = git_tag__parse((git_tag *)object, odb_obj);
|
||||
break;
|
||||
|
||||
case GIT_OBJ_BLOB:
|
||||
error = git_blob__parse((git_blob *)object, odb_obj);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
if ((object_size = git_object__size(odb_obj->cached.type)) == 0) {
|
||||
giterr_set(GITERR_INVALID, "The requested type is invalid");
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
git_object__free(object);
|
||||
/* Allocate and initialize base object */
|
||||
object = git__calloc(1, object_size);
|
||||
GITERR_CHECK_ALLOC(object);
|
||||
|
||||
git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
|
||||
object->cached.type = odb_obj->cached.type;
|
||||
object->cached.size = odb_obj->cached.size;
|
||||
object->repo = repo;
|
||||
|
||||
/* Parse raw object data */
|
||||
def = &git_objects_table[odb_obj->cached.type];
|
||||
assert(def->free && def->parse);
|
||||
|
||||
if ((error = def->parse(object, odb_obj)) < 0)
|
||||
def->free(object);
|
||||
else
|
||||
*object_out = git_cache_try_store(&repo->objects, object);
|
||||
*object_out = git_cache_store_parsed(&repo->objects, object);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void git_object__free(void *obj)
|
||||
{
|
||||
git_otype type = ((git_object *)obj)->cached.type;
|
||||
|
||||
if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) ||
|
||||
!git_objects_table[type].free)
|
||||
git__free(obj);
|
||||
else
|
||||
git_objects_table[type].free(obj);
|
||||
}
|
||||
|
||||
int git_object_lookup_prefix(
|
||||
git_object **object_out,
|
||||
git_repository *repo,
|
||||
@ -154,27 +133,38 @@ int git_object_lookup_prefix(
|
||||
len = GIT_OID_HEXSZ;
|
||||
|
||||
if (len == GIT_OID_HEXSZ) {
|
||||
git_cached_obj *cached = NULL;
|
||||
|
||||
/* We want to match the full id : we can first look up in the cache,
|
||||
* since there is no need to check for non ambiguousity
|
||||
*/
|
||||
object = git_cache_get(&repo->objects, id);
|
||||
if (object != NULL) {
|
||||
if (type != GIT_OBJ_ANY && type != object->type) {
|
||||
git_object_free(object);
|
||||
giterr_set(GITERR_INVALID, "The requested type does not match the type in ODB");
|
||||
return GIT_ENOTFOUND;
|
||||
cached = git_cache_get_any(&repo->objects, id);
|
||||
if (cached != NULL) {
|
||||
if (cached->flags == GIT_CACHE_STORE_PARSED) {
|
||||
object = (git_object *)cached;
|
||||
|
||||
if (type != GIT_OBJ_ANY && type != object->cached.type) {
|
||||
git_object_free(object);
|
||||
giterr_set(GITERR_INVALID,
|
||||
"The requested type does not match the type in ODB");
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
*object_out = object;
|
||||
return 0;
|
||||
} else if (cached->flags == GIT_CACHE_STORE_RAW) {
|
||||
odb_obj = (git_odb_object *)cached;
|
||||
} else {
|
||||
assert(!"Wrong caching type in the global object cache");
|
||||
}
|
||||
|
||||
*object_out = object;
|
||||
return 0;
|
||||
} else {
|
||||
/* Object was not found in the cache, let's explore the backends.
|
||||
* We could just use git_odb_read_unique_short_oid,
|
||||
* it is the same cost for packed and loose object backends,
|
||||
* but it may be much more costly for sqlite and hiredis.
|
||||
*/
|
||||
error = git_odb_read(&odb_obj, odb, id);
|
||||
}
|
||||
|
||||
/* Object was not found in the cache, let's explore the backends.
|
||||
* We could just use git_odb_read_unique_short_oid,
|
||||
* it is the same cost for packed and loose object backends,
|
||||
* but it may be much more costly for sqlite and hiredis.
|
||||
*/
|
||||
error = git_odb_read(&odb_obj, odb, id);
|
||||
} else {
|
||||
git_oid short_oid;
|
||||
|
||||
@ -211,41 +201,12 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
|
||||
return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
|
||||
}
|
||||
|
||||
void git_object__free(void *_obj)
|
||||
{
|
||||
git_object *object = (git_object *)_obj;
|
||||
|
||||
assert(object);
|
||||
|
||||
switch (object->type) {
|
||||
case GIT_OBJ_COMMIT:
|
||||
git_commit__free((git_commit *)object);
|
||||
break;
|
||||
|
||||
case GIT_OBJ_TREE:
|
||||
git_tree__free((git_tree *)object);
|
||||
break;
|
||||
|
||||
case GIT_OBJ_TAG:
|
||||
git_tag__free((git_tag *)object);
|
||||
break;
|
||||
|
||||
case GIT_OBJ_BLOB:
|
||||
git_blob__free((git_blob *)object);
|
||||
break;
|
||||
|
||||
default:
|
||||
git__free(object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void git_object_free(git_object *object)
|
||||
{
|
||||
if (object == NULL)
|
||||
return;
|
||||
|
||||
git_cached_obj_decref((git_cached_obj *)object, git_object__free);
|
||||
git_cached_obj_decref(object);
|
||||
}
|
||||
|
||||
const git_oid *git_object_id(const git_object *obj)
|
||||
@ -257,7 +218,7 @@ const git_oid *git_object_id(const git_object *obj)
|
||||
git_otype git_object_type(const git_object *obj)
|
||||
{
|
||||
assert(obj);
|
||||
return obj->type;
|
||||
return obj->cached.type;
|
||||
}
|
||||
|
||||
git_repository *git_object_owner(const git_object *obj)
|
||||
@ -293,7 +254,7 @@ int git_object_typeisloose(git_otype type)
|
||||
if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
|
||||
return 0;
|
||||
|
||||
return git_objects_table[type].loose;
|
||||
return (git_objects_table[type].size > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
size_t git_object__size(git_otype type)
|
||||
|
@ -11,7 +11,6 @@
|
||||
struct git_object {
|
||||
git_cached_obj cached;
|
||||
git_repository *repo;
|
||||
git_otype type;
|
||||
};
|
||||
|
||||
/* fully free the object; internal method, DO NOT EXPORT */
|
||||
|
129
src/object_api.c
Normal file
129
src/object_api.c
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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 "git2/object.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "repository.h"
|
||||
|
||||
#include "commit.h"
|
||||
#include "tree.h"
|
||||
#include "blob.h"
|
||||
#include "tag.h"
|
||||
|
||||
/**
|
||||
* Blob
|
||||
*/
|
||||
int git_commit_lookup(git_commit **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_COMMIT);
|
||||
}
|
||||
|
||||
int git_commit_lookup_prefix(git_commit **out, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_COMMIT);
|
||||
}
|
||||
|
||||
void git_commit_free(git_commit *obj)
|
||||
{
|
||||
git_object_free((git_object *)obj);
|
||||
}
|
||||
|
||||
const git_oid *git_commit_id(const git_commit *obj)
|
||||
{
|
||||
return git_object_id((const git_object *)obj);
|
||||
}
|
||||
|
||||
git_repository *git_commit_owner(const git_commit *obj)
|
||||
{
|
||||
return git_object_owner((const git_object *)obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tree
|
||||
*/
|
||||
int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE);
|
||||
}
|
||||
|
||||
int git_tree_lookup_prefix(git_tree **out, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_TREE);
|
||||
}
|
||||
|
||||
void git_tree_free(git_tree *obj)
|
||||
{
|
||||
git_object_free((git_object *)obj);
|
||||
}
|
||||
|
||||
const git_oid *git_tree_id(const git_tree *obj)
|
||||
{
|
||||
return git_object_id((const git_object *)obj);
|
||||
}
|
||||
|
||||
git_repository *git_tree_owner(const git_tree *obj)
|
||||
{
|
||||
return git_object_owner((const git_object *)obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tag
|
||||
*/
|
||||
int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TAG);
|
||||
}
|
||||
|
||||
int git_tag_lookup_prefix(git_tag **out, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_TAG);
|
||||
}
|
||||
|
||||
void git_tag_free(git_tag *obj)
|
||||
{
|
||||
git_object_free((git_object *)obj);
|
||||
}
|
||||
|
||||
const git_oid *git_tag_id(const git_tag *obj)
|
||||
{
|
||||
return git_object_id((const git_object *)obj);
|
||||
}
|
||||
|
||||
git_repository *git_tag_owner(const git_tag *obj)
|
||||
{
|
||||
return git_object_owner((const git_object *)obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blob
|
||||
*/
|
||||
int git_blob_lookup(git_blob **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_BLOB);
|
||||
}
|
||||
|
||||
int git_blob_lookup_prefix(git_blob **out, git_repository *repo, const git_oid *id, size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJ_BLOB);
|
||||
}
|
||||
|
||||
void git_blob_free(git_blob *obj)
|
||||
{
|
||||
git_object_free((git_object *)obj);
|
||||
}
|
||||
|
||||
const git_oid *git_blob_id(const git_blob *obj)
|
||||
{
|
||||
return git_object_id((const git_object *)obj);
|
||||
}
|
||||
|
||||
git_repository *git_blob_owner(const git_blob *obj)
|
||||
{
|
||||
return git_object_owner((const git_object *)obj);
|
||||
}
|
147
src/odb.c
147
src/odb.c
@ -8,11 +8,13 @@
|
||||
#include "common.h"
|
||||
#include <zlib.h>
|
||||
#include "git2/object.h"
|
||||
#include "git2/sys/odb_backend.h"
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "odb.h"
|
||||
#include "delta-apply.h"
|
||||
#include "filter.h"
|
||||
#include "repository.h"
|
||||
|
||||
#include "git2/odb_backend.h"
|
||||
#include "git2/oid.h"
|
||||
@ -29,10 +31,19 @@ typedef struct
|
||||
{
|
||||
git_odb_backend *backend;
|
||||
int priority;
|
||||
int is_alternate;
|
||||
bool is_alternate;
|
||||
ino_t disk_inode;
|
||||
} backend_internal;
|
||||
|
||||
size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE;
|
||||
static git_cache *odb_cache(git_odb *odb)
|
||||
{
|
||||
if (odb->rc.owner != NULL) {
|
||||
git_repository *owner = odb->rc.owner;
|
||||
return &owner->objects;
|
||||
}
|
||||
|
||||
return &odb->own_cache;
|
||||
}
|
||||
|
||||
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
|
||||
|
||||
@ -54,6 +65,7 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj)
|
||||
|
||||
if (!git_object_typeisloose(obj->type))
|
||||
return -1;
|
||||
|
||||
if (!obj->data && obj->len != 0)
|
||||
return -1;
|
||||
|
||||
@ -70,23 +82,24 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj)
|
||||
}
|
||||
|
||||
|
||||
static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source)
|
||||
static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source)
|
||||
{
|
||||
git_odb_object *object = git__malloc(sizeof(git_odb_object));
|
||||
memset(object, 0x0, sizeof(git_odb_object));
|
||||
git_odb_object *object = git__calloc(1, sizeof(git_odb_object));
|
||||
|
||||
git_oid_cpy(&object->cached.oid, oid);
|
||||
memcpy(&object->raw, source, sizeof(git_rawobj));
|
||||
if (object != NULL) {
|
||||
git_oid_cpy(&object->cached.oid, oid);
|
||||
object->cached.type = source->type;
|
||||
object->cached.size = source->len;
|
||||
object->buffer = source->data;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void free_odb_object(void *o)
|
||||
void git_odb_object__free(void *object)
|
||||
{
|
||||
git_odb_object *object = (git_odb_object *)o;
|
||||
|
||||
if (object != NULL) {
|
||||
git__free(object->raw.data);
|
||||
git__free(((git_odb_object *)object)->buffer);
|
||||
git__free(object);
|
||||
}
|
||||
}
|
||||
@ -98,17 +111,17 @@ const git_oid *git_odb_object_id(git_odb_object *object)
|
||||
|
||||
const void *git_odb_object_data(git_odb_object *object)
|
||||
{
|
||||
return object->raw.data;
|
||||
return object->buffer;
|
||||
}
|
||||
|
||||
size_t git_odb_object_size(git_odb_object *object)
|
||||
{
|
||||
return object->raw.len;
|
||||
return object->cached.size;
|
||||
}
|
||||
|
||||
git_otype git_odb_object_type(git_odb_object *object)
|
||||
{
|
||||
return object->raw.type;
|
||||
return object->cached.type;
|
||||
}
|
||||
|
||||
void git_odb_object_free(git_odb_object *object)
|
||||
@ -116,7 +129,7 @@ void git_odb_object_free(git_odb_object *object)
|
||||
if (object == NULL)
|
||||
return;
|
||||
|
||||
git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
|
||||
git_cached_obj_decref(object);
|
||||
}
|
||||
|
||||
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
|
||||
@ -353,9 +366,8 @@ int git_odb_new(git_odb **out)
|
||||
git_odb *db = git__calloc(1, sizeof(*db));
|
||||
GITERR_CHECK_ALLOC(db);
|
||||
|
||||
if (git_cache_init(&db->cache, git_odb__cache_size, &free_odb_object) < 0 ||
|
||||
git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
|
||||
{
|
||||
if (git_cache_init(&db->own_cache) < 0 ||
|
||||
git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
|
||||
git__free(db);
|
||||
return -1;
|
||||
}
|
||||
@ -365,7 +377,9 @@ int git_odb_new(git_odb **out)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
|
||||
static int add_backend_internal(
|
||||
git_odb *odb, git_odb_backend *backend,
|
||||
int priority, bool is_alternate, ino_t disk_inode)
|
||||
{
|
||||
backend_internal *internal;
|
||||
|
||||
@ -382,6 +396,7 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
|
||||
internal->backend = backend;
|
||||
internal->priority = priority;
|
||||
internal->is_alternate = is_alternate;
|
||||
internal->disk_inode = disk_inode;
|
||||
|
||||
if (git_vector_insert(&odb->backends, internal) < 0) {
|
||||
git__free(internal);
|
||||
@ -395,26 +410,74 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
|
||||
|
||||
int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
|
||||
{
|
||||
return add_backend_internal(odb, backend, priority, 0);
|
||||
return add_backend_internal(odb, backend, priority, false, 0);
|
||||
}
|
||||
|
||||
int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
|
||||
{
|
||||
return add_backend_internal(odb, backend, priority, 1);
|
||||
return add_backend_internal(odb, backend, priority, true, 0);
|
||||
}
|
||||
|
||||
static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth)
|
||||
size_t git_odb_num_backends(git_odb *odb)
|
||||
{
|
||||
assert(odb);
|
||||
return odb->backends.length;
|
||||
}
|
||||
|
||||
int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos)
|
||||
{
|
||||
backend_internal *internal;
|
||||
|
||||
assert(odb && odb);
|
||||
internal = git_vector_get(&odb->backends, pos);
|
||||
|
||||
if (internal && internal->backend) {
|
||||
*out = internal->backend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
static int add_default_backends(
|
||||
git_odb *db, const char *objects_dir,
|
||||
bool as_alternates, int alternate_depth)
|
||||
{
|
||||
size_t i;
|
||||
struct stat st;
|
||||
ino_t inode;
|
||||
git_odb_backend *loose, *packed;
|
||||
|
||||
/* TODO: inodes are not really relevant on Win32, so we need to find
|
||||
* a cross-platform workaround for this */
|
||||
#ifdef GIT_WIN32
|
||||
GIT_UNUSED(i);
|
||||
GIT_UNUSED(st);
|
||||
|
||||
inode = 0;
|
||||
#else
|
||||
if (p_stat(objects_dir, &st) < 0) {
|
||||
giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
inode = st.st_ino;
|
||||
|
||||
for (i = 0; i < db->backends.length; ++i) {
|
||||
backend_internal *backend = git_vector_get(&db->backends, i);
|
||||
if (backend->disk_inode == inode)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* add the loose object backend */
|
||||
if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
|
||||
add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)
|
||||
add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
|
||||
return -1;
|
||||
|
||||
/* add the packed file backend */
|
||||
if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
|
||||
add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
|
||||
add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0)
|
||||
return -1;
|
||||
|
||||
return load_alternates(db, objects_dir, alternate_depth);
|
||||
@ -464,7 +527,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_
|
||||
alternate = git_buf_cstr(&alternates_path);
|
||||
}
|
||||
|
||||
if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0)
|
||||
if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -476,7 +539,7 @@ static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_
|
||||
|
||||
int git_odb_add_disk_alternate(git_odb *odb, const char *path)
|
||||
{
|
||||
return add_default_backends(odb, path, 1, 0);
|
||||
return add_default_backends(odb, path, true, 0);
|
||||
}
|
||||
|
||||
int git_odb_open(git_odb **out, const char *objects_dir)
|
||||
@ -514,7 +577,7 @@ static void odb_free(git_odb *db)
|
||||
}
|
||||
|
||||
git_vector_free(&db->backends);
|
||||
git_cache_free(&db->cache);
|
||||
git_cache_free(&db->own_cache);
|
||||
git__free(db);
|
||||
}
|
||||
|
||||
@ -535,7 +598,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
|
||||
|
||||
assert(db && id);
|
||||
|
||||
if ((object = git_cache_get(&db->cache, id)) != NULL) {
|
||||
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
|
||||
git_odb_object_free(object);
|
||||
return (int)true;
|
||||
}
|
||||
@ -585,9 +648,9 @@ int git_odb__read_header_or_object(
|
||||
|
||||
assert(db && id && out && len_p && type_p);
|
||||
|
||||
if ((object = git_cache_get(&db->cache, id)) != NULL) {
|
||||
*len_p = object->raw.len;
|
||||
*type_p = object->raw.type;
|
||||
if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
|
||||
*len_p = object->cached.size;
|
||||
*type_p = object->cached.type;
|
||||
*out = object;
|
||||
return 0;
|
||||
}
|
||||
@ -612,8 +675,8 @@ int git_odb__read_header_or_object(
|
||||
if ((error = git_odb_read(&object, db, id)) < 0)
|
||||
return error; /* error already set - pass along */
|
||||
|
||||
*len_p = object->raw.len;
|
||||
*type_p = object->raw.type;
|
||||
*len_p = object->cached.size;
|
||||
*type_p = object->cached.type;
|
||||
*out = object;
|
||||
|
||||
return 0;
|
||||
@ -625,6 +688,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
|
||||
int error;
|
||||
bool refreshed = false;
|
||||
git_rawobj raw;
|
||||
git_odb_object *object;
|
||||
|
||||
assert(out && db && id);
|
||||
|
||||
@ -633,7 +697,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
*out = git_cache_get(&db->cache, id);
|
||||
*out = git_cache_get_raw(odb_cache(db), id);
|
||||
if (*out != NULL)
|
||||
return 0;
|
||||
|
||||
@ -659,7 +723,10 @@ attempt_lookup:
|
||||
if (error && error != GIT_PASSTHROUGH)
|
||||
return error;
|
||||
|
||||
*out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
|
||||
if ((object = odb_object__alloc(id, &raw)) == NULL)
|
||||
return -1;
|
||||
|
||||
*out = git_cache_store_raw(odb_cache(db), object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -672,6 +739,7 @@ int git_odb_read_prefix(
|
||||
git_rawobj raw;
|
||||
void *data = NULL;
|
||||
bool found = false, refreshed = false;
|
||||
git_odb_object *object;
|
||||
|
||||
assert(out && db);
|
||||
|
||||
@ -682,7 +750,7 @@ int git_odb_read_prefix(
|
||||
len = GIT_OID_HEXSZ;
|
||||
|
||||
if (len == GIT_OID_HEXSZ) {
|
||||
*out = git_cache_get(&db->cache, short_id);
|
||||
*out = git_cache_get_raw(odb_cache(db), short_id);
|
||||
if (*out != NULL)
|
||||
return 0;
|
||||
}
|
||||
@ -704,7 +772,7 @@ attempt_lookup:
|
||||
git__free(data);
|
||||
data = raw.data;
|
||||
|
||||
if (found && git_oid_cmp(&full_oid, &found_full_oid))
|
||||
if (found && git_oid__cmp(&full_oid, &found_full_oid))
|
||||
return git_odb__error_ambiguous("multiple matches for prefix");
|
||||
|
||||
found_full_oid = full_oid;
|
||||
@ -723,7 +791,10 @@ attempt_lookup:
|
||||
if (!found)
|
||||
return git_odb__error_notfound("no match for prefix", short_id);
|
||||
|
||||
*out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));
|
||||
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
|
||||
return -1;
|
||||
|
||||
*out = git_cache_store_raw(odb_cache(db), object);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -29,14 +29,14 @@ typedef struct {
|
||||
/* EXPORT */
|
||||
struct git_odb_object {
|
||||
git_cached_obj cached;
|
||||
git_rawobj raw;
|
||||
void *buffer;
|
||||
};
|
||||
|
||||
/* EXPORT */
|
||||
struct git_odb {
|
||||
git_refcount rc;
|
||||
git_vector backends;
|
||||
git_cache cache;
|
||||
git_cache own_cache;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -96,4 +96,7 @@ int git_odb__read_header_or_object(
|
||||
git_odb_object **out, size_t *len_p, git_otype *type_p,
|
||||
git_odb *db, const git_oid *id);
|
||||
|
||||
/* fully free the object; internal method, DO NOT EXPORT */
|
||||
void git_odb_object__free(void *object);
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "common.h"
|
||||
#include <zlib.h>
|
||||
#include "git2/object.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/sys/odb_backend.h"
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "odb.h"
|
||||
|
110
src/odb_pack.c
110
src/odb_pack.c
@ -8,7 +8,8 @@
|
||||
#include "common.h"
|
||||
#include <zlib.h>
|
||||
#include "git2/repository.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/indexer.h"
|
||||
#include "git2/sys/odb_backend.h"
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "odb.h"
|
||||
@ -206,7 +207,7 @@ static int packfile_load__cb(void *_data, git_buf *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
error = git_packfile_check(&pack, path->ptr);
|
||||
error = git_packfile_alloc(&pack, path->ptr);
|
||||
if (error == GIT_ENOTFOUND)
|
||||
/* ignore missing .pack file as git does */
|
||||
return 0;
|
||||
@ -526,67 +527,17 @@ static void pack_backend__free(git_odb_backend *_backend)
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
|
||||
static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
|
||||
{
|
||||
struct pack_backend *backend = NULL;
|
||||
struct git_pack_file *packfile = NULL;
|
||||
|
||||
if (git_packfile_check(&packfile, idx) < 0)
|
||||
return -1;
|
||||
|
||||
backend = git__calloc(1, sizeof(struct pack_backend));
|
||||
struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
|
||||
GITERR_CHECK_ALLOC(backend);
|
||||
backend->parent.version = GIT_ODB_BACKEND_VERSION;
|
||||
|
||||
if (git_vector_init(&backend->packs, 1, NULL) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (git_vector_insert(&backend->packs, packfile) < 0)
|
||||
goto on_error;
|
||||
|
||||
backend->parent.read = &pack_backend__read;
|
||||
backend->parent.read_prefix = &pack_backend__read_prefix;
|
||||
backend->parent.read_header = &pack_backend__read_header;
|
||||
backend->parent.exists = &pack_backend__exists;
|
||||
backend->parent.refresh = &pack_backend__refresh;
|
||||
backend->parent.foreach = &pack_backend__foreach;
|
||||
backend->parent.free = &pack_backend__free;
|
||||
|
||||
*backend_out = (git_odb_backend *)backend;
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_vector_free(&backend->packs);
|
||||
git__free(backend);
|
||||
git__free(packfile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
backend = git__calloc(1, sizeof(struct pack_backend));
|
||||
GITERR_CHECK_ALLOC(backend);
|
||||
backend->parent.version = GIT_ODB_BACKEND_VERSION;
|
||||
|
||||
if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||
|
||||
git_buf_joinpath(&path, objects_dir, "pack") < 0)
|
||||
{
|
||||
if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
|
||||
git__free(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_path_isdir(git_buf_cstr(&path)) == true) {
|
||||
int error;
|
||||
|
||||
backend->pack_folder = git_buf_detach(&path);
|
||||
error = pack_backend__refresh((git_odb_backend *)backend);
|
||||
if (error < 0)
|
||||
return error;
|
||||
}
|
||||
backend->parent.version = GIT_ODB_BACKEND_VERSION;
|
||||
|
||||
backend->parent.read = &pack_backend__read;
|
||||
backend->parent.read_prefix = &pack_backend__read_prefix;
|
||||
@ -597,9 +548,54 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
|
||||
backend->parent.writepack = &pack_backend__writepack;
|
||||
backend->parent.free = &pack_backend__free;
|
||||
|
||||
*out = backend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
|
||||
{
|
||||
struct pack_backend *backend = NULL;
|
||||
struct git_pack_file *packfile = NULL;
|
||||
|
||||
if (pack_backend__alloc(&backend, 1) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_packfile_alloc(&packfile, idx) < 0 ||
|
||||
git_vector_insert(&backend->packs, packfile) < 0)
|
||||
{
|
||||
pack_backend__free((git_odb_backend *)backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*backend_out = (git_odb_backend *)backend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
|
||||
{
|
||||
int error = 0;
|
||||
struct pack_backend *backend = NULL;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
if (pack_backend__alloc(&backend, 8) < 0)
|
||||
return -1;
|
||||
|
||||
if (!(error = git_buf_joinpath(&path, objects_dir, "pack")) &&
|
||||
git_path_isdir(git_buf_cstr(&path)))
|
||||
{
|
||||
backend->pack_folder = git_buf_detach(&path);
|
||||
|
||||
error = pack_backend__refresh((git_odb_backend *)backend);
|
||||
}
|
||||
|
||||
if (error < 0) {
|
||||
pack_backend__free((git_odb_backend *)backend);
|
||||
backend = NULL;
|
||||
}
|
||||
|
||||
*backend_out = (git_odb_backend *)backend;
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
39
src/oid.c
39
src/oid.c
@ -166,18 +166,26 @@ void git_oid_cpy(git_oid *out, const git_oid *src)
|
||||
memcpy(out->id, src->id, sizeof(out->id));
|
||||
}
|
||||
|
||||
int git_oid_cmp(const git_oid *a, const git_oid *b)
|
||||
{
|
||||
return git_oid__cmp(a, b);
|
||||
}
|
||||
|
||||
int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
|
||||
{
|
||||
const unsigned char *a = oid_a->id;
|
||||
const unsigned char *b = oid_b->id;
|
||||
|
||||
do {
|
||||
if (len > GIT_OID_HEXSZ)
|
||||
len = GIT_OID_HEXSZ;
|
||||
|
||||
while (len > 1) {
|
||||
if (*a != *b)
|
||||
return 1;
|
||||
a++;
|
||||
b++;
|
||||
len -= 2;
|
||||
} while (len > 1);
|
||||
};
|
||||
|
||||
if (len)
|
||||
if ((*a ^ *b) & 0xf0)
|
||||
@ -186,14 +194,31 @@ int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_oid_streq(const git_oid *a, const char *str)
|
||||
int git_oid_strcmp(const git_oid *oid_a, const char *str)
|
||||
{
|
||||
git_oid id;
|
||||
const unsigned char *a = oid_a->id;
|
||||
unsigned char strval;
|
||||
int hexval;
|
||||
|
||||
if (git_oid_fromstr(&id, str) < 0)
|
||||
return -1;
|
||||
for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
|
||||
if ((hexval = git__fromhex(*str++)) < 0)
|
||||
return -1;
|
||||
strval = hexval << 4;
|
||||
if (*str) {
|
||||
if ((hexval = git__fromhex(*str++)) < 0)
|
||||
return -1;
|
||||
strval |= hexval;
|
||||
}
|
||||
if (*a != strval)
|
||||
return (*a - strval);
|
||||
}
|
||||
|
||||
return git_oid_cmp(a, &id) == 0 ? 0 : -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_oid_streq(const git_oid *oid_a, const char *str)
|
||||
{
|
||||
return git_oid_strcmp(oid_a, str) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
int git_oid_iszero(const git_oid *oid_a)
|
||||
|
33
src/oid.h
Normal file
33
src/oid.h
Normal file
@ -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_oid_h__
|
||||
#define INCLUDE_oid_h__
|
||||
|
||||
#include "git2/oid.h"
|
||||
|
||||
/*
|
||||
* Compare two oid structures.
|
||||
*
|
||||
* @param a first oid structure.
|
||||
* @param b second oid structure.
|
||||
* @return <0, 0, >0 if a < b, a == b, a > b.
|
||||
*/
|
||||
GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
|
||||
{
|
||||
const unsigned char *sha1 = a->id;
|
||||
const unsigned char *sha2 = b->id;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
|
||||
if (*sha1 != *sha2)
|
||||
return *sha1 - *sha2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
10
src/oidmap.h
10
src/oidmap.h
@ -19,17 +19,15 @@
|
||||
__KHASH_TYPE(oid, const git_oid *, void *);
|
||||
typedef khash_t(oid) git_oidmap;
|
||||
|
||||
GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid)
|
||||
GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
|
||||
{
|
||||
int i;
|
||||
khint_t h = 0;
|
||||
for (i = 0; i < 20; ++i)
|
||||
h = (h << 5) - h + oid->id[i];
|
||||
khint_t h;
|
||||
memcpy(&h, oid, sizeof(khint_t));
|
||||
return h;
|
||||
}
|
||||
|
||||
#define GIT__USE_OIDMAP \
|
||||
__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, hash_git_oid, git_oid_equal)
|
||||
__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal)
|
||||
|
||||
#define git_oidmap_alloc() kh_init(oid)
|
||||
#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
|
||||
|
@ -1284,6 +1284,21 @@ static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *pay
|
||||
git_buf_cstr(&ctx->buf));
|
||||
}
|
||||
|
||||
int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid)
|
||||
{
|
||||
git_commit *commit;
|
||||
|
||||
if (git_commit_lookup(&commit, pb->repo, oid) < 0 ||
|
||||
git_packbuilder_insert(pb, oid, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0)
|
||||
return -1;
|
||||
|
||||
git_commit_free(commit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid)
|
||||
{
|
||||
git_tree *tree;
|
||||
|
136
src/pack.c
136
src/pack.c
@ -12,8 +12,8 @@
|
||||
#include "sha1_lookup.h"
|
||||
#include "mwindow.h"
|
||||
#include "fileops.h"
|
||||
#include "oid.h"
|
||||
|
||||
#include "git2/oid.h"
|
||||
#include <zlib.h>
|
||||
|
||||
static int packfile_open(struct git_pack_file *p);
|
||||
@ -205,13 +205,18 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (p_fstat(fd, &st) < 0 ||
|
||||
!S_ISREG(st.st_mode) ||
|
||||
if (p_fstat(fd, &st) < 0) {
|
||||
p_close(fd);
|
||||
giterr_set(GITERR_OS, "Unable to stat pack index '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode) ||
|
||||
!git__is_sizet(st.st_size) ||
|
||||
(idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
|
||||
{
|
||||
p_close(fd);
|
||||
giterr_set(GITERR_OS, "Failed to check pack index.");
|
||||
giterr_set(GITERR_ODB, "Invalid pack index '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -288,32 +293,40 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
|
||||
}
|
||||
}
|
||||
|
||||
p->index_version = version;
|
||||
p->num_objects = nr;
|
||||
p->index_version = version;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pack_index_open(struct git_pack_file *p)
|
||||
{
|
||||
char *idx_name;
|
||||
int error;
|
||||
size_t name_len, offset;
|
||||
int error = 0;
|
||||
size_t name_len, base_len;
|
||||
|
||||
if (p->index_map.data)
|
||||
if (p->index_version > -1)
|
||||
return 0;
|
||||
|
||||
idx_name = git__strdup(p->pack_name);
|
||||
GITERR_CHECK_ALLOC(idx_name);
|
||||
name_len = strlen(p->pack_name);
|
||||
assert(name_len > strlen(".pack")); /* checked by git_pack_file alloc */
|
||||
|
||||
name_len = strlen(idx_name);
|
||||
offset = name_len - strlen(".pack");
|
||||
assert(offset < name_len); /* make sure no underflow */
|
||||
if ((idx_name = git__malloc(name_len)) == NULL)
|
||||
return -1;
|
||||
|
||||
strncpy(idx_name + offset, ".idx", name_len - offset);
|
||||
base_len = name_len - strlen(".pack");
|
||||
memcpy(idx_name, p->pack_name, base_len);
|
||||
memcpy(idx_name + base_len, ".idx", sizeof(".idx"));
|
||||
|
||||
if ((error = git_mutex_lock(&p->lock)) < 0)
|
||||
return error;
|
||||
|
||||
if (p->index_version == -1)
|
||||
error = pack_index_check(idx_name, p);
|
||||
|
||||
error = pack_index_check(idx_name, p);
|
||||
git__free(idx_name);
|
||||
|
||||
git_mutex_unlock(&p->lock);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -389,12 +402,12 @@ int git_packfile_unpack_header(
|
||||
* the maximum deflated object size is 2^137, which is just
|
||||
* insane, so we know won't exceed what we have been given.
|
||||
*/
|
||||
// base = pack_window_open(p, w_curs, *curpos, &left);
|
||||
/* base = pack_window_open(p, w_curs, *curpos, &left); */
|
||||
base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);
|
||||
if (base == NULL)
|
||||
return GIT_EBUFS;
|
||||
|
||||
ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
|
||||
ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
|
||||
git_mwindow_close(w_curs);
|
||||
if (ret == GIT_EBUFS)
|
||||
return ret;
|
||||
@ -786,23 +799,14 @@ git_off_t get_delta_base(
|
||||
*
|
||||
***********************************************************/
|
||||
|
||||
static struct git_pack_file *packfile_alloc(size_t extra)
|
||||
{
|
||||
struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra);
|
||||
if (p != NULL)
|
||||
p->mwf.fd = -1;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void git_packfile_free(struct git_pack_file *p)
|
||||
{
|
||||
assert(p);
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
cache_free(&p->bases);
|
||||
|
||||
git_mwindow_free_all(&p->mwf);
|
||||
git_mwindow_file_deregister(&p->mwf);
|
||||
|
||||
if (p->mwf.fd != -1)
|
||||
p_close(p->mwf.fd);
|
||||
@ -810,6 +814,8 @@ void git_packfile_free(struct git_pack_file *p)
|
||||
pack_index_free(p);
|
||||
|
||||
git__free(p->bad_object_sha1);
|
||||
|
||||
git_mutex_free(&p->lock);
|
||||
git__free(p);
|
||||
}
|
||||
|
||||
@ -820,17 +826,22 @@ static int packfile_open(struct git_pack_file *p)
|
||||
git_oid sha1;
|
||||
unsigned char *idx_sha1;
|
||||
|
||||
assert(p->index_map.data);
|
||||
|
||||
if (!p->index_map.data && pack_index_open(p) < 0)
|
||||
if (p->index_version == -1 && pack_index_open(p) < 0)
|
||||
return git_odb__error_notfound("failed to open packfile", NULL);
|
||||
|
||||
/* if mwf opened by another thread, return now */
|
||||
if (git_mutex_lock(&p->lock) < 0)
|
||||
return packfile_error("failed to get lock for open");
|
||||
|
||||
if (p->mwf.fd >= 0) {
|
||||
git_mutex_unlock(&p->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: open with noatime */
|
||||
p->mwf.fd = git_futils_open_ro(p->pack_name);
|
||||
if (p->mwf.fd < 0) {
|
||||
p->mwf.fd = -1;
|
||||
return -1;
|
||||
}
|
||||
if (p->mwf.fd < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (p_fstat(p->mwf.fd, &st) < 0 ||
|
||||
git_mwindow_file_register(&p->mwf) < 0)
|
||||
@ -871,44 +882,54 @@ static int packfile_open(struct git_pack_file *p)
|
||||
|
||||
idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
|
||||
|
||||
if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) == 0)
|
||||
return 0;
|
||||
if (git_oid__cmp(&sha1, (git_oid *)idx_sha1) != 0)
|
||||
goto cleanup;
|
||||
|
||||
git_mutex_unlock(&p->lock);
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
|
||||
|
||||
p_close(p->mwf.fd);
|
||||
p->mwf.fd = -1;
|
||||
|
||||
git_mutex_unlock(&p->lock);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_packfile_check(struct git_pack_file **pack_out, const char *path)
|
||||
int git_packfile_alloc(struct git_pack_file **pack_out, const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
struct git_pack_file *p;
|
||||
size_t path_len;
|
||||
size_t path_len = path ? strlen(path) : 0;
|
||||
|
||||
*pack_out = NULL;
|
||||
path_len = strlen(path);
|
||||
p = packfile_alloc(path_len + 2);
|
||||
|
||||
if (path_len < strlen(".idx"))
|
||||
return git_odb__error_notfound("invalid packfile path", NULL);
|
||||
|
||||
p = git__calloc(1, sizeof(*p) + path_len + 2);
|
||||
GITERR_CHECK_ALLOC(p);
|
||||
|
||||
memcpy(p->pack_name, path, path_len + 1);
|
||||
|
||||
/*
|
||||
* Make sure a corresponding .pack file exists and that
|
||||
* the index looks sane.
|
||||
*/
|
||||
path_len -= strlen(".idx");
|
||||
if (path_len < 1) {
|
||||
git__free(p);
|
||||
return git_odb__error_notfound("invalid packfile path", NULL);
|
||||
if (git__suffixcmp(path, ".idx") == 0) {
|
||||
size_t root_len = path_len - strlen(".idx");
|
||||
|
||||
memcpy(p->pack_name + root_len, ".keep", sizeof(".keep"));
|
||||
if (git_path_exists(p->pack_name) == true)
|
||||
p->pack_keep = 1;
|
||||
|
||||
memcpy(p->pack_name + root_len, ".pack", sizeof(".pack"));
|
||||
path_len = path_len - strlen(".idx") + strlen(".pack");
|
||||
}
|
||||
|
||||
memcpy(p->pack_name, path, path_len);
|
||||
|
||||
strcpy(p->pack_name + path_len, ".keep");
|
||||
if (git_path_exists(p->pack_name) == true)
|
||||
p->pack_keep = 1;
|
||||
|
||||
strcpy(p->pack_name + path_len, ".pack");
|
||||
if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
|
||||
git__free(p);
|
||||
return git_odb__error_notfound("packfile not found", NULL);
|
||||
@ -917,9 +938,13 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
|
||||
/* ok, it looks sane as far as we can check without
|
||||
* actually mapping the pack file.
|
||||
*/
|
||||
p->mwf.fd = -1;
|
||||
p->mwf.size = st.st_size;
|
||||
p->pack_local = 1;
|
||||
p->mtime = (git_time_t)st.st_mtime;
|
||||
p->index_version = -1;
|
||||
|
||||
git_mutex_init(&p->lock);
|
||||
|
||||
/* see if we can parse the sha1 oid in the packfile name */
|
||||
if (path_len < 40 ||
|
||||
@ -1034,12 +1059,11 @@ static int pack_entry_find_offset(
|
||||
|
||||
*offset_out = 0;
|
||||
|
||||
if (index == NULL) {
|
||||
if (p->index_version == -1) {
|
||||
int error;
|
||||
|
||||
if ((error = pack_index_open(p)) < 0)
|
||||
return error;
|
||||
|
||||
assert(p->index_map.data);
|
||||
|
||||
index = p->index_map.data;
|
||||
@ -1099,6 +1123,7 @@ static int pack_entry_find_offset(
|
||||
return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
|
||||
if (found > 1)
|
||||
return git_odb__error_ambiguous("found multiple offsets for pack entry");
|
||||
|
||||
*offset_out = nth_packed_object_offset(p, pos);
|
||||
git_oid_fromraw(found_oid, current);
|
||||
|
||||
@ -1110,6 +1135,7 @@ static int pack_entry_find_offset(
|
||||
printf("found lo=%d %s\n", lo, hex_sha1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1128,7 +1154,7 @@ int git_pack_entry_find(
|
||||
if (len == GIT_OID_HEXSZ && p->num_bad_objects) {
|
||||
unsigned i;
|
||||
for (i = 0; i < p->num_bad_objects; i++)
|
||||
if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0)
|
||||
if (git_oid__cmp(short_oid, &p->bad_object_sha1[i]) == 0)
|
||||
return packfile_error("bad object found in packfile");
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,7 @@ typedef struct {
|
||||
struct git_pack_file {
|
||||
git_mwindow_file mwf;
|
||||
git_map index_map;
|
||||
git_mutex lock; /* protect updates to mwf and index_map */
|
||||
|
||||
uint32_t num_objects;
|
||||
uint32_t num_bad_objects;
|
||||
@ -142,7 +143,8 @@ git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
|
||||
git_off_t delta_obj_offset);
|
||||
|
||||
void git_packfile_free(struct git_pack_file *p);
|
||||
int git_packfile_check(struct git_pack_file **pack_out, const char *path);
|
||||
int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
|
||||
|
||||
int git_pack_entry_find(
|
||||
struct git_pack_entry *e,
|
||||
struct git_pack_file *p,
|
||||
|
@ -177,9 +177,9 @@ int git_push_add_refspec(git_push *push, const char *refspec)
|
||||
|
||||
int git_push_update_tips(git_push *push)
|
||||
{
|
||||
git_refspec *fetch_spec = &push->remote->fetch;
|
||||
git_buf remote_ref_name = GIT_BUF_INIT;
|
||||
size_t i, j;
|
||||
git_refspec *fetch_spec;
|
||||
push_spec *push_spec;
|
||||
git_reference *remote_ref;
|
||||
push_status *status;
|
||||
@ -191,7 +191,8 @@ int git_push_update_tips(git_push *push)
|
||||
continue;
|
||||
|
||||
/* Find the corresponding remote ref */
|
||||
if (!git_refspec_src_matches(fetch_spec, status->ref))
|
||||
fetch_spec = git_remote__matching_refspec(push->remote, status->ref);
|
||||
if (!fetch_spec)
|
||||
continue;
|
||||
|
||||
if ((error = git_refspec_transform_r(&remote_ref_name, fetch_spec, status->ref)) < 0)
|
||||
@ -375,7 +376,7 @@ static int queue_differences(
|
||||
const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j);
|
||||
int cmp = 0;
|
||||
|
||||
if (!git_oid_cmp(&b_entry->oid, &d_entry->oid))
|
||||
if (!git_oid__cmp(&b_entry->oid, &d_entry->oid))
|
||||
goto loop;
|
||||
|
||||
cmp = strcmp(b_entry->filename, d_entry->filename);
|
||||
|
40
src/refdb.c
40
src/refdb.c
@ -7,15 +7,16 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "posix.h"
|
||||
|
||||
#include "git2/object.h"
|
||||
#include "git2/refs.h"
|
||||
#include "git2/refdb.h"
|
||||
#include "git2/sys/refdb_backend.h"
|
||||
|
||||
#include "hash.h"
|
||||
#include "refdb.h"
|
||||
#include "refs.h"
|
||||
|
||||
#include "git2/refdb_backend.h"
|
||||
|
||||
int git_refdb_new(git_refdb **out, git_repository *repo)
|
||||
{
|
||||
git_refdb *db;
|
||||
@ -45,7 +46,7 @@ int git_refdb_open(git_refdb **out, git_repository *repo)
|
||||
return -1;
|
||||
|
||||
/* Add the default (filesystem) backend */
|
||||
if (git_refdb_backend_fs(&dir, repo, db) < 0) {
|
||||
if (git_refdb_backend_fs(&dir, repo) < 0) {
|
||||
git_refdb_free(db);
|
||||
return -1;
|
||||
}
|
||||
@ -57,15 +58,19 @@ int git_refdb_open(git_refdb **out, git_repository *repo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
|
||||
static void refdb_free_backend(git_refdb *db)
|
||||
{
|
||||
if (db->backend) {
|
||||
if(db->backend->free)
|
||||
if (db->backend->free)
|
||||
db->backend->free(db->backend);
|
||||
else
|
||||
git__free(db->backend);
|
||||
}
|
||||
}
|
||||
|
||||
int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
|
||||
{
|
||||
refdb_free_backend(db);
|
||||
db->backend = backend;
|
||||
|
||||
return 0;
|
||||
@ -75,22 +80,15 @@ int git_refdb_compress(git_refdb *db)
|
||||
{
|
||||
assert(db);
|
||||
|
||||
if (db->backend->compress) {
|
||||
if (db->backend->compress)
|
||||
return db->backend->compress(db->backend);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void refdb_free(git_refdb *db)
|
||||
{
|
||||
if (db->backend) {
|
||||
if(db->backend->free)
|
||||
db->backend->free(db->backend);
|
||||
else
|
||||
git__free(db->backend);
|
||||
}
|
||||
|
||||
refdb_free_backend(db);
|
||||
git__free(db);
|
||||
}
|
||||
|
||||
@ -111,9 +109,19 @@ int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
|
||||
|
||||
int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
|
||||
{
|
||||
assert(db && db->backend && ref_name);
|
||||
git_reference *ref;
|
||||
int error;
|
||||
|
||||
return db->backend->lookup(out, db->backend, ref_name);
|
||||
assert(db && db->backend && out && ref_name);
|
||||
|
||||
if (!(error = db->backend->lookup(&ref, db->backend, ref_name))) {
|
||||
ref->db = db;
|
||||
*out = ref;
|
||||
} else {
|
||||
*out = NULL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_refdb_foreach(
|
||||
|
@ -41,6 +41,6 @@ int git_refdb_foreach_glob(
|
||||
|
||||
int git_refdb_write(git_refdb *refdb, const git_reference *ref);
|
||||
|
||||
int git_refdb_delete(struct git_refdb *refdb, const git_reference *ref);
|
||||
int git_refdb_delete(git_refdb *refdb, const git_reference *ref);
|
||||
|
||||
#endif
|
||||
|
135
src/refdb_fs.c
135
src/refdb_fs.c
@ -11,14 +11,14 @@
|
||||
#include "fileops.h"
|
||||
#include "pack.h"
|
||||
#include "reflog.h"
|
||||
#include "config.h"
|
||||
#include "refdb.h"
|
||||
#include "refdb_fs.h"
|
||||
|
||||
#include <git2/tag.h>
|
||||
#include <git2/object.h>
|
||||
#include <git2/refdb.h>
|
||||
#include <git2/refdb_backend.h>
|
||||
#include <git2/sys/refdb_backend.h>
|
||||
#include <git2/sys/refs.h>
|
||||
|
||||
GIT__USE_STRMAP;
|
||||
|
||||
@ -26,8 +26,15 @@ GIT__USE_STRMAP;
|
||||
#define MAX_NESTING_LEVEL 10
|
||||
|
||||
enum {
|
||||
GIT_PACKREF_HAS_PEEL = 1,
|
||||
GIT_PACKREF_WAS_LOOSE = 2
|
||||
PACKREF_HAS_PEEL = 1,
|
||||
PACKREF_WAS_LOOSE = 2,
|
||||
PACKREF_CANNOT_PEEL = 4
|
||||
};
|
||||
|
||||
enum {
|
||||
PEELING_NONE = 0,
|
||||
PEELING_STANDARD,
|
||||
PEELING_FULL
|
||||
};
|
||||
|
||||
struct packref {
|
||||
@ -41,10 +48,10 @@ typedef struct refdb_fs_backend {
|
||||
git_refdb_backend parent;
|
||||
|
||||
git_repository *repo;
|
||||
const char *path;
|
||||
git_refdb *refdb;
|
||||
char *path;
|
||||
|
||||
git_refcache refcache;
|
||||
int peeling_mode;
|
||||
} refdb_fs_backend;
|
||||
|
||||
static int reference_read(
|
||||
@ -133,10 +140,6 @@ static int packed_parse_peel(
|
||||
if (tag_ref == NULL)
|
||||
goto corrupt;
|
||||
|
||||
/* Ensure reference is a tag */
|
||||
if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
|
||||
goto corrupt;
|
||||
|
||||
if (buffer + GIT_OID_HEXSZ > buffer_end)
|
||||
goto corrupt;
|
||||
|
||||
@ -155,6 +158,7 @@ static int packed_parse_peel(
|
||||
goto corrupt;
|
||||
}
|
||||
|
||||
tag_ref->flags |= PACKREF_HAS_PEEL;
|
||||
*buffer_out = buffer;
|
||||
return 0;
|
||||
|
||||
@ -206,6 +210,30 @@ static int packed_load(refdb_fs_backend *backend)
|
||||
buffer_start = (const char *)packfile.ptr;
|
||||
buffer_end = (const char *)(buffer_start) + packfile.size;
|
||||
|
||||
backend->peeling_mode = PEELING_NONE;
|
||||
|
||||
if (buffer_start[0] == '#') {
|
||||
static const char *traits_header = "# pack-refs with: ";
|
||||
|
||||
if (git__prefixcmp(buffer_start, traits_header) == 0) {
|
||||
char *traits = (char *)buffer_start + strlen(traits_header);
|
||||
char *traits_end = strchr(traits, '\n');
|
||||
|
||||
if (traits_end == NULL)
|
||||
goto parse_failed;
|
||||
|
||||
*traits_end = '\0';
|
||||
|
||||
if (strstr(traits, " fully-peeled ") != NULL) {
|
||||
backend->peeling_mode = PEELING_FULL;
|
||||
} else if (strstr(traits, " peeled ") != NULL) {
|
||||
backend->peeling_mode = PEELING_STANDARD;
|
||||
}
|
||||
|
||||
buffer_start = traits_end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (buffer_start < buffer_end && buffer_start[0] == '#') {
|
||||
buffer_start = strchr(buffer_start, '\n');
|
||||
if (buffer_start == NULL)
|
||||
@ -224,6 +252,10 @@ static int packed_load(refdb_fs_backend *backend)
|
||||
if (buffer_start[0] == '^') {
|
||||
if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
|
||||
goto parse_failed;
|
||||
} else if (backend->peeling_mode == PEELING_FULL ||
|
||||
(backend->peeling_mode == PEELING_STANDARD &&
|
||||
git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) {
|
||||
ref->flags |= PACKREF_CANNOT_PEEL;
|
||||
}
|
||||
|
||||
git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
|
||||
@ -241,7 +273,7 @@ parse_failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int loose_parse_oid(git_oid *oid, git_buf *file_content)
|
||||
static int loose_parse_oid(git_oid *oid, const char *filename, git_buf *file_content)
|
||||
{
|
||||
size_t len;
|
||||
const char *str;
|
||||
@ -263,7 +295,7 @@ static int loose_parse_oid(git_oid *oid, git_buf *file_content)
|
||||
return 0;
|
||||
|
||||
corrupted:
|
||||
giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
|
||||
giterr_set(GITERR_REFERENCE, "Corrupted loose reference file: %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -290,13 +322,13 @@ static int loose_lookup_to_packfile(
|
||||
memcpy(ref->name, name, name_len);
|
||||
ref->name[name_len] = 0;
|
||||
|
||||
if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
|
||||
if (loose_parse_oid(&ref->oid, name, &ref_file) < 0) {
|
||||
git_buf_free(&ref_file);
|
||||
git__free(ref);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ref->flags = GIT_PACKREF_WAS_LOOSE;
|
||||
ref->flags = PACKREF_WAS_LOOSE;
|
||||
|
||||
*ref_out = ref;
|
||||
git_buf_free(&ref_file);
|
||||
@ -430,12 +462,12 @@ static int loose_lookup(
|
||||
goto done;
|
||||
}
|
||||
|
||||
*out = git_reference__alloc(backend->refdb, ref_name, NULL, target);
|
||||
*out = git_reference__alloc_symbolic(ref_name, target);
|
||||
} else {
|
||||
if ((error = loose_parse_oid(&oid, &ref_file)) < 0)
|
||||
if ((error = loose_parse_oid(&oid, ref_name, &ref_file)) < 0)
|
||||
goto done;
|
||||
|
||||
*out = git_reference__alloc(backend->refdb, ref_name, &oid, NULL);
|
||||
*out = git_reference__alloc(ref_name, &oid, NULL);
|
||||
}
|
||||
|
||||
if (*out == NULL)
|
||||
@ -484,7 +516,8 @@ static int packed_lookup(
|
||||
if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0)
|
||||
return error;
|
||||
|
||||
if ((*out = git_reference__alloc(backend->refdb, ref_name, &entry->oid, NULL)) == NULL)
|
||||
if ((*out = git_reference__alloc(ref_name,
|
||||
&entry->oid, &entry->peel)) == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
@ -678,14 +711,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
|
||||
{
|
||||
git_object *object;
|
||||
|
||||
if (ref->flags & GIT_PACKREF_HAS_PEEL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Only applies to tags, i.e. references
|
||||
* in the /refs/tags folder
|
||||
*/
|
||||
if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
|
||||
if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -706,7 +732,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
|
||||
* Find the object pointed at by this tag
|
||||
*/
|
||||
git_oid_cpy(&ref->peel, git_tag_target_id(tag));
|
||||
ref->flags |= GIT_PACKREF_HAS_PEEL;
|
||||
ref->flags |= PACKREF_HAS_PEEL;
|
||||
|
||||
/*
|
||||
* The reference has now cached the resolved OID, and is
|
||||
@ -739,7 +765,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
|
||||
* This obviously only applies to tags.
|
||||
* The required peels have already been loaded into `ref->peel_target`.
|
||||
*/
|
||||
if (ref->flags & GIT_PACKREF_HAS_PEEL) {
|
||||
if (ref->flags & PACKREF_HAS_PEEL) {
|
||||
char peel[GIT_OID_HEXSZ + 1];
|
||||
git_oid_fmt(peel, &ref->peel);
|
||||
peel[GIT_OID_HEXSZ] = 0;
|
||||
@ -776,7 +802,7 @@ static int packed_remove_loose(
|
||||
for (i = 0; i < packing_list->length; ++i) {
|
||||
struct packref *ref = git_vector_get(packing_list, i);
|
||||
|
||||
if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
|
||||
if ((ref->flags & PACKREF_WAS_LOOSE) == 0)
|
||||
continue;
|
||||
|
||||
if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0)
|
||||
@ -993,22 +1019,65 @@ static void refdb_fs_backend__free(git_refdb_backend *_backend)
|
||||
backend = (refdb_fs_backend *)_backend;
|
||||
|
||||
refcache_free(&backend->refcache);
|
||||
git__free(backend->path);
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
static int setup_namespace(git_buf *path, git_repository *repo)
|
||||
{
|
||||
char *parts, *start, *end;
|
||||
|
||||
/* Load the path to the repo first */
|
||||
git_buf_puts(path, repo->path_repository);
|
||||
|
||||
/* if the repo is not namespaced, nothing else to do */
|
||||
if (repo->namespace == NULL)
|
||||
return 0;
|
||||
|
||||
parts = end = git__strdup(repo->namespace);
|
||||
if (parts == NULL)
|
||||
return -1;
|
||||
|
||||
/**
|
||||
* From `man gitnamespaces`:
|
||||
* namespaces which include a / will expand to a hierarchy
|
||||
* of namespaces; for example, GIT_NAMESPACE=foo/bar will store
|
||||
* refs under refs/namespaces/foo/refs/namespaces/bar/
|
||||
*/
|
||||
while ((start = git__strsep(&end, "/")) != NULL) {
|
||||
git_buf_printf(path, "refs/namespaces/%s/", start);
|
||||
}
|
||||
|
||||
git_buf_printf(path, "refs/namespaces/%s/refs", end);
|
||||
free(parts);
|
||||
|
||||
/* Make sure that the folder with the namespace exists */
|
||||
if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0)
|
||||
return -1;
|
||||
|
||||
/* Return the root of the namespaced path, i.e. without the trailing '/refs' */
|
||||
git_buf_rtruncate_at_char(path, '/');
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_refdb_backend_fs(
|
||||
git_refdb_backend **backend_out,
|
||||
git_repository *repository,
|
||||
git_refdb *refdb)
|
||||
git_repository *repository)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
refdb_fs_backend *backend;
|
||||
|
||||
backend = git__calloc(1, sizeof(refdb_fs_backend));
|
||||
GITERR_CHECK_ALLOC(backend);
|
||||
|
||||
backend->repo = repository;
|
||||
backend->path = repository->path_repository;
|
||||
backend->refdb = refdb;
|
||||
|
||||
if (setup_namespace(&path, repository) < 0) {
|
||||
git__free(backend);
|
||||
return -1;
|
||||
}
|
||||
|
||||
backend->path = git_buf_detach(&path);
|
||||
|
||||
backend->parent.exists = &refdb_fs_backend__exists;
|
||||
backend->parent.lookup = &refdb_fs_backend__lookup;
|
||||
|
173
src/refs.c
173
src/refs.c
@ -19,7 +19,7 @@
|
||||
#include <git2/branch.h>
|
||||
#include <git2/refs.h>
|
||||
#include <git2/refdb.h>
|
||||
#include <git2/refdb_backend.h>
|
||||
#include <git2/sys/refs.h>
|
||||
|
||||
GIT__USE_STRMAP;
|
||||
|
||||
@ -31,37 +31,58 @@ enum {
|
||||
GIT_PACKREF_WAS_LOOSE = 2
|
||||
};
|
||||
|
||||
|
||||
git_reference *git_reference__alloc(
|
||||
git_refdb *refdb,
|
||||
const char *name,
|
||||
const git_oid *oid,
|
||||
const char *symbolic)
|
||||
static git_reference *alloc_ref(const char *name)
|
||||
{
|
||||
git_reference *ref;
|
||||
size_t namelen;
|
||||
|
||||
assert(refdb && name && ((oid && !symbolic) || (!oid && symbolic)));
|
||||
|
||||
namelen = strlen(name);
|
||||
size_t namelen = strlen(name);
|
||||
|
||||
if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (oid) {
|
||||
ref->type = GIT_REF_OID;
|
||||
git_oid_cpy(&ref->target.oid, oid);
|
||||
} else {
|
||||
ref->type = GIT_REF_SYMBOLIC;
|
||||
memcpy(ref->name, name, namelen + 1);
|
||||
|
||||
if ((ref->target.symbolic = git__strdup(symbolic)) == NULL) {
|
||||
git__free(ref);
|
||||
return NULL;
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
git_reference *git_reference__alloc_symbolic(
|
||||
const char *name, const char *target)
|
||||
{
|
||||
git_reference *ref;
|
||||
|
||||
assert(name && target);
|
||||
|
||||
ref = alloc_ref(name);
|
||||
if (!ref)
|
||||
return NULL;
|
||||
|
||||
ref->type = GIT_REF_SYMBOLIC;
|
||||
|
||||
if ((ref->target.symbolic = git__strdup(target)) == NULL) {
|
||||
git__free(ref);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref->db = refdb;
|
||||
memcpy(ref->name, name, namelen + 1);
|
||||
return ref;
|
||||
}
|
||||
|
||||
git_reference *git_reference__alloc(
|
||||
const char *name,
|
||||
const git_oid *oid,
|
||||
const git_oid *peel)
|
||||
{
|
||||
git_reference *ref;
|
||||
|
||||
assert(name && oid);
|
||||
|
||||
ref = alloc_ref(name);
|
||||
if (!ref)
|
||||
return NULL;
|
||||
|
||||
ref->type = GIT_REF_OID;
|
||||
git_oid_cpy(&ref->target.oid, oid);
|
||||
|
||||
if (peel != NULL)
|
||||
git_oid_cpy(&ref->peel, peel);
|
||||
|
||||
return ref;
|
||||
}
|
||||
@ -71,13 +92,8 @@ void git_reference_free(git_reference *reference)
|
||||
if (reference == NULL)
|
||||
return;
|
||||
|
||||
if (reference->type == GIT_REF_SYMBOLIC) {
|
||||
if (reference->type == GIT_REF_SYMBOLIC)
|
||||
git__free(reference->target.symbolic);
|
||||
reference->target.symbolic = NULL;
|
||||
}
|
||||
|
||||
reference->db = NULL;
|
||||
reference->type = GIT_REF_INVALID;
|
||||
|
||||
git__free(reference);
|
||||
}
|
||||
@ -305,6 +321,16 @@ const git_oid *git_reference_target(const git_reference *ref)
|
||||
return &ref->target.oid;
|
||||
}
|
||||
|
||||
const git_oid *git_reference_target_peel(const git_reference *ref)
|
||||
{
|
||||
assert(ref);
|
||||
|
||||
if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->peel))
|
||||
return NULL;
|
||||
|
||||
return &ref->peel;
|
||||
}
|
||||
|
||||
const char *git_reference_symbolic_target(const git_reference *ref)
|
||||
{
|
||||
assert(ref);
|
||||
@ -336,8 +362,15 @@ static int reference__create(
|
||||
(error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if ((ref = git_reference__alloc(refdb, name, oid, symbolic)) == NULL)
|
||||
return -1;
|
||||
if (oid != NULL) {
|
||||
assert(symbolic == NULL);
|
||||
ref = git_reference__alloc(name, oid, NULL);
|
||||
} else {
|
||||
ref = git_reference__alloc_symbolic(name, symbolic);
|
||||
}
|
||||
|
||||
GITERR_CHECK_ALLOC(ref);
|
||||
ref->db = refdb;
|
||||
|
||||
if ((error = git_refdb_write(refdb, ref)) < 0) {
|
||||
git_reference_free(ref);
|
||||
@ -437,8 +470,6 @@ int git_reference_rename(
|
||||
char normalized[GIT_REFNAME_MAX];
|
||||
bool should_head_be_updated = false;
|
||||
git_reference *result = NULL;
|
||||
git_oid *oid;
|
||||
const char *symbolic;
|
||||
int error = 0;
|
||||
int reference_has_log;
|
||||
|
||||
@ -447,7 +478,8 @@ int git_reference_rename(
|
||||
normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
|
||||
GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
|
||||
|
||||
if ((error = git_reference_normalize_name(normalized, sizeof(normalized), new_name, normalization_flags)) < 0 ||
|
||||
if ((error = git_reference_normalize_name(
|
||||
normalized, sizeof(normalized), new_name, normalization_flags)) < 0 ||
|
||||
(error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0)
|
||||
return error;
|
||||
|
||||
@ -455,16 +487,18 @@ int git_reference_rename(
|
||||
* Create the new reference.
|
||||
*/
|
||||
if (ref->type == GIT_REF_OID) {
|
||||
oid = &ref->target.oid;
|
||||
symbolic = NULL;
|
||||
result = git_reference__alloc(new_name, &ref->target.oid, &ref->peel);
|
||||
} else if (ref->type == GIT_REF_SYMBOLIC) {
|
||||
result = git_reference__alloc_symbolic(new_name, ref->target.symbolic);
|
||||
} else {
|
||||
oid = NULL;
|
||||
symbolic = ref->target.symbolic;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
if ((result = git_reference__alloc(ref->db, new_name, oid, symbolic)) == NULL)
|
||||
if (result == NULL)
|
||||
return -1;
|
||||
|
||||
result->db = ref->db;
|
||||
|
||||
/* Check if we have to update HEAD. */
|
||||
if ((error = git_branch_is_head(ref)) < 0)
|
||||
goto on_error;
|
||||
@ -509,11 +543,17 @@ on_error:
|
||||
|
||||
int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
|
||||
{
|
||||
if (ref->type == GIT_REF_OID)
|
||||
switch (git_reference_type(ref)) {
|
||||
case GIT_REF_OID:
|
||||
return git_reference_lookup(ref_out, ref->db->repo, ref->name);
|
||||
else
|
||||
return git_reference_lookup_resolved(ref_out, ref->db->repo,
|
||||
ref->target.symbolic, -1);
|
||||
|
||||
case GIT_REF_SYMBOLIC:
|
||||
return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
|
||||
|
||||
default:
|
||||
giterr_set(GITERR_REFERENCE, "Invalid reference");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int git_reference_foreach(
|
||||
@ -712,6 +752,7 @@ int git_reference__normalize_name(
|
||||
goto cleanup;
|
||||
|
||||
if ((segments_count == 1 ) &&
|
||||
!(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) &&
|
||||
!(is_all_caps_and_underscore(name, (size_t)segment_len) ||
|
||||
((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
|
||||
goto cleanup;
|
||||
@ -778,16 +819,20 @@ int git_reference__normalize_name_lax(
|
||||
|
||||
int git_reference_cmp(git_reference *ref1, git_reference *ref2)
|
||||
{
|
||||
git_ref_t type1, type2;
|
||||
assert(ref1 && ref2);
|
||||
|
||||
/* let's put symbolic refs before OIDs */
|
||||
if (ref1->type != ref2->type)
|
||||
return (ref1->type == GIT_REF_SYMBOLIC) ? -1 : 1;
|
||||
type1 = git_reference_type(ref1);
|
||||
type2 = git_reference_type(ref2);
|
||||
|
||||
if (ref1->type == GIT_REF_SYMBOLIC)
|
||||
/* let's put symbolic refs before OIDs */
|
||||
if (type1 != type2)
|
||||
return (type1 == GIT_REF_SYMBOLIC) ? -1 : 1;
|
||||
|
||||
if (type1 == GIT_REF_SYMBOLIC)
|
||||
return strcmp(ref1->target.symbolic, ref2->target.symbolic);
|
||||
|
||||
return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
|
||||
return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
|
||||
}
|
||||
|
||||
static int reference__update_terminal(
|
||||
@ -905,15 +950,6 @@ static int peel_error(int error, git_reference *ref, const char* msg)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int reference_target(git_object **object, git_reference *ref)
|
||||
{
|
||||
const git_oid *oid;
|
||||
|
||||
oid = git_reference_target(ref);
|
||||
|
||||
return git_object_lookup(object, git_reference_owner(ref), oid, GIT_OBJ_ANY);
|
||||
}
|
||||
|
||||
int git_reference_peel(
|
||||
git_object **peeled,
|
||||
git_reference *ref,
|
||||
@ -925,10 +961,22 @@ int git_reference_peel(
|
||||
|
||||
assert(ref);
|
||||
|
||||
if ((error = git_reference_resolve(&resolved, ref)) < 0)
|
||||
return peel_error(error, ref, "Cannot resolve reference");
|
||||
if (ref->type == GIT_REF_OID) {
|
||||
resolved = ref;
|
||||
} else {
|
||||
if ((error = git_reference_resolve(&resolved, ref)) < 0)
|
||||
return peel_error(error, ref, "Cannot resolve reference");
|
||||
}
|
||||
|
||||
if ((error = reference_target(&target, resolved)) < 0) {
|
||||
if (!git_oid_iszero(&resolved->peel)) {
|
||||
error = git_object_lookup(&target,
|
||||
git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
|
||||
} else {
|
||||
error = git_object_lookup(&target,
|
||||
git_reference_owner(ref), &resolved->target.oid, GIT_OBJ_ANY);
|
||||
}
|
||||
|
||||
if (error < 0) {
|
||||
peel_error(error, ref, "Cannot retrieve reference target");
|
||||
goto cleanup;
|
||||
}
|
||||
@ -940,7 +988,10 @@ int git_reference_peel(
|
||||
|
||||
cleanup:
|
||||
git_object_free(target);
|
||||
git_reference_free(resolved);
|
||||
|
||||
if (resolved != ref)
|
||||
git_reference_free(resolved);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "git2/refdb.h"
|
||||
#include "strmap.h"
|
||||
#include "buffer.h"
|
||||
#include "oid.h"
|
||||
|
||||
#define GIT_REFS_DIR "refs/"
|
||||
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
|
||||
@ -25,7 +26,7 @@
|
||||
|
||||
#define GIT_SYMREF "ref: "
|
||||
#define GIT_PACKEDREFS_FILE "packed-refs"
|
||||
#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled "
|
||||
#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled "
|
||||
#define GIT_PACKEDREFS_FILE_MODE 0666
|
||||
|
||||
#define GIT_HEAD_FILE "HEAD"
|
||||
@ -49,7 +50,6 @@
|
||||
|
||||
struct git_reference {
|
||||
git_refdb *db;
|
||||
|
||||
git_ref_t type;
|
||||
|
||||
union {
|
||||
@ -57,6 +57,7 @@ struct git_reference {
|
||||
char *symbolic;
|
||||
} target;
|
||||
|
||||
git_oid peel;
|
||||
char name[0];
|
||||
};
|
||||
|
||||
|
@ -25,6 +25,7 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
|
||||
assert(refspec && input);
|
||||
|
||||
memset(refspec, 0x0, sizeof(git_refspec));
|
||||
refspec->push = !is_fetch;
|
||||
|
||||
lhs = input;
|
||||
if (*lhs == '+') {
|
||||
@ -59,7 +60,7 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
|
||||
|
||||
refspec->pattern = is_glob;
|
||||
refspec->src = git__strndup(lhs, llen);
|
||||
flags = GIT_REF_FORMAT_ALLOW_ONELEVEL
|
||||
flags = GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND
|
||||
| (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0);
|
||||
|
||||
if (is_fetch) {
|
||||
@ -119,6 +120,9 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
|
||||
}
|
||||
}
|
||||
|
||||
refspec->string = git__strdup(input);
|
||||
GITERR_CHECK_ALLOC(refspec->string);
|
||||
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
@ -132,6 +136,7 @@ void git_refspec__free(git_refspec *refspec)
|
||||
|
||||
git__free(refspec->src);
|
||||
git__free(refspec->dst);
|
||||
git__free(refspec->string);
|
||||
}
|
||||
|
||||
const char *git_refspec_src(const git_refspec *refspec)
|
||||
@ -144,6 +149,11 @@ const char *git_refspec_dst(const git_refspec *refspec)
|
||||
return refspec == NULL ? NULL : refspec->dst;
|
||||
}
|
||||
|
||||
const char *git_refspec_string(const git_refspec *refspec)
|
||||
{
|
||||
return refspec == NULL ? NULL : refspec->string;
|
||||
}
|
||||
|
||||
int git_refspec_force(const git_refspec *refspec)
|
||||
{
|
||||
assert(refspec);
|
||||
@ -264,3 +274,10 @@ int git_refspec_is_wildcard(const git_refspec *spec)
|
||||
|
||||
return (spec->src[strlen(spec->src) - 1] == '*');
|
||||
}
|
||||
|
||||
git_direction git_refspec_direction(const git_refspec *spec)
|
||||
{
|
||||
assert(spec);
|
||||
|
||||
return spec->push;
|
||||
}
|
||||
|
@ -11,11 +11,13 @@
|
||||
#include "buffer.h"
|
||||
|
||||
struct git_refspec {
|
||||
struct git_refspec *next;
|
||||
char *string;
|
||||
char *src;
|
||||
char *dst;
|
||||
unsigned int force :1,
|
||||
push : 1,
|
||||
pattern :1,
|
||||
dwim :1,
|
||||
matching :1;
|
||||
};
|
||||
|
||||
|
530
src/remote.c
530
src/remote.c
@ -8,6 +8,7 @@
|
||||
#include "git2/config.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/net.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "repository.h"
|
||||
@ -19,15 +20,26 @@
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var, bool is_fetch)
|
||||
static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
|
||||
{
|
||||
int error;
|
||||
const char *val;
|
||||
git_refspec *spec;
|
||||
|
||||
if ((error = git_config_get_string(&val, cfg, var)) < 0)
|
||||
return error;
|
||||
spec = git__calloc(1, sizeof(git_refspec));
|
||||
GITERR_CHECK_ALLOC(spec);
|
||||
|
||||
return git_refspec__parse(refspec, val, is_fetch);
|
||||
if (git_refspec__parse(spec, string, is_fetch) < 0) {
|
||||
git__free(spec);
|
||||
return -1;
|
||||
}
|
||||
|
||||
spec->push = !is_fetch;
|
||||
if (git_vector_insert(&remote->refspecs, spec) < 0) {
|
||||
git_refspec__free(spec);
|
||||
git__free(spec);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int download_tags_value(git_remote *remote, git_config *cfg)
|
||||
@ -99,7 +111,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
|
||||
}
|
||||
|
||||
if (fetch != NULL) {
|
||||
if (git_refspec__parse(&remote->fetch, fetch, true) < 0)
|
||||
if (add_refspec(remote, fetch, true) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
@ -186,6 +198,18 @@ int git_remote_create_inmemory(git_remote **out, git_repository *repo, const cha
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct refspec_cb_data {
|
||||
git_remote *remote;
|
||||
int fetch;
|
||||
};
|
||||
|
||||
static int refspec_cb(const git_config_entry *entry, void *payload)
|
||||
{
|
||||
const struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
|
||||
|
||||
return add_refspec(data->remote, entry->value, data->fetch);
|
||||
}
|
||||
|
||||
int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
{
|
||||
git_remote *remote;
|
||||
@ -193,6 +217,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
const char *val;
|
||||
int error = 0;
|
||||
git_config *config;
|
||||
struct refspec_cb_data data;
|
||||
|
||||
|
||||
assert(out && repo && name);
|
||||
|
||||
@ -211,7 +237,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
remote->name = git__strdup(name);
|
||||
GITERR_CHECK_ALLOC(remote->name);
|
||||
|
||||
if (git_vector_init(&remote->refs, 32, NULL) < 0) {
|
||||
if ((git_vector_init(&remote->refs, 32, NULL) < 0) ||
|
||||
(git_vector_init(&remote->refspecs, 2, NULL))) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -262,7 +289,9 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf), true);
|
||||
data.remote = remote;
|
||||
data.fetch = true;
|
||||
error = git_config_get_multivar(config, git_buf_cstr(&buf), NULL, refspec_cb, &data);
|
||||
if (error == GIT_ENOTFOUND)
|
||||
error = 0;
|
||||
|
||||
@ -277,7 +306,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf), false);
|
||||
data.fetch = false;
|
||||
error = git_config_get_multivar(config, git_buf_cstr(&buf), NULL, refspec_cb, &data);
|
||||
if (error == GIT_ENOTFOUND)
|
||||
error = 0;
|
||||
|
||||
@ -300,36 +330,44 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int update_config_refspec(
|
||||
git_config *config,
|
||||
const char *remote_name,
|
||||
const git_refspec *refspec,
|
||||
int git_direction)
|
||||
static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
|
||||
{
|
||||
git_buf name = GIT_BUF_INIT, value = GIT_BUF_INIT;
|
||||
git_buf name = GIT_BUF_INIT;
|
||||
int push;
|
||||
const char *dir;
|
||||
size_t i;
|
||||
int error = -1;
|
||||
|
||||
if (refspec->src == NULL || refspec->dst == NULL)
|
||||
return 0;
|
||||
push = direction == GIT_DIRECTION_PUSH;
|
||||
dir = push ? "push" : "fetch";
|
||||
|
||||
if (git_buf_printf(
|
||||
&name,
|
||||
"remote.%s.%s",
|
||||
remote_name,
|
||||
git_direction == GIT_DIRECTION_FETCH ? "fetch" : "push") < 0)
|
||||
if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
|
||||
return -1;
|
||||
|
||||
/* Clear out the existing config */
|
||||
do {
|
||||
error = git_config_delete_entry(config, git_buf_cstr(&name));
|
||||
} while (!error);
|
||||
|
||||
if (error != GIT_ENOTFOUND)
|
||||
return error;
|
||||
|
||||
for (i = 0; i < remote->refspecs.length; i++) {
|
||||
git_refspec *spec = git_vector_get(&remote->refspecs, i);
|
||||
|
||||
if (spec->push != push)
|
||||
continue;
|
||||
|
||||
if ((error = git_config_set_multivar(config, git_buf_cstr(&name), "", spec->string)) < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (git_refspec__serialize(&value, refspec) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_config_set_string(
|
||||
config,
|
||||
git_buf_cstr(&name),
|
||||
git_buf_cstr(&value));
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&name);
|
||||
git_buf_free(&value);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -383,19 +421,11 @@ int git_remote_save(const git_remote *remote)
|
||||
}
|
||||
}
|
||||
|
||||
if (update_config_refspec(
|
||||
config,
|
||||
remote->name,
|
||||
&remote->fetch,
|
||||
GIT_DIRECTION_FETCH) < 0)
|
||||
goto on_error;
|
||||
if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (update_config_refspec(
|
||||
config,
|
||||
remote->name,
|
||||
&remote->push,
|
||||
GIT_DIRECTION_PUSH) < 0)
|
||||
goto on_error;
|
||||
if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0)
|
||||
goto on_error;
|
||||
|
||||
/*
|
||||
* What action to take depends on the old and new values. This
|
||||
@ -482,49 +512,6 @@ int git_remote_set_pushurl(git_remote *remote, const char* url)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_remote_set_fetchspec(git_remote *remote, const char *spec)
|
||||
{
|
||||
git_refspec refspec;
|
||||
|
||||
assert(remote && spec);
|
||||
|
||||
if (git_refspec__parse(&refspec, spec, true) < 0)
|
||||
return -1;
|
||||
|
||||
git_refspec__free(&remote->fetch);
|
||||
memcpy(&remote->fetch, &refspec, sizeof(git_refspec));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const git_refspec *git_remote_fetchspec(const git_remote *remote)
|
||||
{
|
||||
assert(remote);
|
||||
return &remote->fetch;
|
||||
}
|
||||
|
||||
int git_remote_set_pushspec(git_remote *remote, const char *spec)
|
||||
{
|
||||
git_refspec refspec;
|
||||
|
||||
assert(remote && spec);
|
||||
|
||||
if (git_refspec__parse(&refspec, spec, false) < 0)
|
||||
return -1;
|
||||
|
||||
git_refspec__free(&remote->push);
|
||||
remote->push.src = refspec.src;
|
||||
remote->push.dst = refspec.dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const git_refspec *git_remote_pushspec(const git_remote *remote)
|
||||
{
|
||||
assert(remote);
|
||||
return &remote->push;
|
||||
}
|
||||
|
||||
const char* git_remote__urlfordirection(git_remote *remote, int direction)
|
||||
{
|
||||
assert(remote);
|
||||
@ -646,28 +633,105 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int store_refs(git_remote_head *head, void *payload)
|
||||
{
|
||||
git_vector *refs = (git_vector *)payload;
|
||||
|
||||
return git_vector_insert(refs, head);
|
||||
}
|
||||
|
||||
static int dwim_refspecs(git_vector *refspecs, git_vector *refs)
|
||||
{
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
git_refspec *spec;
|
||||
size_t i, j, pos;
|
||||
git_remote_head key;
|
||||
|
||||
const char* formatters[] = {
|
||||
GIT_REFS_DIR "%s",
|
||||
GIT_REFS_TAGS_DIR "%s",
|
||||
GIT_REFS_HEADS_DIR "%s",
|
||||
NULL
|
||||
};
|
||||
|
||||
git_vector_foreach(refspecs, i, spec) {
|
||||
if (spec->dwim)
|
||||
continue;
|
||||
|
||||
/* shorthand on the lhs */
|
||||
if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
|
||||
for (j = 0; formatters[j]; j++) {
|
||||
git_buf_clear(&buf);
|
||||
if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
|
||||
return -1;
|
||||
|
||||
key.name = (char *) git_buf_cstr(&buf);
|
||||
if (!git_vector_search(&pos, refs, &key)) {
|
||||
/* we found something to match the shorthand, set src to that */
|
||||
git__free(spec->src);
|
||||
spec->src = git_buf_detach(&buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
|
||||
/* if it starts with "remotes" then we just prepend "refs/" */
|
||||
if (!git__prefixcmp(spec->dst, "remotes/")) {
|
||||
git_buf_puts(&buf, GIT_REFS_DIR);
|
||||
} else {
|
||||
git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
|
||||
}
|
||||
|
||||
if (git_buf_puts(&buf, spec->dst) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(spec->dst);
|
||||
spec->dst = git_buf_detach(&buf);
|
||||
}
|
||||
|
||||
spec->dwim = 1;
|
||||
}
|
||||
|
||||
git_buf_free(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_head_cmp(const void *_a, const void *_b)
|
||||
{
|
||||
const git_remote_head *a = (git_remote_head *) _a;
|
||||
const git_remote_head *b = (git_remote_head *) _b;
|
||||
|
||||
return git__strcmp_cb(a->name, b->name);
|
||||
}
|
||||
|
||||
int git_remote_download(
|
||||
git_remote *remote,
|
||||
git_transfer_progress_callback progress_cb,
|
||||
void *progress_payload)
|
||||
{
|
||||
int error;
|
||||
git_vector refs;
|
||||
|
||||
assert(remote);
|
||||
|
||||
if (git_vector_init(&refs, 16, remote_head_cmp) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_remote_ls(remote, store_refs, &refs) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = dwim_refspecs(&remote->refspecs, &refs);
|
||||
git_vector_free(&refs);
|
||||
if (error < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = git_fetch_negotiate(remote)) < 0)
|
||||
return error;
|
||||
|
||||
return git_fetch_download_pack(remote, progress_cb, progress_payload);
|
||||
}
|
||||
|
||||
static int update_tips_callback(git_remote_head *head, void *payload)
|
||||
{
|
||||
git_vector *refs = (git_vector *)payload;
|
||||
|
||||
return git_vector_insert(refs, head);
|
||||
}
|
||||
|
||||
static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
|
||||
{
|
||||
unsigned int i;
|
||||
@ -687,21 +751,21 @@ static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *upda
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_vector *update_heads, git_reference *ref)
|
||||
static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref)
|
||||
{
|
||||
git_reference *resolved_ref = NULL;
|
||||
git_reference *tracking_ref = NULL;
|
||||
git_buf remote_name = GIT_BUF_INIT;
|
||||
int error = 0;
|
||||
|
||||
assert(out && remote && ref);
|
||||
assert(out && spec && ref);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 ||
|
||||
(!git_reference_is_branch(resolved_ref)) ||
|
||||
(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
|
||||
(error = git_refspec_transform_l(&remote_name, &remote->fetch, git_reference_name(tracking_ref))) < 0) {
|
||||
(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
|
||||
/* Not an error if HEAD is orphaned or no tracking branch */
|
||||
if (error == GIT_ENOTFOUND)
|
||||
error = 0;
|
||||
@ -718,9 +782,8 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_heads)
|
||||
static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
|
||||
{
|
||||
struct git_refspec *spec;
|
||||
git_reference *head_ref = NULL;
|
||||
git_fetchhead_ref *fetchhead_ref;
|
||||
git_remote_head *remote_ref, *merge_remote_ref;
|
||||
@ -735,8 +798,6 @@ static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_hea
|
||||
if (update_heads->length == 0)
|
||||
return 0;
|
||||
|
||||
spec = &remote->fetch;
|
||||
|
||||
if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
|
||||
return -1;
|
||||
|
||||
@ -746,7 +807,7 @@ static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_hea
|
||||
/* Determine what to merge: if refspec was a wildcard, just use HEAD */
|
||||
if (git_refspec_is_wildcard(spec)) {
|
||||
if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
|
||||
(error = remote_head_for_ref(&merge_remote_ref, remote, update_heads, head_ref)) < 0)
|
||||
(error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0)
|
||||
goto cleanup;
|
||||
} else {
|
||||
/* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
|
||||
@ -786,7 +847,7 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_remote_update_tips(git_remote *remote)
|
||||
static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs)
|
||||
{
|
||||
int error = 0, autotag;
|
||||
unsigned int i = 0;
|
||||
@ -795,14 +856,11 @@ int git_remote_update_tips(git_remote *remote)
|
||||
git_odb *odb;
|
||||
git_remote_head *head;
|
||||
git_reference *ref;
|
||||
struct git_refspec *spec;
|
||||
git_refspec tagspec;
|
||||
git_vector refs, update_heads;
|
||||
git_vector update_heads;
|
||||
|
||||
assert(remote);
|
||||
|
||||
spec = &remote->fetch;
|
||||
|
||||
if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
|
||||
return -1;
|
||||
|
||||
@ -810,16 +868,12 @@ int git_remote_update_tips(git_remote *remote)
|
||||
return -1;
|
||||
|
||||
/* Make a copy of the transport's refs */
|
||||
if (git_vector_init(&refs, 16, NULL) < 0 ||
|
||||
git_vector_init(&update_heads, 16, NULL) < 0)
|
||||
if (git_vector_init(&update_heads, 16, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_remote_ls(remote, update_tips_callback, &refs) < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Let's go find HEAD, if it exists. Check only the first ref in the vector. */
|
||||
if (refs.length > 0) {
|
||||
head = (git_remote_head *)refs.contents[0];
|
||||
if (refs->length > 0) {
|
||||
head = git_vector_get(refs, 0);
|
||||
|
||||
if (!strcmp(head->name, GIT_HEAD_FILE)) {
|
||||
if (git_reference_create(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
|
||||
@ -830,15 +884,15 @@ int git_remote_update_tips(git_remote *remote)
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < refs.length; ++i) {
|
||||
head = (git_remote_head *)refs.contents[i];
|
||||
for (; i < refs->length; ++i) {
|
||||
head = git_vector_get(refs, i);
|
||||
autotag = 0;
|
||||
|
||||
/* Ignore malformed ref names (which also saves us from tag^{} */
|
||||
if (!git_reference_is_valid_name(head->name))
|
||||
continue;
|
||||
|
||||
if (git_refspec_src_matches(spec, head->name)) {
|
||||
if (git_refspec_src_matches(spec, head->name) && spec->dst) {
|
||||
if (git_refspec_transform_r(&refname, spec, head->name) < 0)
|
||||
goto on_error;
|
||||
} else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
|
||||
@ -869,7 +923,7 @@ int git_remote_update_tips(git_remote *remote)
|
||||
if (error == GIT_ENOTFOUND)
|
||||
memset(&old, 0, GIT_OID_RAWSZ);
|
||||
|
||||
if (!git_oid_cmp(&old, &head->oid))
|
||||
if (!git_oid__cmp(&old, &head->oid))
|
||||
continue;
|
||||
|
||||
/* In autotag mode, don't overwrite any locally-existing tags */
|
||||
@ -886,17 +940,15 @@ int git_remote_update_tips(git_remote *remote)
|
||||
}
|
||||
|
||||
if (git_remote_update_fetchhead(remote) &&
|
||||
(error = git_remote_write_fetchhead(remote, &update_heads)) < 0)
|
||||
(error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_vector_free(&refs);
|
||||
git_vector_free(&update_heads);
|
||||
git_refspec__free(&tagspec);
|
||||
git_buf_free(&refname);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_vector_free(&refs);
|
||||
git_vector_free(&update_heads);
|
||||
git_refspec__free(&tagspec);
|
||||
git_buf_free(&refname);
|
||||
@ -904,6 +956,34 @@ on_error:
|
||||
|
||||
}
|
||||
|
||||
int git_remote_update_tips(git_remote *remote)
|
||||
{
|
||||
git_refspec *spec;
|
||||
git_vector refs;
|
||||
size_t i;
|
||||
|
||||
if (git_vector_init(&refs, 16, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_remote_ls(remote, store_refs, &refs) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
if (spec->push)
|
||||
continue;
|
||||
|
||||
if (update_tips_for_spec(remote, spec, &refs) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
git_vector_free(&refs);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_vector_free(&refs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_remote_connected(git_remote *remote)
|
||||
{
|
||||
assert(remote);
|
||||
@ -933,6 +1013,9 @@ void git_remote_disconnect(git_remote *remote)
|
||||
|
||||
void git_remote_free(git_remote *remote)
|
||||
{
|
||||
git_refspec *spec;
|
||||
size_t i;
|
||||
|
||||
if (remote == NULL)
|
||||
return;
|
||||
|
||||
@ -945,8 +1028,12 @@ void git_remote_free(git_remote *remote)
|
||||
|
||||
git_vector_free(&remote->refs);
|
||||
|
||||
git_refspec__free(&remote->fetch);
|
||||
git_refspec__free(&remote->push);
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
git_refspec__free(spec);
|
||||
git__free(spec);
|
||||
}
|
||||
git_vector_free(&remote->refspecs);
|
||||
|
||||
git__free(remote->url);
|
||||
git__free(remote->pushurl);
|
||||
git__free(remote->name);
|
||||
@ -1237,58 +1324,58 @@ static int rename_fetch_refspecs(
|
||||
void *payload)
|
||||
{
|
||||
git_config *config;
|
||||
const git_refspec *fetch_refspec;
|
||||
git_buf dst_prefix = GIT_BUF_INIT, serialized = GIT_BUF_INIT;
|
||||
const char* pos;
|
||||
git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
|
||||
const git_refspec *spec;
|
||||
size_t i;
|
||||
int error = -1;
|
||||
|
||||
fetch_refspec = git_remote_fetchspec(remote);
|
||||
|
||||
/* Is there a refspec to deal with? */
|
||||
if (fetch_refspec->src == NULL &&
|
||||
fetch_refspec->dst == NULL)
|
||||
return 0;
|
||||
|
||||
if (git_refspec__serialize(&serialized, fetch_refspec) < 0)
|
||||
if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Is it an in-memory remote? */
|
||||
if (!remote->name) {
|
||||
error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0;
|
||||
goto cleanup;
|
||||
}
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
if (spec->push)
|
||||
continue;
|
||||
|
||||
if (git_buf_printf(&dst_prefix, ":refs/remotes/%s/", remote->name) < 0)
|
||||
goto cleanup;
|
||||
/* Every refspec is a problem refspec for an in-memory remote */
|
||||
if (!remote->name) {
|
||||
if (callback(spec->string, payload) < 0) {
|
||||
error = GIT_EUSER;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
pos = strstr(git_buf_cstr(&serialized), git_buf_cstr(&dst_prefix));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Does the dst part of the refspec follow the extected standard format? */
|
||||
if (!pos) {
|
||||
error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0;
|
||||
goto cleanup;
|
||||
}
|
||||
/* Does the dst part of the refspec follow the extected standard format? */
|
||||
if (strcmp(git_buf_cstr(&base), spec->string)) {
|
||||
if (callback(spec->string, payload) < 0) {
|
||||
error = GIT_EUSER;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_buf_splice(
|
||||
&serialized,
|
||||
pos - git_buf_cstr(&serialized) + strlen(":refs/remotes/"),
|
||||
strlen(remote->name), new_name,
|
||||
strlen(new_name)) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we do want to move it to the new section */
|
||||
if (git_buf_printf(&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_refspec__free(&remote->fetch);
|
||||
if (git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_refspec__parse(&remote->fetch, git_buf_cstr(&serialized), true) < 0)
|
||||
goto cleanup;
|
||||
if (git_repository_config__weakptr(&config, remote->repo) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_repository_config__weakptr(&config, remote->repo) < 0)
|
||||
goto cleanup;
|
||||
if (git_config_set_string(config, git_buf_cstr(&var), git_buf_cstr(&val)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = update_config_refspec(config, new_name, &remote->fetch, GIT_DIRECTION_FETCH);
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&serialized);
|
||||
git_buf_free(&dst_prefix);
|
||||
git_buf_free(&base);
|
||||
git_buf_free(&var);
|
||||
git_buf_free(&val);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1389,3 +1476,128 @@ int git_remote_is_valid_name(
|
||||
giterr_clear();
|
||||
return error == 0;
|
||||
}
|
||||
|
||||
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
|
||||
{
|
||||
git_refspec *spec;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
if (spec->push)
|
||||
continue;
|
||||
|
||||
if (git_refspec_src_matches(spec, refname))
|
||||
return spec;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
|
||||
{
|
||||
git_refspec *spec;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
if (spec->push)
|
||||
continue;
|
||||
|
||||
if (git_refspec_dst_matches(spec, refname))
|
||||
return spec;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void git_remote_clear_refspecs(git_remote *remote)
|
||||
{
|
||||
git_refspec *spec;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
git_refspec__free(spec);
|
||||
git__free(spec);
|
||||
}
|
||||
git_vector_clear(&remote->refspecs);
|
||||
}
|
||||
|
||||
int git_remote_add_fetch(git_remote *remote, const char *refspec)
|
||||
{
|
||||
return add_refspec(remote, refspec, true);
|
||||
}
|
||||
|
||||
int git_remote_add_push(git_remote *remote, const char *refspec)
|
||||
{
|
||||
return add_refspec(remote, refspec, false);
|
||||
}
|
||||
|
||||
static int copy_refspecs(git_strarray *array, git_remote *remote, int push)
|
||||
{
|
||||
size_t i;
|
||||
git_vector refspecs;
|
||||
git_refspec *spec;
|
||||
char *dup;
|
||||
|
||||
if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
git_vector_foreach(&remote->refspecs, i, spec) {
|
||||
if (spec->push != push)
|
||||
continue;
|
||||
|
||||
if ((dup = git__strdup(spec->string)) == NULL)
|
||||
goto on_error;
|
||||
|
||||
if (git_vector_insert(&refspecs, dup) < 0) {
|
||||
git__free(dup);
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
array->strings = (char **)refspecs.contents;
|
||||
array->count = refspecs.length;
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_vector_foreach(&refspecs, i, dup)
|
||||
git__free(dup);
|
||||
git_vector_free(&refspecs);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote)
|
||||
{
|
||||
return copy_refspecs(array, remote, false);
|
||||
}
|
||||
|
||||
int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote)
|
||||
{
|
||||
return copy_refspecs(array, remote, true);
|
||||
}
|
||||
|
||||
size_t git_remote_refspec_count(git_remote *remote)
|
||||
{
|
||||
return remote->refspecs.length;
|
||||
}
|
||||
|
||||
const git_refspec *git_remote_get_refspec(git_remote *remote, size_t n)
|
||||
{
|
||||
return git_vector_get(&remote->refspecs, n);
|
||||
}
|
||||
|
||||
int git_remote_remove_refspec(git_remote *remote, size_t n)
|
||||
{
|
||||
git_refspec *spec;
|
||||
|
||||
assert(remote);
|
||||
|
||||
spec = git_vector_get(&remote->refspecs, n);
|
||||
if (spec) {
|
||||
git_refspec__free(spec);
|
||||
git__free(spec);
|
||||
}
|
||||
|
||||
return git_vector_remove(&remote->refspecs, n);
|
||||
}
|
||||
|
@ -20,8 +20,7 @@ struct git_remote {
|
||||
char *url;
|
||||
char *pushurl;
|
||||
git_vector refs;
|
||||
struct git_refspec fetch;
|
||||
struct git_refspec push;
|
||||
git_vector refspecs;
|
||||
git_cred_acquire_cb cred_acquire_cb;
|
||||
void *cred_acquire_payload;
|
||||
git_transport *transport;
|
||||
@ -37,4 +36,7 @@ struct git_remote {
|
||||
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
|
||||
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
|
||||
|
||||
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
|
||||
git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);
|
||||
|
||||
#endif
|
||||
|
289
src/repository.c
289
src/repository.c
@ -9,6 +9,7 @@
|
||||
|
||||
#include "git2/object.h"
|
||||
#include "git2/refdb.h"
|
||||
#include "git2/sys/repository.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "repository.h"
|
||||
@ -31,42 +32,71 @@
|
||||
|
||||
#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
|
||||
|
||||
static void drop_odb(git_repository *repo)
|
||||
static void set_odb(git_repository *repo, git_odb *odb)
|
||||
{
|
||||
if (repo->_odb != NULL) {
|
||||
GIT_REFCOUNT_OWN(repo->_odb, NULL);
|
||||
git_odb_free(repo->_odb);
|
||||
repo->_odb = NULL;
|
||||
if (odb) {
|
||||
GIT_REFCOUNT_OWN(odb, repo);
|
||||
GIT_REFCOUNT_INC(odb);
|
||||
}
|
||||
|
||||
if ((odb = git__swap(repo->_odb, odb)) != NULL) {
|
||||
GIT_REFCOUNT_OWN(odb, NULL);
|
||||
git_odb_free(odb);
|
||||
}
|
||||
}
|
||||
|
||||
static void drop_refdb(git_repository *repo)
|
||||
static void set_refdb(git_repository *repo, git_refdb *refdb)
|
||||
{
|
||||
if (repo->_refdb != NULL) {
|
||||
GIT_REFCOUNT_OWN(repo->_refdb, NULL);
|
||||
git_refdb_free(repo->_refdb);
|
||||
repo->_refdb = NULL;
|
||||
if (refdb) {
|
||||
GIT_REFCOUNT_OWN(refdb, repo);
|
||||
GIT_REFCOUNT_INC(refdb);
|
||||
}
|
||||
|
||||
if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) {
|
||||
GIT_REFCOUNT_OWN(refdb, NULL);
|
||||
git_refdb_free(refdb);
|
||||
}
|
||||
}
|
||||
|
||||
static void drop_config(git_repository *repo)
|
||||
static void set_config(git_repository *repo, git_config *config)
|
||||
{
|
||||
if (repo->_config != NULL) {
|
||||
GIT_REFCOUNT_OWN(repo->_config, NULL);
|
||||
git_config_free(repo->_config);
|
||||
repo->_config = NULL;
|
||||
if (config) {
|
||||
GIT_REFCOUNT_OWN(config, repo);
|
||||
GIT_REFCOUNT_INC(config);
|
||||
}
|
||||
|
||||
if ((config = git__swap(repo->_config, config)) != NULL) {
|
||||
GIT_REFCOUNT_OWN(config, NULL);
|
||||
git_config_free(config);
|
||||
}
|
||||
|
||||
git_repository__cvar_cache_clear(repo);
|
||||
}
|
||||
|
||||
static void drop_index(git_repository *repo)
|
||||
static void set_index(git_repository *repo, git_index *index)
|
||||
{
|
||||
if (repo->_index != NULL) {
|
||||
GIT_REFCOUNT_OWN(repo->_index, NULL);
|
||||
git_index_free(repo->_index);
|
||||
repo->_index = NULL;
|
||||
if (index) {
|
||||
GIT_REFCOUNT_OWN(index, repo);
|
||||
GIT_REFCOUNT_INC(index);
|
||||
}
|
||||
|
||||
if ((index = git__swap(repo->_index, index)) != NULL) {
|
||||
GIT_REFCOUNT_OWN(index, NULL);
|
||||
git_index_free(index);
|
||||
}
|
||||
}
|
||||
|
||||
void git_repository__cleanup(git_repository *repo)
|
||||
{
|
||||
assert(repo);
|
||||
|
||||
git_cache_clear(&repo->objects);
|
||||
git_attr_cache_flush(repo);
|
||||
|
||||
set_config(repo, NULL);
|
||||
set_index(repo, NULL);
|
||||
set_odb(repo, NULL);
|
||||
set_refdb(repo, NULL);
|
||||
}
|
||||
|
||||
void git_repository_free(git_repository *repo)
|
||||
@ -74,17 +104,14 @@ void git_repository_free(git_repository *repo)
|
||||
if (repo == NULL)
|
||||
return;
|
||||
|
||||
git_repository__cleanup(repo);
|
||||
|
||||
git_cache_free(&repo->objects);
|
||||
git_attr_cache_flush(repo);
|
||||
git_submodule_config_free(repo);
|
||||
|
||||
git__free(repo->path_repository);
|
||||
git__free(repo->workdir);
|
||||
|
||||
drop_config(repo);
|
||||
drop_index(repo);
|
||||
drop_odb(repo);
|
||||
drop_refdb(repo);
|
||||
git__free(repo->namespace);
|
||||
|
||||
git__free(repo);
|
||||
}
|
||||
@ -118,7 +145,7 @@ static git_repository *repository_alloc(void)
|
||||
|
||||
memset(repo, 0x0, sizeof(git_repository));
|
||||
|
||||
if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
|
||||
if (git_cache_init(&repo->objects) < 0) {
|
||||
git__free(repo);
|
||||
return NULL;
|
||||
}
|
||||
@ -129,6 +156,12 @@ static git_repository *repository_alloc(void)
|
||||
return repo;
|
||||
}
|
||||
|
||||
int git_repository_new(git_repository **out)
|
||||
{
|
||||
*out = repository_alloc();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_config_data(git_repository *repo)
|
||||
{
|
||||
int is_bare;
|
||||
@ -368,6 +401,37 @@ static int find_repo(
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_open_bare(
|
||||
git_repository **repo_ptr,
|
||||
const char *bare_path)
|
||||
{
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
git_repository *repo = NULL;
|
||||
|
||||
if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0)
|
||||
return error;
|
||||
|
||||
if (!valid_repository_path(&path)) {
|
||||
git_buf_free(&path);
|
||||
giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
repo = repository_alloc();
|
||||
GITERR_CHECK_ALLOC(repo);
|
||||
|
||||
repo->path_repository = git_buf_detach(&path);
|
||||
GITERR_CHECK_ALLOC(repo->path_repository);
|
||||
|
||||
/* of course we're bare! */
|
||||
repo->is_bare = 1;
|
||||
repo->workdir = NULL;
|
||||
|
||||
*repo_ptr = repo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_repository_open_ext(
|
||||
git_repository **repo_ptr,
|
||||
const char *start_path,
|
||||
@ -511,39 +575,47 @@ on_error:
|
||||
return error;
|
||||
}
|
||||
|
||||
static const char *path_unless_empty(git_buf *buf)
|
||||
{
|
||||
return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL;
|
||||
}
|
||||
|
||||
int git_repository_config__weakptr(git_config **out, git_repository *repo)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (repo->_config == NULL) {
|
||||
git_buf global_buf = GIT_BUF_INIT, xdg_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
|
||||
int res;
|
||||
git_buf global_buf = GIT_BUF_INIT;
|
||||
git_buf xdg_buf = GIT_BUF_INIT;
|
||||
git_buf system_buf = GIT_BUF_INIT;
|
||||
git_config *config;
|
||||
|
||||
const char *global_config_path = NULL;
|
||||
const char *xdg_config_path = NULL;
|
||||
const char *system_config_path = NULL;
|
||||
git_config_find_global_r(&global_buf);
|
||||
git_config_find_xdg_r(&xdg_buf);
|
||||
git_config_find_system_r(&system_buf);
|
||||
|
||||
if (git_config_find_global_r(&global_buf) == 0)
|
||||
global_config_path = global_buf.ptr;
|
||||
error = load_config(
|
||||
&config, repo,
|
||||
path_unless_empty(&global_buf),
|
||||
path_unless_empty(&xdg_buf),
|
||||
path_unless_empty(&system_buf));
|
||||
if (!error) {
|
||||
GIT_REFCOUNT_OWN(config, repo);
|
||||
|
||||
if (git_config_find_xdg_r(&xdg_buf) == 0)
|
||||
xdg_config_path = xdg_buf.ptr;
|
||||
|
||||
if (git_config_find_system_r(&system_buf) == 0)
|
||||
system_config_path = system_buf.ptr;
|
||||
|
||||
res = load_config(&repo->_config, repo, global_config_path, xdg_config_path, system_config_path);
|
||||
config = git__compare_and_swap(&repo->_config, NULL, config);
|
||||
if (config != NULL) {
|
||||
GIT_REFCOUNT_OWN(config, NULL);
|
||||
git_config_free(config);
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_free(&global_buf);
|
||||
git_buf_free(&xdg_buf);
|
||||
git_buf_free(&system_buf);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
GIT_REFCOUNT_OWN(repo->_config, repo);
|
||||
}
|
||||
|
||||
*out = repo->_config;
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_config(git_config **out, git_repository *repo)
|
||||
@ -558,36 +630,37 @@ int git_repository_config(git_config **out, git_repository *repo)
|
||||
void git_repository_set_config(git_repository *repo, git_config *config)
|
||||
{
|
||||
assert(repo && config);
|
||||
|
||||
drop_config(repo);
|
||||
|
||||
repo->_config = config;
|
||||
GIT_REFCOUNT_OWN(repo->_config, repo);
|
||||
GIT_REFCOUNT_INC(repo->_config);
|
||||
set_config(repo, config);
|
||||
}
|
||||
|
||||
int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
assert(repo && out);
|
||||
|
||||
if (repo->_odb == NULL) {
|
||||
git_buf odb_path = GIT_BUF_INIT;
|
||||
int res;
|
||||
git_odb *odb;
|
||||
|
||||
if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
|
||||
return -1;
|
||||
git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR);
|
||||
|
||||
res = git_odb_open(&repo->_odb, odb_path.ptr);
|
||||
git_buf_free(&odb_path); /* done with path */
|
||||
error = git_odb_open(&odb, odb_path.ptr);
|
||||
if (!error) {
|
||||
GIT_REFCOUNT_OWN(odb, repo);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
odb = git__compare_and_swap(&repo->_odb, NULL, odb);
|
||||
if (odb != NULL) {
|
||||
GIT_REFCOUNT_OWN(odb, NULL);
|
||||
git_odb_free(odb);
|
||||
}
|
||||
}
|
||||
|
||||
GIT_REFCOUNT_OWN(repo->_odb, repo);
|
||||
git_buf_free(&odb_path);
|
||||
}
|
||||
|
||||
*out = repo->_odb;
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_odb(git_odb **out, git_repository *repo)
|
||||
@ -602,31 +675,32 @@ int git_repository_odb(git_odb **out, git_repository *repo)
|
||||
void git_repository_set_odb(git_repository *repo, git_odb *odb)
|
||||
{
|
||||
assert(repo && odb);
|
||||
|
||||
drop_odb(repo);
|
||||
|
||||
repo->_odb = odb;
|
||||
GIT_REFCOUNT_OWN(repo->_odb, repo);
|
||||
GIT_REFCOUNT_INC(odb);
|
||||
set_odb(repo, odb);
|
||||
}
|
||||
|
||||
int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
assert(out && repo);
|
||||
|
||||
if (repo->_refdb == NULL) {
|
||||
int res;
|
||||
git_refdb *refdb;
|
||||
|
||||
res = git_refdb_open(&repo->_refdb, repo);
|
||||
error = git_refdb_open(&refdb, repo);
|
||||
if (!error) {
|
||||
GIT_REFCOUNT_OWN(refdb, repo);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
GIT_REFCOUNT_OWN(repo->_refdb, repo);
|
||||
refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb);
|
||||
if (refdb != NULL) {
|
||||
GIT_REFCOUNT_OWN(refdb, NULL);
|
||||
git_refdb_free(refdb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out = repo->_refdb;
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_refdb(git_refdb **out, git_repository *repo)
|
||||
@ -640,40 +714,40 @@ int git_repository_refdb(git_refdb **out, git_repository *repo)
|
||||
|
||||
void git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
|
||||
{
|
||||
assert (repo && refdb);
|
||||
|
||||
drop_refdb(repo);
|
||||
|
||||
repo->_refdb = refdb;
|
||||
GIT_REFCOUNT_OWN(repo->_refdb, repo);
|
||||
GIT_REFCOUNT_INC(refdb);
|
||||
assert(repo && refdb);
|
||||
set_refdb(repo, refdb);
|
||||
}
|
||||
|
||||
int git_repository_index__weakptr(git_index **out, git_repository *repo)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
assert(out && repo);
|
||||
|
||||
if (repo->_index == NULL) {
|
||||
int res;
|
||||
git_buf index_path = GIT_BUF_INIT;
|
||||
git_index *index;
|
||||
|
||||
if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
|
||||
return -1;
|
||||
git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE);
|
||||
|
||||
res = git_index_open(&repo->_index, index_path.ptr);
|
||||
git_buf_free(&index_path); /* done with path */
|
||||
error = git_index_open(&index, index_path.ptr);
|
||||
if (!error) {
|
||||
GIT_REFCOUNT_OWN(index, repo);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
index = git__compare_and_swap(&repo->_index, NULL, index);
|
||||
if (index != NULL) {
|
||||
GIT_REFCOUNT_OWN(index, NULL);
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
GIT_REFCOUNT_OWN(repo->_index, repo);
|
||||
error = git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER);
|
||||
}
|
||||
|
||||
if (git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER) < 0)
|
||||
return -1;
|
||||
git_buf_free(&index_path);
|
||||
}
|
||||
|
||||
*out = repo->_index;
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_index(git_index **out, git_repository *repo)
|
||||
@ -688,12 +762,24 @@ int git_repository_index(git_index **out, git_repository *repo)
|
||||
void git_repository_set_index(git_repository *repo, git_index *index)
|
||||
{
|
||||
assert(repo && index);
|
||||
set_index(repo, index);
|
||||
}
|
||||
|
||||
drop_index(repo);
|
||||
int git_repository_set_namespace(git_repository *repo, const char *namespace)
|
||||
{
|
||||
git__free(repo->namespace);
|
||||
|
||||
repo->_index = index;
|
||||
GIT_REFCOUNT_OWN(repo->_index, repo);
|
||||
GIT_REFCOUNT_INC(index);
|
||||
if (namespace == NULL) {
|
||||
repo->namespace = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (repo->namespace = git__strdup(namespace)) ? 0 : -1;
|
||||
}
|
||||
|
||||
const char *git_repository_get_namespace(git_repository *repo)
|
||||
{
|
||||
return repo->namespace;
|
||||
}
|
||||
|
||||
static int check_repositoryformatversion(git_config *config)
|
||||
@ -1383,14 +1469,13 @@ static int at_least_one_cb(const char *refname, void *payload)
|
||||
|
||||
static int repo_contains_no_reference(git_repository *repo)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL);
|
||||
int error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL);
|
||||
|
||||
if (error == GIT_EUSER)
|
||||
return 0;
|
||||
|
||||
return error == 0 ? 1 : error;
|
||||
if (!error)
|
||||
return 1;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_repository_is_empty(git_repository *repo)
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "git2/odb.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/object.h"
|
||||
#include "git2/config.h"
|
||||
|
||||
#include "index.h"
|
||||
#include "cache.h"
|
||||
@ -31,7 +32,13 @@
|
||||
/** Cvar cache identifiers */
|
||||
typedef enum {
|
||||
GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
|
||||
GIT_CVAR_EOL, /* core.eol */
|
||||
GIT_CVAR_EOL, /* core.eol */
|
||||
GIT_CVAR_SYMLINKS, /* core.symlinks */
|
||||
GIT_CVAR_IGNORECASE, /* core.ignorecase */
|
||||
GIT_CVAR_FILEMODE, /* core.filemode */
|
||||
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
|
||||
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
|
||||
GIT_CVAR_ABBREV, /* core.abbrev */
|
||||
GIT_CVAR_CACHE_MAX
|
||||
} git_cvar_cached;
|
||||
|
||||
@ -67,7 +74,21 @@ typedef enum {
|
||||
#else
|
||||
GIT_EOL_NATIVE = GIT_EOL_LF,
|
||||
#endif
|
||||
GIT_EOL_DEFAULT = GIT_EOL_NATIVE
|
||||
GIT_EOL_DEFAULT = GIT_EOL_NATIVE,
|
||||
|
||||
/* core.symlinks: bool */
|
||||
GIT_SYMLINKS_DEFAULT = GIT_CVAR_TRUE,
|
||||
/* core.ignorecase */
|
||||
GIT_IGNORECASE_DEFAULT = GIT_CVAR_FALSE,
|
||||
/* core.filemode */
|
||||
GIT_FILEMODE_DEFAULT = GIT_CVAR_TRUE,
|
||||
/* core.ignorestat */
|
||||
GIT_IGNORESTAT_DEFAULT = GIT_CVAR_FALSE,
|
||||
/* core.trustctime */
|
||||
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
|
||||
/* core.abbrev */
|
||||
GIT_ABBREV_DEFAULT = 7,
|
||||
|
||||
} git_cvar_value;
|
||||
|
||||
/* internal repository init flags */
|
||||
@ -90,6 +111,7 @@ struct git_repository {
|
||||
|
||||
char *path_repository;
|
||||
char *workdir;
|
||||
char *namespace;
|
||||
|
||||
unsigned is_bare:1;
|
||||
unsigned int lru_counter;
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname)
|
||||
{
|
||||
int error, i;
|
||||
int error = 0, i;
|
||||
bool fallbackmode = true;
|
||||
git_reference *ref;
|
||||
git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
|
||||
|
@ -69,7 +69,7 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
|
||||
if (p->name == NULL || p->email == NULL ||
|
||||
p->name[0] == '\0' || p->email[0] == '\0') {
|
||||
git_signature_free(p);
|
||||
return -1;
|
||||
return signature_error("Empty name or email");
|
||||
}
|
||||
|
||||
p->when.time = time;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "git2/config.h"
|
||||
#include "git2/sys/config.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/index.h"
|
||||
|
56
src/tag.c
56
src/tag.c
@ -13,20 +13,17 @@
|
||||
#include "git2/object.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/signature.h"
|
||||
#include "git2/odb_backend.h"
|
||||
|
||||
void git_tag__free(git_tag *tag)
|
||||
void git_tag__free(void *_tag)
|
||||
{
|
||||
git_tag *tag = _tag;
|
||||
git_signature_free(tag->tagger);
|
||||
git__free(tag->message);
|
||||
git__free(tag->tag_name);
|
||||
git__free(tag);
|
||||
}
|
||||
|
||||
const git_oid *git_tag_id(const git_tag *c)
|
||||
{
|
||||
return git_object_id((const git_object *)c);
|
||||
}
|
||||
|
||||
int git_tag_target(git_object **target, const git_tag *t)
|
||||
{
|
||||
assert(t);
|
||||
@ -68,7 +65,7 @@ static int tag_error(const char *str)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
|
||||
static int tag_parse(git_tag *tag, const char *buffer, const char *buffer_end)
|
||||
{
|
||||
static const char *tag_types[] = {
|
||||
NULL, "commit\n", "tree\n", "blob\n", "tag\n"
|
||||
@ -78,8 +75,6 @@ int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
|
||||
size_t text_len;
|
||||
char *search;
|
||||
|
||||
const char *buffer_end = buffer + length;
|
||||
|
||||
if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
|
||||
return tag_error("Object field invalid");
|
||||
|
||||
@ -156,6 +151,15 @@ int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_tag__parse(void *_tag, git_odb_object *odb_obj)
|
||||
{
|
||||
git_tag *tag = _tag;
|
||||
const char *buffer = git_odb_object_data(odb_obj);
|
||||
const char *buffer_end = buffer + git_odb_object_size(odb_obj);
|
||||
|
||||
return tag_parse(tag, buffer, buffer_end);
|
||||
}
|
||||
|
||||
static int retrieve_tag_reference(
|
||||
git_reference **tag_reference_out,
|
||||
git_buf *ref_name_out,
|
||||
@ -276,23 +280,23 @@ cleanup:
|
||||
}
|
||||
|
||||
int git_tag_create(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *tag_name,
|
||||
const git_object *target,
|
||||
const git_signature *tagger,
|
||||
const char *message,
|
||||
int allow_ref_overwrite)
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *tag_name,
|
||||
const git_object *target,
|
||||
const git_signature *tagger,
|
||||
const char *message,
|
||||
int allow_ref_overwrite)
|
||||
{
|
||||
return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
|
||||
}
|
||||
|
||||
int git_tag_create_lightweight(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *tag_name,
|
||||
const git_object *target,
|
||||
int allow_ref_overwrite)
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *tag_name,
|
||||
const git_object *target,
|
||||
int allow_ref_overwrite)
|
||||
{
|
||||
return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
|
||||
}
|
||||
@ -316,14 +320,14 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
|
||||
return -1;
|
||||
|
||||
/* validate the buffer */
|
||||
if (git_tag__parse_buffer(&tag, buffer, strlen(buffer)) < 0)
|
||||
if (tag_parse(&tag, buffer, buffer + strlen(buffer)) < 0)
|
||||
return -1;
|
||||
|
||||
/* validate the target */
|
||||
if (git_odb_read(&target_obj, odb, &tag.target) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (tag.type != target_obj->raw.type) {
|
||||
if (tag.type != target_obj->cached.type) {
|
||||
giterr_set(GITERR_TAG, "The type for the given target is invalid");
|
||||
goto on_error;
|
||||
}
|
||||
@ -393,12 +397,6 @@ int git_tag_delete(git_repository *repo, const char *tag_name)
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_tag__parse(git_tag *tag, git_odb_object *obj)
|
||||
{
|
||||
assert(tag);
|
||||
return git_tag__parse_buffer(tag, obj->raw.data, obj->raw.len);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_tag_foreach_cb cb;
|
||||
|
@ -22,8 +22,7 @@ struct git_tag {
|
||||
char *message;
|
||||
};
|
||||
|
||||
void git_tag__free(git_tag *tag);
|
||||
int git_tag__parse(git_tag *tag, git_odb_object *obj);
|
||||
int git_tag__parse_buffer(git_tag *tag, const char *data, size_t len);
|
||||
void git_tag__free(void *tag);
|
||||
int git_tag__parse(void *tag, git_odb_object *obj);
|
||||
|
||||
#endif
|
||||
|
@ -18,6 +18,28 @@ typedef struct {
|
||||
#endif
|
||||
} git_atomic;
|
||||
|
||||
#ifdef GIT_ARCH_64
|
||||
|
||||
typedef struct {
|
||||
#if defined(GIT_WIN32)
|
||||
__int64 val;
|
||||
#else
|
||||
int64_t val;
|
||||
#endif
|
||||
} git_atomic64;
|
||||
|
||||
typedef git_atomic64 git_atomic_ssize;
|
||||
|
||||
#define git_atomic_ssize_add git_atomic64_add
|
||||
|
||||
#else
|
||||
|
||||
typedef git_atomic git_atomic_ssize;
|
||||
|
||||
#define git_atomic_ssize_add git_atomic_add
|
||||
|
||||
#endif
|
||||
|
||||
GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
|
||||
{
|
||||
a->val = val;
|
||||
@ -57,6 +79,17 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a)
|
||||
#endif
|
||||
}
|
||||
|
||||
GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
|
||||
{
|
||||
#if defined(GIT_WIN32)
|
||||
return InterlockedExchangeAdd(&a->val, addend);
|
||||
#elif defined(__GNUC__)
|
||||
return __sync_add_and_fetch(&a->val, addend);
|
||||
#else
|
||||
# error "Unsupported architecture for atomic operations"
|
||||
#endif
|
||||
}
|
||||
|
||||
GIT_INLINE(int) git_atomic_dec(git_atomic *a)
|
||||
{
|
||||
#if defined(GIT_WIN32)
|
||||
@ -68,6 +101,35 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a)
|
||||
#endif
|
||||
}
|
||||
|
||||
GIT_INLINE(void *) git___compare_and_swap(
|
||||
volatile void **ptr, void *oldval, void *newval)
|
||||
{
|
||||
volatile void *foundval;
|
||||
#if defined(GIT_WIN32)
|
||||
foundval = InterlockedCompareExchangePointer(ptr, newval, oldval);
|
||||
#elif defined(__GNUC__)
|
||||
foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
#else
|
||||
# error "Unsupported architecture for atomic operations"
|
||||
#endif
|
||||
return (foundval == oldval) ? oldval : newval;
|
||||
}
|
||||
|
||||
#ifdef GIT_ARCH_64
|
||||
|
||||
GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
|
||||
{
|
||||
#if defined(GIT_WIN32)
|
||||
return InterlockedExchangeAdd64(&a->val, addend);
|
||||
#elif defined(__GNUC__)
|
||||
return __sync_add_and_fetch(&a->val, addend);
|
||||
#else
|
||||
# error "Unsupported architecture for atomic operations"
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define git_thread unsigned int
|
||||
@ -96,13 +158,55 @@ GIT_INLINE(int) git_atomic_inc(git_atomic *a)
|
||||
return ++a->val;
|
||||
}
|
||||
|
||||
GIT_INLINE(int) git_atomic_add(git_atomic *a, int32_t addend)
|
||||
{
|
||||
a->val += addend;
|
||||
return a->val;
|
||||
}
|
||||
|
||||
GIT_INLINE(int) git_atomic_dec(git_atomic *a)
|
||||
{
|
||||
return --a->val;
|
||||
}
|
||||
|
||||
GIT_INLINE(void *) git___compare_and_swap(
|
||||
volatile void **ptr, void *oldval, void *newval)
|
||||
{
|
||||
if (*ptr == oldval)
|
||||
*ptr = newval;
|
||||
else
|
||||
oldval = newval;
|
||||
return oldval;
|
||||
}
|
||||
|
||||
#ifdef GIT_ARCH_64
|
||||
|
||||
GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
|
||||
{
|
||||
a->val += addend;
|
||||
return a->val;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Atomically replace oldval with newval
|
||||
* @return oldval if it was replaced or newval if it was not
|
||||
*/
|
||||
#define git__compare_and_swap(P,O,N) \
|
||||
git___compare_and_swap((volatile void **)P, O, N)
|
||||
|
||||
#define git__swap(ptr, val) git__compare_and_swap(&ptr, ptr, val)
|
||||
|
||||
extern int git_online_cpus(void);
|
||||
|
||||
#if defined(GIT_THREADS) && defined(GIT_WIN32)
|
||||
# define GIT_MEMORY_BARRIER MemoryBarrier()
|
||||
#elif defined(GIT_THREADS)
|
||||
# define GIT_MEMORY_BARRIER __sync_synchronize()
|
||||
#else
|
||||
# define GIT_MEMORY_BARRIER /* noop */
|
||||
#endif
|
||||
|
||||
#endif /* INCLUDE_thread_utils_h__ */
|
||||
|
@ -282,7 +282,7 @@ static int local_push_copy_object(
|
||||
odb_obj_size) < 0 ||
|
||||
odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
|
||||
error = -1;
|
||||
} else if (git_oid_cmp(&obj->id, &remote_odb_obj_oid) != 0) {
|
||||
} else if (git_oid__cmp(&obj->id, &remote_odb_obj_oid) != 0) {
|
||||
giterr_set(GITERR_ODB, "Error when writing object to remote odb "
|
||||
"during local push operation. Remote odb object oid does not "
|
||||
"match local oid.");
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user