Merge remote-tracking branch 'origin/development' into ssh_transport

This commit is contained in:
Brad Morgan 2013-05-07 14:30:35 -04:00
commit 00e43380a0
575 changed files with 9911 additions and 2511 deletions

View File

@ -1,3 +1,18 @@
Vicent Martí <vicent@github.com> Vicent Marti <tanoku@gmail.com>
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>

View File

@ -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

View File

@ -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()

View File

@ -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

View 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

View File

@ -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);

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
/**

View File

@ -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

View File

@ -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;
/**

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.
*

View File

@ -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
*

View File

@ -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

View File

@ -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;
/**

View File

@ -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
*

View File

@ -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);

View File

@ -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

View File

@ -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
View 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
View 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
View 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

View 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

View File

@ -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
View 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

View 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

View File

@ -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
*

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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) {

View File

@ -132,14 +132,14 @@ 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,
&head_info->branchname,
head_info->refspec,
reference_name) < 0)
return -1;
if (git_buf_len(&head_info->branchname) > 0) {
if (git_buf_sets(
&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,8 +212,13 @@ 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(
@ -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 &&

View File

@ -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) \
{\

View File

@ -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

View File

@ -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;

View File

@ -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;
}
/***********

View File

@ -26,7 +26,7 @@ struct map_data {
* files that have the text property set. Alternatives are lf, crlf
* and native, which uses the platform's native line ending. The default
* value is native. See gitattributes(5) for more information on
* end-of-line conversion.
* end-of-line conversion.
*/
static git_cvar_map _cvar_map_eol[] = {
{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
@ -37,7 +37,7 @@ static git_cvar_map _cvar_map_eol[] = {
/*
* core.autocrlf
* Setting this variable to "true" is almost the same as setting
* Setting this variable to "true" is almost the same as setting
* the text attribute to "auto" on all files except that text files are
* not guaranteed to be normalized: files that contain CRLF in the
* repository will not be touched. Use this setting if you want to have
@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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(

View File

@ -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,12 +187,12 @@ 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;
int error = 0;
GIT_UNUSED(f);
error = git_hashsig_create((git_hashsig **)out, buf, len, opt);
@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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;
@ -330,6 +378,8 @@ void git_index_clear(git_index *index)
git_vector_clear(&index->entries);
git_index_reuc_clear(index);
git_index_name_clear(index);
git_futils_filestamp_set(&index->stamp, NULL);
@ -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;

View File

@ -33,6 +33,7 @@ struct git_index {
git_tree_cache *tree;
git_vector names;
git_vector reuc;
git_vector_cmp entries_cmp_path;

View File

@ -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(
@ -391,7 +374,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
{
int error = -1;
struct git_pack_header hdr;
size_t processed;
size_t processed;
git_mwindow_file *mwf = &idx->pack->mwf;
assert(idx && data && stats);
@ -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;

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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
View 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
View 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

View File

@ -162,7 +162,7 @@ static git_mwindow *new_window(
git_mwindow *w;
w = git__malloc(sizeof(*w));
if (w == NULL)
return NULL;

View File

@ -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)

View File

@ -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
View 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
View File

@ -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;
}

View File

@ -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

View File

@ -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"

View File

@ -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;
}

View File

@ -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
View 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

View File

@ -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

View File

@ -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;

View File

@ -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");
}

View File

@ -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,

View File

@ -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);

View File

@ -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;
@ -74,23 +79,16 @@ int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
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(

View File

@ -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

View File

@ -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(
@ -62,7 +69,7 @@ static int reference_read(
/* Determine the full path of the file */
if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
return -1;
result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, NULL, updated);
git_buf_free(&path);
@ -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;
@ -175,7 +179,7 @@ static int packed_load(refdb_fs_backend *backend)
ref_cache->packfile = git_strmap_alloc();
GITERR_CHECK_ALLOC(ref_cache->packfile);
}
result = reference_read(&packfile, &ref_cache->packfile_time,
backend->path, GIT_PACKEDREFS_FILE, &updated);
@ -193,7 +197,7 @@ static int packed_load(refdb_fs_backend *backend)
if (result < 0)
return -1;
if (!updated)
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)
@ -456,19 +488,19 @@ static int packed_map_entry(
if (packed_load(backend) < 0)
return -1;
/* Look up on the packfile */
packfile_refs = backend->refcache.packfile;
*pos = git_strmap_lookup_index(packfile_refs, ref_name);
if (!git_strmap_valid_index(packfile_refs, *pos)) {
giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref_name);
return GIT_ENOTFOUND;
}
*entry = git_strmap_value_at(packfile_refs, *pos);
return 0;
}
@ -480,13 +512,14 @@ static int packed_lookup(
struct packref *entry;
khiter_t pos;
int error = 0;
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;
}
@ -582,7 +615,7 @@ static int refdb_fs_backend__foreach(
git_buf refs_path = GIT_BUF_INIT;
const char *ref_name;
void *ref = NULL;
GIT_UNUSED(ref);
assert(_backend);
@ -590,7 +623,7 @@ static int refdb_fs_backend__foreach(
if (packed_load(backend) < 0)
return -1;
/* list all the packed references first */
if (list_type & GIT_REF_OID) {
git_strmap_foreach(backend->refcache.packfile, ref_name, ref, {
@ -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)
@ -924,7 +950,7 @@ static int refdb_fs_backend__delete(
repo = backend->repo;
/* If a loose reference exists, remove it from the filesystem */
if (git_buf_joinpath(&loose_path, repo->path_repository, ref->name) < 0)
return -1;
@ -932,7 +958,7 @@ static int refdb_fs_backend__delete(
error = p_unlink(loose_path.ptr);
loose_deleted = 1;
}
git_buf_free(&loose_path);
if (error != 0)
@ -946,7 +972,7 @@ static int refdb_fs_backend__delete(
error = packed_write(backend);
}
if (pack_error == GIT_ENOTFOUND)
error = loose_deleted ? 0 : GIT_ENOTFOUND;
else
@ -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;

View File

@ -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);
}
@ -238,10 +254,10 @@ int git_reference_lookup_resolved(
max_nesting = MAX_NESTING_LEVEL;
else if (max_nesting < 0)
max_nesting = DEFAULT_NESTING_LEVEL;
strncpy(scan_name, name, GIT_REFNAME_MAX);
scan_type = GIT_REF_SYMBOLIC;
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return -1;
@ -259,7 +275,7 @@ int git_reference_lookup_resolved(
if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
return error;
scan_type = ref->type;
}
@ -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);
@ -327,7 +353,7 @@ static int reference__create(
git_refdb *refdb;
git_reference *ref = NULL;
int error = 0;
if (ref_out)
*ref_out = NULL;
@ -335,15 +361,22 @@ static int reference__create(
(error = reference_can_write(repo, normalized, NULL, force)) < 0 ||
(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);
return error;
}
if (ref_out == NULL)
git_reference_free(ref);
else
@ -363,17 +396,17 @@ int git_reference_create(
int error = 0;
assert(repo && name && oid);
/* Sanity check the reference being created - target must exist. */
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
return error;
if (!git_odb_exists(odb, oid)) {
giterr_set(GITERR_REFERENCE,
"Target OID for the reference doesn't exist on the repository");
return -1;
}
return reference__create(ref_out, repo, name, oid, NULL, force);
}
@ -388,7 +421,7 @@ int git_reference_symbolic_create(
int error = 0;
assert(repo && name && target);
if ((error = git_reference__normalize_name_lax(
normalized, sizeof(normalized), target)) < 0)
return error;
@ -402,7 +435,7 @@ int git_reference_set_target(
const git_oid *id)
{
assert(out && ref && id);
if (ref->type != GIT_REF_OID) {
giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
return -1;
@ -417,13 +450,13 @@ int git_reference_symbolic_set_target(
const char *target)
{
assert(out && ref && target);
if (ref->type != GIT_REF_SYMBOLIC) {
giterr_set(GITERR_REFERENCE,
"Cannot set symbolic target on a direct reference");
return -1;
}
return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1);
}
@ -437,17 +470,16 @@ 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;
*out = NULL;
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;
@ -474,11 +508,11 @@ int git_reference_rename(
/* Now delete the old ref and save the new one. */
if ((error = git_refdb_delete(ref->db, ref)) < 0)
goto on_error;
/* Save the new reference. */
if ((error = git_refdb_write(ref->db, result)) < 0)
goto rollback;
/* Update HEAD it was poiting to the reference being renamed. */
if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
@ -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(
@ -801,7 +846,7 @@ static int reference__update_terminal(
if (nesting > MAX_NESTING_LEVEL)
return GIT_ENOTFOUND;
error = git_reference_lookup(&ref, repo, ref_name);
/* If we haven't found the reference at all, create a new reference. */
@ -809,10 +854,10 @@ static int reference__update_terminal(
giterr_clear();
return git_reference_create(NULL, repo, ref_name, oid, 0);
}
if (error < 0)
return error;
/* If the ref is a symbolic reference, follow its target. */
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
error = reference__update_terminal(repo, git_reference_symbolic_target(ref), oid,
@ -822,7 +867,7 @@ static int reference__update_terminal(
git_reference_free(ref);
error = git_reference_create(NULL, repo, ref_name, oid, 1);
}
return error;
}
@ -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;
}

View File

@ -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,14 +50,14 @@
struct git_reference {
git_refdb *db;
git_ref_t type;
union {
git_oid oid;
char *symbolic;
} target;
git_oid peel;
char name[0];
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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;
}
@ -389,14 +393,8 @@ int git_tag_delete(git_repository *repo, const char *tag_name)
if ((error = git_reference_delete(tag_ref)) == 0)
git_reference_free(tag_ref);
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);
return error;
}
typedef struct {

View File

@ -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

View File

@ -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__ */

Some files were not shown because too many files have changed in this diff Show More