summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Straub <bs@github.com>2013-10-28 11:04:58 -0700
committerBen Straub <bs@github.com>2013-10-28 11:04:58 -0700
commit42c8f8f807fe986534e0cbabbfabc32cb4eb9077 (patch)
treefa76cb66e6d49855be344026b1cbe31d03962916 /src
parenta7d28f40a2a01382b76c55ca0a0672c177adaf69 (diff)
parent5c50f22a93c78190fb7d81802199ff9defc8cf55 (diff)
downloadlibgit2-42c8f8f807fe986534e0cbabbfabc32cb4eb9077.tar.gz
Merge remote-tracking branch 'libgit2/development' into blame
Diffstat (limited to 'src')
-rw-r--r--src/attr.c1
-rw-r--r--src/checkout.c741
-rw-r--r--src/clone.c42
-rw-r--r--src/config.h2
-rw-r--r--src/config_cache.c1
-rw-r--r--src/diff.c132
-rw-r--r--src/diff.h10
-rw-r--r--src/diff_file.c4
-rw-r--r--src/diff_file.h2
-rw-r--r--src/diff_patch.c284
-rw-r--r--src/diff_patch.h19
-rw-r--r--src/diff_print.c241
-rw-r--r--src/diff_tform.c81
-rw-r--r--src/diff_xdiff.c122
-rw-r--r--src/errors.c1
-rw-r--r--src/fileops.c136
-rw-r--r--src/fileops.h16
-rw-r--r--src/global.c95
-rw-r--r--src/ignore.c1
-rw-r--r--src/index.c13
-rw-r--r--src/index.h2
-rw-r--r--src/indexer.c328
-rw-r--r--src/iterator.c18
-rw-r--r--src/iterator.h10
-rw-r--r--src/merge_file.c2
-rw-r--r--src/odb.c9
-rw-r--r--src/odb_loose.c6
-rw-r--r--src/odb_pack.c5
-rw-r--r--src/pack-objects.c81
-rw-r--r--src/pack.c32
-rw-r--r--src/pack.h2
-rw-r--r--src/path.c265
-rw-r--r--src/path.h122
-rw-r--r--src/pathspec.c2
-rw-r--r--src/push.c4
-rw-r--r--src/refdb.c16
-rw-r--r--src/refdb.h4
-rw-r--r--src/refdb_fs.c365
-rw-r--r--src/reflog.c382
-rw-r--r--src/reflog.h7
-rw-r--r--src/refs.c70
-rw-r--r--src/refs.h2
-rw-r--r--src/remote.c1
-rw-r--r--src/repository.c272
-rw-r--r--src/repository.h3
-rw-r--r--src/reset.c13
-rw-r--r--src/revparse.c4
-rw-r--r--src/stash.c150
-rw-r--r--src/status.c6
-rw-r--r--src/status.h4
-rw-r--r--src/submodule.c9
-rw-r--r--src/transports/cred.c40
-rw-r--r--src/transports/smart.c9
-rw-r--r--src/transports/smart.h6
-rw-r--r--src/transports/smart_pkt.c3
-rw-r--r--src/transports/smart_protocol.c92
-rw-r--r--src/transports/ssh.c11
-rw-r--r--src/transports/winhttp.c23
-rw-r--r--src/tree.c9
-rw-r--r--src/win32/posix_w32.c4
60 files changed, 2908 insertions, 1429 deletions
diff --git a/src/attr.c b/src/attr.c
index 7946db4d6..98a328a55 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -1,3 +1,4 @@
+#include "common.h"
#include "repository.h"
#include "fileops.h"
#include "config.h"
diff --git a/src/checkout.c b/src/checkout.c
index 5d741d393..662803725 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -26,6 +26,8 @@
#include "diff.h"
#include "pathspec.h"
#include "buf_text.h"
+#include "merge_file.h"
+#include "path.h"
/* See docs/checkout-internals.md for more information */
@@ -35,21 +37,23 @@ enum {
CHECKOUT_ACTION__UPDATE_BLOB = 2,
CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
CHECKOUT_ACTION__CONFLICT = 8,
- CHECKOUT_ACTION__MAX = 8,
- CHECKOUT_ACTION__DEFER_REMOVE = 16,
+ CHECKOUT_ACTION__UPDATE_CONFLICT = 16,
+ CHECKOUT_ACTION__MAX = 16,
+ CHECKOUT_ACTION__DEFER_REMOVE = 32,
CHECKOUT_ACTION__REMOVE_AND_UPDATE =
(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
};
typedef struct {
git_repository *repo;
- git_diff_list *diff;
+ git_diff *diff;
git_checkout_opts opts;
bool opts_free_baseline;
char *pfx;
git_index *index;
git_pool pool;
git_vector removes;
+ git_vector conflicts;
git_buf path;
size_t workdir_len;
unsigned int strategy;
@@ -59,6 +63,16 @@ typedef struct {
size_t completed_steps;
} checkout_data;
+typedef struct {
+ const git_index_entry *ancestor;
+ const git_index_entry *ours;
+ const git_index_entry *theirs;
+
+ int name_collision:1,
+ directoryfile:1,
+ one_to_two:1;
+} checkout_conflictdata;
+
static int checkout_notify(
checkout_data *data,
git_checkout_notify_t why,
@@ -592,6 +606,359 @@ static int checkout_remaining_wd_items(
return error;
}
+GIT_INLINE(int) checkout_idxentry_cmp(
+ const git_index_entry *a,
+ const git_index_entry *b)
+{
+ if (!a && !b)
+ return 0;
+ else if (!a && b)
+ return -1;
+ else if(a && !b)
+ return 1;
+ else
+ return strcmp(a->path, b->path);
+}
+
+static int checkout_conflictdata_cmp(const void *a, const void *b)
+{
+ const checkout_conflictdata *ca = a;
+ const checkout_conflictdata *cb = b;
+ int diff;
+
+ if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 &&
+ (diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0)
+ diff = checkout_idxentry_cmp(ca->theirs, cb->theirs);
+
+ return diff;
+}
+
+int checkout_conflictdata_empty(const git_vector *conflicts, size_t idx)
+{
+ checkout_conflictdata *conflict;
+
+ if ((conflict = git_vector_get(conflicts, idx)) == NULL)
+ return -1;
+
+ if (conflict->ancestor || conflict->ours || conflict->theirs)
+ return 0;
+
+ git__free(conflict);
+ return 1;
+}
+
+GIT_INLINE(bool) conflict_pathspec_match(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec,
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs)
+{
+ /* if the pathspec matches ours *or* theirs, proceed */
+ if (ours && git_pathspec__match(pathspec, ours->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ if (theirs && git_pathspec__match(pathspec, theirs->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ if (ancestor && git_pathspec__match(pathspec, ancestor->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ return false;
+}
+
+static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
+{
+ git_index_conflict_iterator *iterator = NULL;
+ const git_index_entry *ancestor, *ours, *theirs;
+ checkout_conflictdata *conflict;
+ int error = 0;
+
+ if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0)
+ goto done;
+
+ data->conflicts._cmp = checkout_conflictdata_cmp;
+
+ /* Collect the conflicts */
+ while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
+ if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
+ continue;
+
+ conflict = git__calloc(1, sizeof(checkout_conflictdata));
+ GITERR_CHECK_ALLOC(conflict);
+
+ conflict->ancestor = ancestor;
+ conflict->ours = ours;
+ conflict->theirs = theirs;
+
+ git_vector_insert(&data->conflicts, conflict);
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+done:
+ git_index_conflict_iterator_free(iterator);
+
+ return error;
+}
+
+GIT_INLINE(int) checkout_conflicts_cmp_entry(
+ const char *path,
+ const git_index_entry *entry)
+{
+ return strcmp((const char *)path, entry->path);
+}
+
+static int checkout_conflicts_cmp_ancestor(const void *p, const void *c)
+{
+ const char *path = p;
+ const checkout_conflictdata *conflict = c;
+
+ if (!conflict->ancestor)
+ return 1;
+
+ return checkout_conflicts_cmp_entry(path, conflict->ancestor);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_ancestor(
+ checkout_data *data,
+ const char *path)
+{
+ size_t pos;
+
+ if (git_vector_bsearch2(&pos, &data->conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
+ return NULL;
+
+ return git_vector_get(&data->conflicts, pos);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_branch(
+ checkout_data *data,
+ const char *path)
+{
+ checkout_conflictdata *conflict;
+ size_t i;
+
+ git_vector_foreach(&data->conflicts, i, conflict) {
+ int cmp = -1;
+
+ if (conflict->ancestor)
+ break;
+
+ if (conflict->ours)
+ cmp = checkout_conflicts_cmp_entry(path, conflict->ours);
+ else if (conflict->theirs)
+ cmp = checkout_conflicts_cmp_entry(path, conflict->theirs);
+
+ if (cmp == 0)
+ return conflict;
+ }
+
+ return NULL;
+}
+
+static int checkout_conflicts_load_byname_entry(
+ checkout_conflictdata **ancestor_out,
+ checkout_conflictdata **ours_out,
+ checkout_conflictdata **theirs_out,
+ checkout_data *data,
+ const git_index_name_entry *name_entry)
+{
+ checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL;
+ int error = 0;
+
+ *ancestor_out = NULL;
+ *ours_out = NULL;
+ *theirs_out = NULL;
+
+ if (!name_entry->ancestor) {
+ giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor");
+ error = -1;
+ goto done;
+ }
+
+ if (!name_entry->ours && !name_entry->theirs) {
+ giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs");
+ error = -1;
+ goto done;
+ }
+
+ if ((ancestor = checkout_conflicts_search_ancestor(data,
+ name_entry->ancestor)) == NULL) {
+ giterr_set(GITERR_INDEX,
+ "A NAME entry referenced ancestor entry '%s' which does not exist in the main index",
+ name_entry->ancestor);
+ error = -1;
+ goto done;
+ }
+
+ if (name_entry->ours) {
+ if (strcmp(name_entry->ancestor, name_entry->ours) == 0)
+ ours = ancestor;
+ else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
+ ours->ours == NULL) {
+ giterr_set(GITERR_INDEX,
+ "A NAME entry referenced our entry '%s' which does not exist in the main index",
+ name_entry->ours);
+ error = -1;
+ goto done;
+ }
+ }
+
+ if (name_entry->theirs) {
+ if (strcmp(name_entry->ancestor, name_entry->theirs) == 0)
+ theirs = ancestor;
+ else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0)
+ theirs = ours;
+ else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
+ theirs->theirs == NULL) {
+ giterr_set(GITERR_INDEX,
+ "A NAME entry referenced their entry '%s' which does not exist in the main index",
+ name_entry->theirs);
+ error = -1;
+ goto done;
+ }
+ }
+
+ *ancestor_out = ancestor;
+ *ours_out = ours;
+ *theirs_out = theirs;
+
+done:
+ return error;
+}
+
+static int checkout_conflicts_coalesce_renames(
+ checkout_data *data)
+{
+ const git_index_name_entry *name_entry;
+ checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
+ size_t i, names;
+ int error = 0;
+
+ /* Juggle entries based on renames */
+ names = git_index_name_entrycount(data->index);
+
+ for (i = 0; i < names; i++) {
+ name_entry = git_index_name_get_byindex(data->index, i);
+
+ if ((error = checkout_conflicts_load_byname_entry(
+ &ancestor_conflict, &our_conflict, &their_conflict,
+ data, name_entry)) < 0)
+ goto done;
+
+ if (our_conflict && our_conflict != ancestor_conflict) {
+ ancestor_conflict->ours = our_conflict->ours;
+ our_conflict->ours = NULL;
+
+ if (our_conflict->theirs)
+ our_conflict->name_collision = 1;
+
+ if (our_conflict->name_collision)
+ ancestor_conflict->name_collision = 1;
+ }
+
+ if (their_conflict && their_conflict != ancestor_conflict) {
+ ancestor_conflict->theirs = their_conflict->theirs;
+ their_conflict->theirs = NULL;
+
+ if (their_conflict->ours)
+ their_conflict->name_collision = 1;
+
+ if (their_conflict->name_collision)
+ ancestor_conflict->name_collision = 1;
+ }
+
+ if (our_conflict && our_conflict != ancestor_conflict &&
+ their_conflict && their_conflict != ancestor_conflict)
+ ancestor_conflict->one_to_two = 1;
+ }
+
+ git_vector_remove_matching(&data->conflicts, checkout_conflictdata_empty);
+
+done:
+ return error;
+}
+
+static int checkout_conflicts_mark_directoryfile(
+ checkout_data *data)
+{
+ checkout_conflictdata *conflict;
+ const git_index_entry *entry;
+ size_t i, j, len;
+ const char *path;
+ int prefixed, error = 0;
+
+ len = git_index_entrycount(data->index);
+
+ /* Find d/f conflicts */
+ git_vector_foreach(&data->conflicts, i, conflict) {
+ if ((conflict->ours && conflict->theirs) ||
+ (!conflict->ours && !conflict->theirs))
+ continue;
+
+ path = conflict->ours ?
+ conflict->ours->path : conflict->theirs->path;
+
+ if ((error = git_index_find(&j, data->index, path)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ giterr_set(GITERR_INDEX,
+ "Index inconsistency, could not find entry for expected conflict '%s'", path);
+
+ goto done;
+ }
+
+ for (; j < len; j++) {
+ if ((entry = git_index_get_byindex(data->index, j)) == NULL) {
+ giterr_set(GITERR_INDEX,
+ "Index inconsistency, truncated index while loading expected conflict '%s'", path);
+ error = -1;
+ goto done;
+ }
+
+ prefixed = git_path_equal_or_prefixed(path, entry->path);
+
+ if (prefixed == GIT_PATH_EQUAL)
+ continue;
+
+ if (prefixed == GIT_PATH_PREFIX)
+ conflict->directoryfile = 1;
+
+ break;
+ }
+ }
+
+done:
+ return error;
+}
+
+static int checkout_get_conflicts(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec)
+{
+ int error = 0;
+
+ if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED)
+ return 0;
+
+ if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 ||
+ (error = checkout_conflicts_coalesce_renames(data)) < 0 ||
+ (error = checkout_conflicts_mark_directoryfile(data)) < 0)
+ goto done;
+
+done:
+ return error;
+}
+
static int checkout_get_actions(
uint32_t **actions_ptr,
size_t **counts_ptr,
@@ -659,6 +1026,12 @@ static int checkout_get_actions(
goto fail;
}
+
+ if ((error = checkout_get_conflicts(data, workdir, &pathspec)) < 0)
+ goto fail;
+
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->conflicts);
+
git_pathspec__vfree(&pathspec);
git_pool_clear(&pathpool);
@@ -707,6 +1080,7 @@ static int blob_content_to_file(
struct stat *st,
git_blob *blob,
const char *path,
+ const char * hint_path,
mode_t entry_filemode,
git_checkout_opts *opts)
{
@@ -715,9 +1089,12 @@ static int blob_content_to_file(
git_buf out = GIT_BUF_INIT;
git_filter_list *fl = NULL;
+ if (hint_path == NULL)
+ hint_path = path;
+
if (!opts->disable_filters)
error = git_filter_list_load(
- &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE);
+ &fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
if (!error)
error = git_filter_list_apply_to_blob(&out, fl, blob);
@@ -783,7 +1160,8 @@ static int checkout_update_index(
memset(&entry, 0, sizeof(entry));
entry.path = (char *)file->path; /* cast to prevent warning */
- git_index_entry__init_from_stat(&entry, st);
+ git_index_entry__init_from_stat(
+ &entry, st, !(git_index_caps(data->index) & GIT_INDEXCAP_NO_FILEMODE));
git_oid_cpy(&entry.oid, &file->oid);
return git_index_add(data->index, &entry);
@@ -885,34 +1263,26 @@ static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
return 0;
}
-static int checkout_blob(
+static int checkout_write_content(
checkout_data *data,
- const git_diff_file *file)
+ const git_oid *oid,
+ const char *full_path,
+ const char *hint_path,
+ unsigned int mode,
+ struct stat *st)
{
int error = 0;
git_blob *blob;
- struct stat st;
-
- git_buf_truncate(&data->path, data->workdir_len);
- if (git_buf_puts(&data->path, file->path) < 0)
- return -1;
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
- int rval = checkout_safe_for_update_only(
- git_buf_cstr(&data->path), file->mode);
- if (rval <= 0)
- return rval;
- }
- if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
+ if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
return error;
- if (S_ISLNK(file->mode))
+ if (S_ISLNK(mode))
error = blob_content_to_link(
- &st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink);
+ st, blob, full_path, data->opts.dir_mode, data->can_symlink);
else
error = blob_content_to_file(
- &st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
+ st, blob, full_path, hint_path, mode, &data->opts);
git_blob_free(blob);
@@ -927,6 +1297,30 @@ static int checkout_blob(
error = 0;
}
+ return error;
+}
+
+static int checkout_blob(
+ checkout_data *data,
+ const git_diff_file *file)
+{
+ int error = 0;
+ struct stat st;
+
+ git_buf_truncate(&data->path, data->workdir_len);
+ if (git_buf_puts(&data->path, file->path) < 0)
+ return -1;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
+ int rval = checkout_safe_for_update_only(
+ git_buf_cstr(&data->path), file->mode);
+ if (rval <= 0)
+ return rval;
+ }
+
+ error = checkout_write_content(
+ data, &file->oid, git_buf_cstr(&data->path), NULL, file->mode, &st);
+
/* update the index unless prevented */
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
error = checkout_update_index(data, file, &st);
@@ -1097,8 +1491,279 @@ static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
return error;
}
+
+static int conflict_entry_name(
+ git_buf *out,
+ const char *side_name,
+ const char *filename)
+{
+ if (git_buf_puts(out, side_name) < 0 ||
+ git_buf_putc(out, ':') < 0 ||
+ git_buf_puts(out, filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int checkout_path_suffixed(git_buf *path, const char *suffix)
+{
+ size_t path_len;
+ int i = 0, error = 0;
+
+ if ((error = git_buf_putc(path, '~')) < 0 || (error = git_buf_puts(path, suffix)) < 0)
+ return -1;
+
+ path_len = git_buf_len(path);
+
+ while (git_path_exists(git_buf_cstr(path)) && i < INT_MAX) {
+ git_buf_truncate(path, path_len);
+
+ if ((error = git_buf_putc(path, '_')) < 0 ||
+ (error = git_buf_printf(path, "%d", i)) < 0)
+ return error;
+
+ i++;
+ }
+
+ if (i == INT_MAX) {
+ git_buf_truncate(path, path_len);
+
+ giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path);
+ return GIT_EEXISTS;
+ }
+
+ return 0;
+}
+
+static int checkout_write_entry(
+ checkout_data *data,
+ checkout_conflictdata *conflict,
+ const git_index_entry *side)
+{
+ const char *hint_path = NULL, *suffix;
+ struct stat st;
+ int error;
+
+ assert (side == conflict->ours || side == conflict->theirs);
+
+ git_buf_truncate(&data->path, data->workdir_len);
+ if (git_buf_puts(&data->path, side->path) < 0)
+ return -1;
+
+ if ((conflict->name_collision || conflict->directoryfile) &&
+ (data->strategy & GIT_CHECKOUT_USE_OURS) == 0 &&
+ (data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) {
+
+ if (side == conflict->ours)
+ suffix = data->opts.our_label ? data->opts.our_label :
+ "ours";
+ else
+ suffix = data->opts.their_label ? data->opts.their_label :
+ "theirs";
+
+ if (checkout_path_suffixed(&data->path, suffix) < 0)
+ return -1;
+
+ hint_path = side->path;
+ }
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+ (error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0)
+ return error;
+
+ return checkout_write_content(data,
+ &side->oid, git_buf_cstr(&data->path), hint_path, side->mode, &st);
+}
+
+static int checkout_write_entries(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ int error = 0;
+
+ if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ return error;
+}
+
+static int checkout_merge_path(
+ git_buf *out,
+ checkout_data *data,
+ checkout_conflictdata *conflict,
+ git_merge_file_result *result)
+{
+ const char *our_label_raw, *their_label_raw, *suffix;
+ int error = 0;
+
+ if ((error = git_buf_joinpath(out, git_repository_workdir(data->repo), result->path)) < 0)
+ return error;
+
+ /* Most conflicts simply use the filename in the index */
+ if (!conflict->name_collision)
+ return 0;
+
+ /* Rename 2->1 conflicts need the branch name appended */
+ our_label_raw = data->opts.our_label ? data->opts.our_label : "ours";
+ their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs";
+ suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw;
+
+ if ((error = checkout_path_suffixed(out, suffix)) < 0)
+ return error;
+
+ return 0;
+}
+
+static int checkout_write_merge(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
+ path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT;
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+ int error = 0;
+
+ if ((conflict->ancestor &&
+ (error = git_merge_file_input_from_index_entry(
+ &ancestor, data->repo, conflict->ancestor)) < 0) ||
+ (error = git_merge_file_input_from_index_entry(
+ &ours, data->repo, conflict->ours)) < 0 ||
+ (error = git_merge_file_input_from_index_entry(
+ &theirs, data->repo, conflict->theirs)) < 0)
+ goto done;
+
+ ancestor.label = NULL;
+ ours.label = data->opts.our_label ? data->opts.our_label : "ours";
+ theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
+
+ /* If all the paths are identical, decorate the diff3 file with the branch
+ * names. Otherwise, append branch_name:path.
+ */
+ if (conflict->ours && conflict->theirs &&
+ strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
+
+ if ((error = conflict_entry_name(
+ &our_label, ours.label, conflict->ours->path)) < 0 ||
+ (error = conflict_entry_name(
+ &their_label, theirs.label, conflict->theirs->path)) < 0)
+ goto done;
+
+ ours.label = git_buf_cstr(&our_label);
+ theirs.label = git_buf_cstr(&their_label);
+ }
+
+ if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, 0)) < 0)
+ goto done;
+
+ if (result.path == NULL || result.mode == 0) {
+ giterr_set(GITERR_CHECKOUT, "Could not merge contents of file");
+ error = GIT_EMERGECONFLICT;
+ goto done;
+ }
+
+ if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
+ goto done;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+ (error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0)
+ goto done;
+
+ if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
+ (error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER)) < 0 ||
+ (error = git_filebuf_write(&output, result.data, result.len)) < 0 ||
+ (error = git_filebuf_commit(&output, result.mode)) < 0)
+ goto done;
+
+done:
+ git_buf_free(&our_label);
+ git_buf_free(&their_label);
+
+ git_merge_file_input_free(&ancestor);
+ git_merge_file_input_free(&ours);
+ git_merge_file_input_free(&theirs);
+ git_merge_file_result_free(&result);
+ git_buf_free(&path_workdir);
+ git_buf_free(&path_suffixed);
+
+ return error;
+}
+
+static int checkout_create_conflicts(checkout_data *data)
+{
+ git_vector conflicts = GIT_VECTOR_INIT;
+ checkout_conflictdata *conflict;
+ size_t i;
+ int error = 0;
+
+ git_vector_foreach(&data->conflicts, i, conflict) {
+ /* Both deleted: nothing to do */
+ if (conflict->ours == NULL && conflict->theirs == NULL)
+ error = 0;
+
+ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+ conflict->ours)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+ conflict->theirs)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ /* Ignore the other side of name collisions. */
+ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+ !conflict->ours && conflict->name_collision)
+ error = 0;
+ else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+ !conflict->theirs && conflict->name_collision)
+ error = 0;
+
+ /* For modify/delete, name collisions and d/f conflicts, write
+ * the file (potentially with the name mangled.
+ */
+ else if (conflict->ours != NULL && conflict->theirs == NULL)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ else if (conflict->ours == NULL && conflict->theirs != NULL)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ /* Add/add conflicts and rename 1->2 conflicts, write the
+ * ours/theirs sides (potentially name mangled).
+ */
+ else if (conflict->one_to_two)
+ error = checkout_write_entries(data, conflict);
+
+ /* If all sides are links, write the ours side */
+ else if (S_ISLNK(conflict->ours->mode) &&
+ S_ISLNK(conflict->theirs->mode))
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ /* Link/file conflicts, write the file side */
+ else if (S_ISLNK(conflict->ours->mode))
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+ else if (S_ISLNK(conflict->theirs->mode))
+ error = checkout_write_entry(data, conflict, conflict->ours);
+
+ else
+ error = checkout_write_merge(data, conflict);
+
+ if (error)
+ break;
+
+ data->completed_steps++;
+ report_progress(data,
+ conflict->ours ? conflict->ours->path :
+ (conflict->theirs ? conflict->theirs->path : conflict->ancestor->path));
+ }
+
+ return error;
+}
+
+
static void checkout_data_clear(checkout_data *data)
{
+ checkout_conflictdata *conflict;
+ size_t i;
+
if (data->opts_free_baseline) {
git_tree_free(data->opts.baseline);
data->opts.baseline = NULL;
@@ -1107,6 +1772,11 @@ static void checkout_data_clear(checkout_data *data)
git_vector_free(&data->removes);
git_pool_clear(&data->pool);
+ git_vector_foreach(&data->conflicts, i, conflict)
+ git__free(conflict);
+
+ git_vector_free(&data->conflicts);
+
git__free(data->pfx);
data->pfx = NULL;
@@ -1171,7 +1841,17 @@ static int checkout_data_init(
(error = git_index_read(data->index)) < 0)
goto cleanup;
- /* clear the REUC when doing a tree or commit checkout */
+ /* cannot checkout if unresolved conflicts exist */
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
+ git_index_has_conflicts(data->index)) {
+ error = GIT_EMERGECONFLICT;
+ giterr_set(GITERR_CHECKOUT,
+ "unresolved conflicts exist in the index");
+ goto cleanup;
+ }
+
+ /* clean conflict data when doing a tree or commit checkout */
+ git_index_name_clear(data->index);
git_index_reuc_clear(data->index);
}
}
@@ -1213,6 +1893,7 @@ static int checkout_data_init(
}
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
+ (error = git_vector_init(&data->conflicts, 0, NULL)) < 0 ||
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->path)) < 0)
@@ -1283,14 +1964,16 @@ int git_checkout_iterator(
goto cleanup;
/* Loop through diff (and working directory iterator) building a list of
- * actions to be taken, plus look for conflicts and send notifications.
+ * actions to be taken, plus look for conflicts and send notifications,
+ * then loop through conflicts.
*/
if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
goto cleanup;
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
counts[CHECKOUT_ACTION__UPDATE_BLOB] +
- counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
report_progress(&data, NULL); /* establish 0 baseline */
@@ -1309,6 +1992,10 @@ int git_checkout_iterator(
(error = checkout_create_submodules(actions, &data)) < 0)
goto cleanup;
+ if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 &&
+ (error = checkout_create_conflicts(&data)) < 0)
+ goto cleanup;
+
assert(data.completed_steps == data.total_steps);
cleanup:
@@ -1319,7 +2006,7 @@ cleanup:
(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
error = git_index_write(data.index);
- git_diff_list_free(data.diff);
+ git_diff_free(data.diff);
git_iterator_free(workdir);
git_iterator_free(baseline);
git__free(actions);
diff --git a/src/clone.c b/src/clone.c
index f3e365c07..657243945 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -391,11 +391,11 @@ int git_clone(
const char *local_path,
const git_clone_options *_options)
{
- int retcode = GIT_ERROR;
+ int error = 0;
git_repository *repo = NULL;
git_remote *origin;
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
- int remove_directory_on_failure = 0;
+ uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
assert(out && url && local_path);
@@ -408,33 +408,29 @@ int git_clone(
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
giterr_set(GITERR_INVALID,
"'%s' exists and is not an empty directory", local_path);
- return GIT_ERROR;
+ return GIT_EEXISTS;
}
- /* Only remove the directory on failure if we create it */
- remove_directory_on_failure = !git_path_exists(local_path);
+ /* Only remove the root directory on failure if we create it */
+ if (git_path_exists(local_path))
+ rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
- if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
- return retcode;
+ if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
+ return error;
- if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
- goto cleanup;
+ if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
+ error = git_clone_into(
+ repo, origin, &options.checkout_opts, options.checkout_branch);
- retcode = git_clone_into(repo, origin, &options.checkout_opts, options.checkout_branch);
- git_remote_free(origin);
+ git_remote_free(origin);
+ }
- if (retcode < 0)
- goto cleanup;
+ if (error < 0) {
+ git_repository_free(repo);
+ repo = NULL;
+ (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
+ }
*out = repo;
- return 0;
-
-cleanup:
- git_repository_free(repo);
- if (remove_directory_on_failure)
- git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
- else
- git_futils_cleanupdir_r(local_path);
-
- return retcode;
+ return error;
}
diff --git a/src/config.h b/src/config.h
index 85db5e3e1..01e8465cc 100644
--- a/src/config.h
+++ b/src/config.h
@@ -47,7 +47,7 @@ extern int git_config_rename_section(
* @param out the new backend
* @param path where the config file is located
*/
-extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
+extern int git_config_file__ondisk(git_config_backend **out, const char *path);
extern int git_config__normalize_name(const char *in, char **out);
diff --git a/src/config_cache.c b/src/config_cache.c
index 84de3a5ed..6808521a3 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -67,6 +67,7 @@ static struct map_data _cvar_maps[] = {
{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
+ {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
};
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
diff --git a/src/diff.c b/src/diff.c
index 39facce60..37bc737d6 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -21,7 +21,7 @@
(VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
static git_diff_delta *diff_delta__alloc(
- git_diff_list *diff,
+ git_diff *diff,
git_delta_t status,
const char *path)
{
@@ -50,7 +50,7 @@ static git_diff_delta *diff_delta__alloc(
}
static int diff_notify(
- const git_diff_list *diff,
+ const git_diff *diff,
const git_diff_delta *delta,
const char *matched_pathspec)
{
@@ -62,7 +62,7 @@ static int diff_notify(
}
static int diff_delta__from_one(
- git_diff_list *diff,
+ git_diff *diff,
git_delta_t status,
const git_index_entry *entry)
{
@@ -81,7 +81,7 @@ static int diff_delta__from_one(
if (!git_pathspec__match(
&diff->pathspec, entry->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
&matched_pathspec, NULL))
return 0;
@@ -90,6 +90,7 @@ static int diff_delta__from_one(
/* This fn is just for single-sided diffs */
assert(status != GIT_DELTA_MODIFIED);
+ delta->nfiles = 1;
if (delta->status == GIT_DELTA_DELETED) {
delta->old_file.mode = entry->mode;
@@ -120,7 +121,7 @@ static int diff_delta__from_one(
}
static int diff_delta__from_two(
- git_diff_list *diff,
+ git_diff *diff,
git_delta_t status,
const git_index_entry *old_entry,
uint32_t old_mode,
@@ -148,6 +149,7 @@ static int diff_delta__from_two(
delta = diff_delta__alloc(diff, status, canonical_path);
GITERR_CHECK_ALLOC(delta);
+ delta->nfiles = 2;
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
delta->old_file.size = old_entry->file_size;
@@ -181,7 +183,7 @@ static int diff_delta__from_two(
}
static git_diff_delta *diff_delta__last_for_item(
- git_diff_list *diff,
+ git_diff *diff,
const git_index_entry *item)
{
git_diff_delta *delta = git_vector_last(&diff->deltas);
@@ -340,13 +342,13 @@ static const char *diff_mnemonic_prefix(
return pfx;
}
-static git_diff_list *diff_list_alloc(
+static git_diff *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));
+ git_diff *diff = git__calloc(1, sizeof(git_diff));
if (!diff)
return NULL;
@@ -360,7 +362,7 @@ static git_diff_list *diff_list_alloc(
if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
git_pool_init(&diff->pool, 1, 0) < 0) {
- git_diff_list_free(diff);
+ git_diff_free(diff);
return NULL;
}
@@ -368,14 +370,14 @@ static git_diff_list *diff_list_alloc(
* 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->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
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->opts.flags |= GIT_DIFF_IGNORE_CASE;
diff->strcomp = git__strcasecmp;
diff->strncomp = git__strncasecmp;
@@ -389,7 +391,7 @@ static git_diff_list *diff_list_alloc(
}
static int diff_list_apply_options(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_options *opts)
{
git_config *cfg;
@@ -399,9 +401,9 @@ static int diff_list_apply_options(
if (opts) {
/* copy user options (except case sensitivity info from iterators) */
- bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE);
+ bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
memcpy(&diff->opts, opts, sizeof(diff->opts));
- DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
+ DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
/* initialize pathspec from options */
if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
@@ -413,7 +415,7 @@ static int diff_list_apply_options(
diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
/* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT))
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* load config values that affect diff behavior */
@@ -490,7 +492,7 @@ static int diff_list_apply_options(
return 0;
}
-static void diff_list_free(git_diff_list *diff)
+static void diff_list_free(git_diff *diff)
{
git_diff_delta *delta;
unsigned int i;
@@ -508,7 +510,7 @@ static void diff_list_free(git_diff_list *diff)
git__free(diff);
}
-void git_diff_list_free(git_diff_list *diff)
+void git_diff_free(git_diff *diff)
{
if (!diff)
return;
@@ -516,7 +518,7 @@ void git_diff_list_free(git_diff_list *diff)
GIT_REFCOUNT_DEC(diff, diff_list_free);
}
-void git_diff_list_addref(git_diff_list *diff)
+void git_diff_addref(git_diff *diff)
{
GIT_REFCOUNT_INC(diff);
}
@@ -612,7 +614,7 @@ typedef struct {
static int maybe_modified_submodule(
git_delta_t *status,
git_oid *found_oid,
- git_diff_list *diff,
+ git_diff *diff,
diff_in_progress *info)
{
int error = 0;
@@ -659,7 +661,7 @@ static int maybe_modified_submodule(
}
static int maybe_modified(
- git_diff_list *diff,
+ git_diff *diff,
diff_in_progress *info)
{
git_oid noid;
@@ -674,7 +676,7 @@ static int maybe_modified(
if (!git_pathspec__match(
&diff->pathspec, oitem->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
&matched_pathspec, NULL))
return 0;
@@ -778,7 +780,7 @@ static int maybe_modified(
}
static bool entry_is_prefixed(
- git_diff_list *diff,
+ git_diff *diff,
const git_index_entry *item,
const git_index_entry *prefix_item)
{
@@ -795,7 +797,7 @@ static bool entry_is_prefixed(
}
static int diff_scan_inside_untracked_dir(
- git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
+ git_diff *diff, diff_in_progress *info, git_delta_t *delta_type)
{
int error = 0;
git_buf base = GIT_BUF_INIT;
@@ -861,7 +863,7 @@ done:
}
static int handle_unmatched_new_item(
- git_diff_list *diff, diff_in_progress *info)
+ git_diff *diff, diff_in_progress *info)
{
int error = 0;
const git_index_entry *nitem = info->nitem;
@@ -910,7 +912,7 @@ static int handle_unmatched_new_item(
*/
if (!recurse_into_dir &&
delta_type == GIT_DELTA_UNTRACKED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
{
git_diff_delta *last;
@@ -1016,7 +1018,7 @@ static int handle_unmatched_new_item(
}
static int handle_unmatched_old_item(
- git_diff_list *diff, diff_in_progress *info)
+ git_diff *diff, diff_in_progress *info)
{
int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
if (error < 0)
@@ -1048,7 +1050,7 @@ static int handle_unmatched_old_item(
}
static int handle_matched_item(
- git_diff_list *diff, diff_in_progress *info)
+ git_diff *diff, diff_in_progress *info)
{
int error = 0;
@@ -1063,7 +1065,7 @@ static int handle_matched_item(
}
int git_diff__from_iterators(
- git_diff_list **diff_ptr,
+ git_diff **diff_ptr,
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter,
@@ -1071,7 +1073,7 @@ int git_diff__from_iterators(
{
int error = 0;
diff_in_progress info;
- git_diff_list *diff;
+ git_diff *diff;
*diff_ptr = NULL;
@@ -1084,7 +1086,7 @@ int git_diff__from_iterators(
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 (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
(error = git_iterator_set_ignore_case(new_iter, true)) < 0)
goto cleanup;
@@ -1132,7 +1134,7 @@ cleanup:
if (!error)
*diff_ptr = diff;
else
- git_diff_list_free(diff);
+ git_diff_free(diff);
git_buf_free(&info.ignore_prefix);
@@ -1149,7 +1151,7 @@ cleanup:
} while (0)
int git_diff_tree_to_tree(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
git_tree *new_tree,
@@ -1164,7 +1166,7 @@ int git_diff_tree_to_tree(
* currently case insensitive, unless the user explicitly asked
* for case insensitivity
*/
- if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
+ if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
iflag = GIT_ITERATOR_IGNORE_CASE;
DIFF_FROM_ITERATORS(
@@ -1176,7 +1178,7 @@ int git_diff_tree_to_tree(
}
int git_diff_tree_to_index(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
git_index *index,
@@ -1204,9 +1206,9 @@ int git_diff_tree_to_index(
git_index__set_ignore_case(index, true);
if (!error) {
- git_diff_list *d = *diff;
+ git_diff *d = *diff;
- d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
+ d->opts.flags |= GIT_DIFF_IGNORE_CASE;
d->strcomp = git__strcasecmp;
d->strncomp = git__strncasecmp;
d->pfxcomp = git__prefixcmp_icase;
@@ -1221,7 +1223,7 @@ int git_diff_tree_to_index(
}
int git_diff_index_to_workdir(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_index *index,
const git_diff_options *opts)
@@ -1242,9 +1244,8 @@ int git_diff_index_to_workdir(
return error;
}
-
int git_diff_tree_to_workdir(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
const git_diff_options *opts)
@@ -1262,16 +1263,43 @@ int git_diff_tree_to_workdir(
return error;
}
-size_t git_diff_num_deltas(git_diff_list *diff)
+int git_diff_tree_to_workdir_with_index(
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ const git_diff_options *opts)
+{
+ int error = 0;
+ git_diff *d1 = NULL, *d2 = NULL;
+
+ assert(diff && repo);
+
+ if (!(error = git_diff_tree_to_index(&d1, repo, old_tree, NULL, opts)) &&
+ !(error = git_diff_index_to_workdir(&d2, repo, NULL, opts)))
+ error = git_diff_merge(d1, d2);
+
+ git_diff_free(d2);
+
+ if (error) {
+ git_diff_free(d1);
+ d1 = NULL;
+ }
+
+ *diff = d1;
+ return error;
+}
+
+
+size_t git_diff_num_deltas(const git_diff *diff)
{
assert(diff);
- return (size_t)diff->deltas.length;
+ return diff->deltas.length;
}
-size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
+size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
{
size_t i, count = 0;
- git_diff_delta *delta;
+ const git_diff_delta *delta;
assert(diff);
@@ -1282,14 +1310,20 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
return count;
}
-int git_diff_is_sorted_icase(const git_diff_list *diff)
+const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
+{
+ assert(diff);
+ return git_vector_get(&diff->deltas, idx);
+}
+
+int git_diff_is_sorted_icase(const git_diff *diff)
{
- return (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
+ return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
}
int git_diff__paired_foreach(
- git_diff_list *head2idx,
- git_diff_list *idx2wd,
+ git_diff *head2idx,
+ git_diff *idx2wd,
int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
void *payload)
{
@@ -1318,10 +1352,10 @@ int git_diff__paired_foreach(
* always sort by the old name in the i2w list.
*/
h2i_icase = head2idx != NULL &&
- (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
+ (head2idx->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
i2w_icase = idx2wd != NULL &&
- (idx2wd->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
+ (idx2wd->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
icase_mismatch =
(head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
diff --git a/src/diff.h b/src/diff.h
index bec7e27d7..270bea071 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -52,7 +52,7 @@ enum {
#define GIT_DIFF__VERBOSE (1 << 30)
-struct git_diff_list {
+struct git_diff {
git_refcount rc;
git_repository *repo;
git_diff_options opts;
@@ -72,7 +72,7 @@ struct git_diff_list {
extern void git_diff__cleanup_modes(
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
-extern void git_diff_list_addref(git_diff_list *diff);
+extern void git_diff_addref(git_diff *diff);
extern int git_diff_delta__cmp(const void *a, const void *b);
extern int git_diff_delta__casecmp(const void *a, const void *b);
@@ -93,15 +93,15 @@ extern int git_diff__oid_for_file(
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
extern int git_diff__from_iterators(
- git_diff_list **diff_ptr,
+ git_diff **diff_ptr,
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter,
const git_diff_options *opts);
extern int git_diff__paired_foreach(
- git_diff_list *idx2head,
- git_diff_list *wd2idx,
+ git_diff *idx2head,
+ git_diff *wd2idx,
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
void *payload);
diff --git a/src/diff_file.c b/src/diff_file.c
index 5939ee8b8..a4c8641bc 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -88,7 +88,7 @@ static int diff_file_content_init_common(
int git_diff_file_content__init_from_diff(
git_diff_file_content *fc,
- git_diff_list *diff,
+ git_diff *diff,
size_t delta_index,
bool use_old)
{
@@ -110,7 +110,7 @@ int git_diff_file_content__init_from_diff(
has_data = use_old; break;
case GIT_DELTA_UNTRACKED:
has_data = !use_old &&
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0;
+ (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break;
case GIT_DELTA_MODIFIED:
case GIT_DELTA_COPIED:
diff --git a/src/diff_file.h b/src/diff_file.h
index fb08cca6a..84bf255aa 100644
--- a/src/diff_file.h
+++ b/src/diff_file.h
@@ -27,7 +27,7 @@ typedef struct {
extern int git_diff_file_content__init_from_diff(
git_diff_file_content *fc,
- git_diff_list *diff,
+ git_diff *diff,
size_t delta_index,
bool use_old);
diff --git a/src/diff_patch.c b/src/diff_patch.c
index cc45b6ddb..cc49d68eb 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -12,36 +12,24 @@
#include "diff_xdiff.h"
#include "fileops.h"
-/* cached information about a single span in a diff */
-typedef struct diff_patch_line diff_patch_line;
-struct diff_patch_line {
- const char *ptr;
- size_t len;
- size_t lines, oldno, newno;
- char origin;
-};
-
/* cached information about a hunk in a diff */
typedef struct diff_patch_hunk diff_patch_hunk;
struct diff_patch_hunk {
- git_diff_range range;
- char header[128];
- size_t header_len;
+ git_diff_hunk hunk;
size_t line_start;
size_t line_count;
};
-struct git_diff_patch {
+struct git_patch {
git_refcount rc;
- git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
+ git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
git_diff_delta *delta;
size_t delta_index;
git_diff_file_content ofile;
git_diff_file_content nfile;
uint32_t flags;
git_array_t(diff_patch_hunk) hunks;
- git_array_t(diff_patch_line) lines;
- size_t oldno, newno;
+ git_array_t(git_diff_line) lines;
size_t content_size, context_size, header_size;
git_pool flattened;
};
@@ -55,12 +43,13 @@ enum {
GIT_DIFF_PATCH_FLATTENED = (1 << 5),
};
-static void diff_output_init(git_diff_output*, const git_diff_options*,
- git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*);
+static void diff_output_init(
+ git_diff_output*, const git_diff_options*,
+ git_diff_file_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
-static void diff_output_to_patch(git_diff_output *, git_diff_patch *);
+static void diff_output_to_patch(git_diff_output *, git_patch *);
-static void diff_patch_update_binary(git_diff_patch *patch)
+static void diff_patch_update_binary(git_patch *patch)
{
if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
return;
@@ -74,21 +63,21 @@ static void diff_patch_update_binary(git_diff_patch *patch)
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
}
-static void diff_patch_init_common(git_diff_patch *patch)
+static void diff_patch_init_common(git_patch *patch)
{
diff_patch_update_binary(patch);
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */
+ patch->flags |= GIT_DIFF_PATCH_LOADED; /* LOADED but not DIFFABLE */
patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
if (patch->diff)
- git_diff_list_addref(patch->diff);
+ git_diff_addref(patch->diff);
}
static int diff_patch_init_from_diff(
- git_diff_patch *patch, git_diff_list *diff, size_t delta_index)
+ git_patch *patch, git_diff *diff, size_t delta_index)
{
int error = 0;
@@ -109,12 +98,10 @@ static int diff_patch_init_from_diff(
}
static int diff_patch_alloc_from_diff(
- git_diff_patch **out,
- git_diff_list *diff,
- size_t delta_index)
+ git_patch **out, git_diff *diff, size_t delta_index)
{
int error;
- git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch));
+ git_patch *patch = git__calloc(1, sizeof(git_patch));
GITERR_CHECK_ALLOC(patch);
if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
@@ -129,7 +116,7 @@ static int diff_patch_alloc_from_diff(
return error;
}
-static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
+static int diff_patch_load(git_patch *patch, git_diff_output *output)
{
int error = 0;
bool incomplete_data;
@@ -207,7 +194,7 @@ cleanup:
}
static int diff_patch_file_callback(
- git_diff_patch *patch, git_diff_output *output)
+ git_patch *patch, git_diff_output *output)
{
float progress;
@@ -223,7 +210,7 @@ static int diff_patch_file_callback(
return output->error;
}
-static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
+static int diff_patch_generate(git_patch *patch, git_diff_output *output)
{
int error = 0;
@@ -248,7 +235,7 @@ static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
return error;
}
-static void diff_patch_free(git_diff_patch *patch)
+static void diff_patch_free(git_patch *patch)
{
git_diff_file_content__clear(&patch->ofile);
git_diff_file_content__clear(&patch->nfile);
@@ -256,7 +243,7 @@ static void diff_patch_free(git_diff_patch *patch)
git_array_clear(patch->lines);
git_array_clear(patch->hunks);
- git_diff_list_free(patch->diff); /* decrements refcount */
+ git_diff_free(patch->diff); /* decrements refcount */
patch->diff = NULL;
git_pool_clear(&patch->flattened);
@@ -265,7 +252,7 @@ static void diff_patch_free(git_diff_patch *patch)
git__free(patch);
}
-static int diff_required(git_diff_list *diff, const char *action)
+static int diff_required(git_diff *diff, const char *action)
{
if (diff)
return 0;
@@ -274,16 +261,16 @@ static int diff_required(git_diff_list *diff, const char *action)
}
int git_diff_foreach(
- git_diff_list *diff,
+ git_diff *diff,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
int error = 0;
git_xdiff_output xo;
size_t idx;
- git_diff_patch patch;
+ git_patch patch;
if (diff_required(diff, "git_diff_foreach") < 0)
return -1;
@@ -305,7 +292,7 @@ int git_diff_foreach(
if (!error)
error = diff_patch_generate(&patch, &xo.output);
- git_diff_patch_free(&patch);
+ git_patch_free(&patch);
}
if (error < 0)
@@ -318,7 +305,7 @@ int git_diff_foreach(
}
typedef struct {
- git_diff_patch patch;
+ git_patch patch;
git_diff_delta delta;
char paths[GIT_FLEX_ARRAY];
} diff_patch_with_delta;
@@ -326,7 +313,7 @@ typedef struct {
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
{
int error = 0;
- git_diff_patch *patch = &pd->patch;
+ git_patch *patch = &pd->patch;
bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
@@ -430,7 +417,7 @@ int git_diff_blobs(
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
int error = 0;
@@ -452,13 +439,13 @@ int git_diff_blobs(
error = diff_patch_from_blobs(
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
- git_diff_patch_free(&pd.patch);
+ git_patch_free(&pd.patch);
return error;
}
-int git_diff_patch_from_blobs(
- git_diff_patch **out,
+int git_patch_from_blobs(
+ git_patch **out,
const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob,
@@ -484,9 +471,9 @@ int git_diff_patch_from_blobs(
pd, &xo, old_blob, old_path, new_blob, new_path, opts);
if (!error)
- *out = (git_diff_patch *)pd;
+ *out = (git_patch *)pd;
else
- git_diff_patch_free((git_diff_patch *)pd);
+ git_patch_free((git_patch *)pd);
return error;
}
@@ -542,7 +529,7 @@ int git_diff_blob_to_buffer(
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
int error = 0;
@@ -564,13 +551,13 @@ int git_diff_blob_to_buffer(
error = diff_patch_from_blob_and_buffer(
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
- git_diff_patch_free(&pd.patch);
+ git_patch_free(&pd.patch);
return error;
}
-int git_diff_patch_from_blob_and_buffer(
- git_diff_patch **out,
+int git_patch_from_blob_and_buffer(
+ git_patch **out,
const git_blob *old_blob,
const char *old_path,
const char *buf,
@@ -597,28 +584,24 @@ int git_diff_patch_from_blob_and_buffer(
pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
if (!error)
- *out = (git_diff_patch *)pd;
+ *out = (git_patch *)pd;
else
- git_diff_patch_free((git_diff_patch *)pd);
+ git_patch_free((git_patch *)pd);
return error;
}
-int git_diff_get_patch(
- git_diff_patch **patch_ptr,
- const git_diff_delta **delta_ptr,
- git_diff_list *diff,
- size_t idx)
+int git_patch_from_diff(
+ git_patch **patch_ptr, git_diff *diff, size_t idx)
{
int error = 0;
git_xdiff_output xo;
git_diff_delta *delta = NULL;
- git_diff_patch *patch = NULL;
+ git_patch *patch = NULL;
if (patch_ptr) *patch_ptr = NULL;
- if (delta_ptr) *delta_ptr = NULL;
- if (diff_required(diff, "git_diff_get_patch") < 0)
+ if (diff_required(diff, "git_patch_from_diff") < 0)
return -1;
delta = git_vector_get(&diff->deltas, idx);
@@ -627,9 +610,6 @@ int git_diff_get_patch(
return GIT_ENOTFOUND;
}
- if (delta_ptr)
- *delta_ptr = delta;
-
if (git_diff_delta__should_skip(&diff->opts, delta))
return 0;
@@ -656,7 +636,7 @@ int git_diff_get_patch(
}
if (error || !patch_ptr)
- git_diff_patch_free(patch);
+ git_patch_free(patch);
else
*patch_ptr = patch;
@@ -665,36 +645,36 @@ int git_diff_get_patch(
return error;
}
-void git_diff_patch_free(git_diff_patch *patch)
+void git_patch_free(git_patch *patch)
{
if (patch)
GIT_REFCOUNT_DEC(patch, diff_patch_free);
}
-const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
+const git_diff_delta *git_patch_get_delta(git_patch *patch)
{
assert(patch);
return patch->delta;
}
-size_t git_diff_patch_num_hunks(git_diff_patch *patch)
+size_t git_patch_num_hunks(git_patch *patch)
{
assert(patch);
return git_array_size(patch->hunks);
}
-int git_diff_patch_line_stats(
+int git_patch_line_stats(
size_t *total_ctxt,
size_t *total_adds,
size_t *total_dels,
- const git_diff_patch *patch)
+ const git_patch *patch)
{
size_t totals[3], idx;
memset(totals, 0, sizeof(totals));
for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
- diff_patch_line *line = git_array_get(patch->lines, idx);
+ git_diff_line *line = git_array_get(patch->lines, idx);
if (!line)
continue;
@@ -726,12 +706,10 @@ static int diff_error_outofrange(const char *thing)
return GIT_ENOTFOUND;
}
-int git_diff_patch_get_hunk(
- const git_diff_range **range,
- const char **header,
- size_t *header_len,
+int git_patch_get_hunk(
+ const git_diff_hunk **out,
size_t *lines_in_hunk,
- git_diff_patch *patch,
+ git_patch *patch,
size_t hunk_idx)
{
diff_patch_hunk *hunk;
@@ -740,21 +718,17 @@ int git_diff_patch_get_hunk(
hunk = git_array_get(patch->hunks, hunk_idx);
if (!hunk) {
- if (range) *range = NULL;
- if (header) *header = NULL;
- if (header_len) *header_len = 0;
+ if (out) *out = NULL;
if (lines_in_hunk) *lines_in_hunk = 0;
return diff_error_outofrange("hunk");
}
- if (range) *range = &hunk->range;
- if (header) *header = hunk->header;
- if (header_len) *header_len = hunk->header_len;
+ if (out) *out = &hunk->hunk;
if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
return 0;
}
-int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
+int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx)
{
diff_patch_hunk *hunk;
assert(patch);
@@ -764,54 +738,35 @@ int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
return (int)hunk->line_count;
}
-int git_diff_patch_get_line_in_hunk(
- char *line_origin,
- const char **content,
- size_t *content_len,
- int *old_lineno,
- int *new_lineno,
- git_diff_patch *patch,
+int git_patch_get_line_in_hunk(
+ const git_diff_line **out,
+ git_patch *patch,
size_t hunk_idx,
size_t line_of_hunk)
{
diff_patch_hunk *hunk;
- diff_patch_line *line;
- const char *thing;
+ git_diff_line *line;
assert(patch);
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
- thing = "hunk";
- goto notfound;
+ if (out) *out = NULL;
+ return diff_error_outofrange("hunk");
}
if (line_of_hunk >= hunk->line_count ||
!(line = git_array_get(
patch->lines, hunk->line_start + line_of_hunk))) {
- thing = "line";
- goto notfound;
+ if (out) *out = NULL;
+ return diff_error_outofrange("line");
}
- if (line_origin) *line_origin = line->origin;
- if (content) *content = line->ptr;
- if (content_len) *content_len = line->len;
- if (old_lineno) *old_lineno = (int)line->oldno;
- if (new_lineno) *new_lineno = (int)line->newno;
-
+ if (out) *out = line;
return 0;
-
-notfound:
- if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
- if (content) *content = NULL;
- if (content_len) *content_len = 0;
- if (old_lineno) *old_lineno = -1;
- if (new_lineno) *new_lineno = -1;
-
- return diff_error_outofrange(thing);
}
-size_t git_diff_patch_size(
- git_diff_patch *patch,
+size_t git_patch_size(
+ git_patch *patch,
int include_context,
int include_hunk_headers,
int include_file_headers)
@@ -843,36 +798,36 @@ size_t git_diff_patch_size(
return out;
}
-git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
+git_diff *git_patch__diff(git_patch *patch)
{
return patch->diff;
}
-git_diff_driver *git_diff_patch__driver(git_diff_patch *patch)
+git_diff_driver *git_patch__driver(git_patch *patch)
{
/* ofile driver is representative for whole patch */
return patch->ofile.driver;
}
-void git_diff_patch__old_data(
- char **ptr, size_t *len, git_diff_patch *patch)
+void git_patch__old_data(
+ char **ptr, size_t *len, git_patch *patch)
{
*ptr = patch->ofile.map.data;
*len = patch->ofile.map.len;
}
-void git_diff_patch__new_data(
- char **ptr, size_t *len, git_diff_patch *patch)
+void git_patch__new_data(
+ char **ptr, size_t *len, git_patch *patch)
{
*ptr = patch->nfile.map.data;
*len = patch->nfile.map.len;
}
-int git_diff_patch__invoke_callbacks(
- git_diff_patch *patch,
+int git_patch__invoke_callbacks(
+ git_patch *patch,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
+ git_diff_line_cb line_cb,
void *payload)
{
int error = 0;
@@ -887,18 +842,16 @@ int git_diff_patch__invoke_callbacks(
for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
diff_patch_hunk *h = git_array_get(patch->hunks, i);
- error = hunk_cb(
- patch->delta, &h->range, h->header, h->header_len, payload);
+ error = hunk_cb(patch->delta, &h->hunk, payload);
if (!line_cb)
continue;
for (j = 0; !error && j < h->line_count; ++j) {
- diff_patch_line *l =
+ git_diff_line *l =
git_array_get(patch->lines, h->line_start + j);
- error = line_cb(
- patch->delta, &h->range, l->origin, l->ptr, l->len, payload);
+ error = line_cb(patch->delta, &h->hunk, l, payload);
}
}
@@ -917,12 +870,10 @@ static int diff_patch_file_cb(
static int diff_patch_hunk_cb(
const git_diff_delta *delta,
- const git_diff_range *range,
- const char *header,
- size_t header_len,
+ const git_diff_hunk *hunk_,
void *payload)
{
- git_diff_patch *patch = payload;
+ git_patch *patch = payload;
diff_patch_hunk *hunk;
GIT_UNUSED(delta);
@@ -930,39 +881,28 @@ static int diff_patch_hunk_cb(
hunk = git_array_alloc(patch->hunks);
GITERR_CHECK_ALLOC(hunk);
- memcpy(&hunk->range, range, sizeof(hunk->range));
-
- assert(header_len + 1 < sizeof(hunk->header));
- memcpy(&hunk->header, header, header_len);
- hunk->header[header_len] = '\0';
- hunk->header_len = header_len;
+ memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
- patch->header_size += header_len;
+ patch->header_size += hunk_->header_len;
hunk->line_start = git_array_size(patch->lines);
hunk->line_count = 0;
- patch->oldno = range->old_start;
- patch->newno = range->new_start;
-
return 0;
}
static int diff_patch_line_cb(
const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
+ const git_diff_hunk *hunk_,
+ const git_diff_line *line_,
void *payload)
{
- git_diff_patch *patch = payload;
+ git_patch *patch = payload;
diff_patch_hunk *hunk;
- diff_patch_line *line;
- const char *content_end = content + content_len;
+ git_diff_line *line;
GIT_UNUSED(delta);
- GIT_UNUSED(range);
+ GIT_UNUSED(hunk_);
hunk = git_array_last(patch->hunks);
GITERR_CHECK_ALLOC(hunk);
@@ -970,48 +910,20 @@ static int diff_patch_line_cb(
line = git_array_alloc(patch->lines);
GITERR_CHECK_ALLOC(line);
- line->ptr = content;
- line->len = content_len;
- line->origin = line_origin;
+ memcpy(line, line_, sizeof(*line));
/* do some bookkeeping so we can provide old/new line numbers */
- line->lines = 0;
- while (content < content_end)
- if (*content++ == '\n')
- ++line->lines;
+ patch->content_size += line->content_len;
- patch->content_size += content_len;
-
- switch (line_origin) {
- case GIT_DIFF_LINE_ADDITION:
- patch->content_size += 1;
- case GIT_DIFF_LINE_DEL_EOFNL:
- line->oldno = -1;
- line->newno = patch->newno;
- patch->newno += line->lines;
- break;
- case GIT_DIFF_LINE_DELETION:
+ if (line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION)
patch->content_size += 1;
- case GIT_DIFF_LINE_ADD_EOFNL:
- line->oldno = patch->oldno;
- line->newno = -1;
- patch->oldno += line->lines;
- break;
- case GIT_DIFF_LINE_CONTEXT:
+ else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
patch->content_size += 1;
- patch->context_size += 1;
- case GIT_DIFF_LINE_CONTEXT_EOFNL:
- patch->context_size += content_len;
- line->oldno = patch->oldno;
- line->newno = patch->newno;
- patch->oldno += line->lines;
- patch->newno += line->lines;
- break;
- default:
- assert(false);
- break;
- }
+ patch->context_size += line->content_len + 1;
+ } else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+ patch->context_size += line->content_len;
hunk->line_count++;
@@ -1023,7 +935,7 @@ static void diff_output_init(
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
GIT_UNUSED(opts);
@@ -1036,7 +948,7 @@ static void diff_output_init(
out->payload = payload;
}
-static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch)
+static void diff_output_to_patch(git_diff_output *out, git_patch *patch)
{
diff_output_init(
out, NULL,
diff --git a/src/diff_patch.h b/src/diff_patch.h
index 56af14600..df2ba4c31 100644
--- a/src/diff_patch.h
+++ b/src/diff_patch.h
@@ -11,19 +11,20 @@
#include "diff.h"
#include "diff_file.h"
#include "array.h"
+#include "git2/patch.h"
-extern git_diff_list *git_diff_patch__diff(git_diff_patch *);
+extern git_diff *git_patch__diff(git_patch *);
-extern git_diff_driver *git_diff_patch__driver(git_diff_patch *);
+extern git_diff_driver *git_patch__driver(git_patch *);
-extern void git_diff_patch__old_data(char **, size_t *, git_diff_patch *);
-extern void git_diff_patch__new_data(char **, size_t *, git_diff_patch *);
+extern void git_patch__old_data(char **, size_t *, git_patch *);
+extern void git_patch__new_data(char **, size_t *, git_patch *);
-extern int git_diff_patch__invoke_callbacks(
- git_diff_patch *patch,
+extern int git_patch__invoke_callbacks(
+ git_patch *patch,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
+ git_diff_line_cb line_cb,
void *payload);
typedef struct git_diff_output git_diff_output;
@@ -31,7 +32,7 @@ struct git_diff_output {
/* these callbacks are issued with the diff data */
git_diff_file_cb file_cb;
git_diff_hunk_cb hunk_cb;
- git_diff_data_cb data_cb;
+ git_diff_line_cb data_cb;
void *payload;
/* this records the actual error in cases where it may be obscured */
@@ -40,7 +41,7 @@ struct git_diff_output {
/* this callback is used to do the diff and drive the other callbacks.
* see diff_xdiff.h for how to use this in practice for now.
*/
- int (*diff_cb)(git_diff_output *output, git_diff_patch *patch);
+ int (*diff_cb)(git_diff_output *output, git_patch *patch);
};
#endif
diff --git a/src/diff_print.c b/src/diff_print.c
index fd18b67e3..b04b11515 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -10,23 +10,36 @@
#include "fileops.h"
typedef struct {
- git_diff_list *diff;
- git_diff_data_cb print_cb;
+ git_diff *diff;
+ git_diff_format_t format;
+ git_diff_line_cb print_cb;
void *payload;
git_buf *buf;
+ uint32_t flags;
int oid_strlen;
+ git_diff_line line;
} diff_print_info;
static int diff_print_info_init(
diff_print_info *pi,
- git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
+ git_buf *out,
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb cb,
+ void *payload)
{
pi->diff = diff;
+ pi->format = format;
pi->print_cb = cb;
pi->payload = payload;
pi->buf = out;
- if (!diff || !diff->repo)
+ if (diff)
+ pi->flags = diff->opts.flags;
+
+ if (diff && diff->opts.oid_abbrev != 0)
+ pi->oid_strlen = diff->opts.oid_abbrev;
+ else if (!diff || !diff->repo)
pi->oid_strlen = GIT_ABBREV_DEFAULT;
else if (git_repository__cvar(
&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
@@ -39,6 +52,11 @@ static int diff_print_info_init(
else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
pi->oid_strlen = GIT_OID_HEXSZ + 1;
+ memset(&pi->line, 0, sizeof(pi->line));
+ pi->line.old_lineno = -1;
+ pi->line.new_lineno = -1;
+ pi->line.num_lines = 1;
+
return 0;
}
@@ -77,7 +95,35 @@ static int callback_error(void)
return GIT_EUSER;
}
-static int diff_print_one_compact(
+static int diff_print_one_name_only(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ git_buf *out = pi->buf;
+
+ GIT_UNUSED(progress);
+
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
+ delta->status == GIT_DELTA_UNMODIFIED)
+ return 0;
+
+ git_buf_clear(out);
+
+ if (git_buf_puts(out, delta->new_file.path) < 0 ||
+ git_buf_putc(out, '\n'))
+ return -1;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(out);
+ pi->line.content_len = git_buf_len(out);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+static int diff_print_one_name_status(
const git_diff_delta *delta, float progress, void *data)
{
diff_print_info *pi = data;
@@ -88,7 +134,7 @@ static int diff_print_one_compact(
GIT_UNUSED(progress);
- if (code == ' ')
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
return 0;
old_suffix = diff_pick_suffix(delta->old_file.mode);
@@ -112,31 +158,16 @@ static int diff_print_one_compact(
if (git_buf_oom(out))
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(out), git_buf_len(out), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(out);
+ pi->line.content_len = git_buf_len(out);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
return 0;
}
-/* print a git_diff_list to a print callback in compact format */
-int git_diff_print_compact(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
static int diff_print_one_raw(
const git_diff_delta *delta, float progress, void *data)
{
@@ -147,7 +178,7 @@ static int diff_print_one_raw(
GIT_UNUSED(progress);
- if (code == ' ')
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
return 0;
git_buf_clear(out);
@@ -173,31 +204,16 @@ static int diff_print_one_raw(
if (git_buf_oom(out))
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(out), git_buf_len(out), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(out);
+ pi->line.content_len = git_buf_len(out);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
return 0;
}
-/* print a git_diff_list to a print callback in raw output format */
-int git_diff_print_raw(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
static int diff_print_oid_range(
git_buf *out, const git_diff_delta *delta, int oid_strlen)
{
@@ -287,7 +303,6 @@ static int diff_print_patch_file(
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
const char *newpfx =
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
- uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
GIT_UNUSED(progress);
@@ -295,15 +310,18 @@ static int diff_print_patch_file(
delta->status == GIT_DELTA_UNMODIFIED ||
delta->status == GIT_DELTA_IGNORED ||
(delta->status == GIT_DELTA_UNTRACKED &&
- (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
+ (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
if (git_diff_delta__format_file_header(
pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(pi->buf);
+ pi->line.content_len = git_buf_len(pi->buf);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
@@ -316,8 +334,12 @@ static int diff_print_patch_file(
"Binary files %s%s and %s%s differ\n") < 0)
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_BINARY;
+ pi->line.content = git_buf_cstr(pi->buf);
+ pi->line.content_len = git_buf_len(pi->buf);
+ pi->line.num_lines = 1;
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
return 0;
@@ -325,9 +347,7 @@ static int diff_print_patch_file(
static int diff_print_patch_hunk(
const git_diff_delta *d,
- const git_diff_range *r,
- const char *header,
- size_t header_len,
+ const git_diff_hunk *h,
void *data)
{
diff_print_info *pi = data;
@@ -335,12 +355,11 @@ static int diff_print_patch_hunk(
if (S_ISDIR(d->new_file.mode))
return 0;
- git_buf_clear(pi->buf);
- if (git_buf_put(pi->buf, header, header_len) < 0)
- return -1;
+ pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
+ pi->line.content = h->header;
+ pi->line.content_len = h->header_len;
- if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ if (pi->print_cb(d, h, &pi->line, pi->payload))
return callback_error();
return 0;
@@ -348,10 +367,8 @@ static int diff_print_patch_hunk(
static int diff_print_patch_line(
const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin, /* GIT_DIFF_LINE value from above */
- const char *content,
- size_t content_len,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
void *data)
{
diff_print_info *pi = data;
@@ -359,50 +376,63 @@ static int diff_print_patch_line(
if (S_ISDIR(delta->new_file.mode))
return 0;
- git_buf_clear(pi->buf);
- git_buf_grow(pi->buf, content_len + 2);
-
- if (line_origin == GIT_DIFF_LINE_ADDITION ||
- line_origin == GIT_DIFF_LINE_DELETION ||
- line_origin == GIT_DIFF_LINE_CONTEXT)
- git_buf_putc(pi->buf, line_origin);
-
- git_buf_put(pi->buf, content, content_len);
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, range, line_origin,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ if (pi->print_cb(delta, hunk, line, pi->payload))
return callback_error();
return 0;
}
-/* print a git_diff_list to an output callback in patch format */
-int git_diff_print_patch(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
+/* print a git_diff to an output callback */
+int git_diff_print(
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb print_cb,
void *payload)
{
int error;
git_buf buf = GIT_BUF_INIT;
diff_print_info pi;
+ git_diff_file_cb print_file = NULL;
+ git_diff_hunk_cb print_hunk = NULL;
+ git_diff_line_cb print_line = NULL;
+
+ switch (format) {
+ case GIT_DIFF_FORMAT_PATCH:
+ print_file = diff_print_patch_file;
+ print_hunk = diff_print_patch_hunk;
+ print_line = diff_print_patch_line;
+ break;
+ case GIT_DIFF_FORMAT_PATCH_HEADER:
+ print_file = diff_print_patch_file;
+ break;
+ case GIT_DIFF_FORMAT_RAW:
+ print_file = diff_print_one_raw;
+ break;
+ case GIT_DIFF_FORMAT_NAME_ONLY:
+ print_file = diff_print_one_name_only;
+ break;
+ case GIT_DIFF_FORMAT_NAME_STATUS:
+ print_file = diff_print_one_name_status;
+ break;
+ default:
+ giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
+ return -1;
+ }
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
+ if (!(error = diff_print_info_init(
+ &pi, &buf, diff, format, print_cb, payload)))
error = git_diff_foreach(
- diff, diff_print_patch_file, diff_print_patch_hunk,
- diff_print_patch_line, &pi);
+ diff, print_file, print_hunk, print_line, &pi);
git_buf_free(&buf);
return error;
}
-/* print a git_diff_patch to an output callback */
-int git_diff_patch_print(
- git_diff_patch *patch,
- git_diff_data_cb print_cb,
+/* print a git_patch to an output callback */
+int git_patch_print(
+ git_patch *patch,
+ git_diff_line_cb print_cb,
void *payload)
{
int error;
@@ -412,8 +442,9 @@ int git_diff_patch_print(
assert(patch && print_cb);
if (!(error = diff_print_info_init(
- &pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
- error = git_diff_patch__invoke_callbacks(
+ &pi, &temp, git_patch__diff(patch),
+ GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
+ error = git_patch__invoke_callbacks(
patch, diff_print_patch_file, diff_print_patch_hunk,
diff_print_patch_line, &pi);
@@ -424,26 +455,30 @@ int git_diff_patch_print(
static int diff_print_to_buffer_cb(
const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
void *payload)
{
git_buf *output = payload;
- GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
- return git_buf_put(output, content, content_len);
+ GIT_UNUSED(delta); GIT_UNUSED(hunk);
+
+ if (line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION ||
+ line->origin == GIT_DIFF_LINE_CONTEXT)
+ git_buf_putc(output, line->origin);
+
+ return git_buf_put(output, line->content, line->content_len);
}
-/* print a git_diff_patch to a string buffer */
-int git_diff_patch_to_str(
+/* print a git_patch to a string buffer */
+int git_patch_to_str(
char **string,
- git_diff_patch *patch)
+ git_patch *patch)
{
int error;
git_buf output = GIT_BUF_INIT;
- error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output);
+ error = git_patch_print(patch, diff_print_to_buffer_cb, &output);
/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
* meaning a memory allocation failure, so just map to -1...
diff --git a/src/diff_tform.c b/src/diff_tform.c
index cbe8bafbd..9461ca2c8 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -97,8 +97,8 @@ static git_diff_delta *diff_delta__merge_like_cgit(
}
int git_diff_merge(
- git_diff_list *onto,
- const git_diff_list *from)
+ git_diff *onto,
+ const git_diff *from)
{
int error = 0;
git_pool onto_pool;
@@ -117,15 +117,15 @@ int git_diff_merge(
git_pool_init(&onto_pool, 1, 0) < 0)
return -1;
- if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
- (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
+ if ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0 ||
+ (from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0)
{
ignore_case = true;
/* This function currently only supports merging diff lists that
* are sorted identically. */
- assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
- (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
+ assert((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0 &&
+ (from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
}
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
@@ -230,9 +230,9 @@ int git_diff_find_similar__calc_similarity(
#define DEFAULT_RENAME_LIMIT 200
static int normalize_find_opts(
- git_diff_list *diff,
+ git_diff *diff,
git_diff_find_options *opts,
- git_diff_find_options *given)
+ const git_diff_find_options *given)
{
git_config *cfg = NULL;
@@ -328,7 +328,7 @@ static int normalize_find_opts(
}
static int apply_splits_and_deletes(
- git_diff_list *diff, size_t expected_size, bool actually_split)
+ git_diff *diff, size_t expected_size, bool actually_split)
{
git_vector onto = GIT_VECTOR_INIT;
size_t i;
@@ -350,6 +350,7 @@ static int apply_splits_and_deletes(
goto on_error;
deleted->status = GIT_DELTA_DELETED;
+ deleted->nfiles = 1;
memset(&deleted->new_file, 0, sizeof(deleted->new_file));
deleted->new_file.path = deleted->old_file.path;
deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -361,6 +362,7 @@ static int apply_splits_and_deletes(
delta->status = GIT_DELTA_UNTRACKED;
else
delta->status = GIT_DELTA_ADDED;
+ delta->nfiles = 1;
memset(&delta->old_file, 0, sizeof(delta->old_file));
delta->old_file.path = delta->new_file.path;
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -402,7 +404,7 @@ on_error:
return -1;
}
-GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
+GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx)
{
git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
return (idx & 1) ? &delta->new_file : &delta->old_file;
@@ -419,7 +421,7 @@ typedef struct {
} similarity_info;
static int similarity_init(
- similarity_info *info, git_diff_list *diff, size_t file_idx)
+ similarity_info *info, git_diff *diff, size_t file_idx)
{
info->idx = file_idx;
info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
@@ -509,7 +511,7 @@ static void similarity_unload(similarity_info *info)
*/
static int similarity_measure(
int *score,
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
void **cache,
size_t a_idx,
@@ -595,7 +597,7 @@ cleanup:
}
static int calc_self_similarity(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
size_t delta_idx,
void **cache)
@@ -612,7 +614,7 @@ static int calc_self_similarity(
return error;
if (similarity >= 0) {
- delta->similarity = (uint32_t)similarity;
+ delta->similarity = (uint16_t)similarity;
delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
}
@@ -620,7 +622,7 @@ static int calc_self_similarity(
}
static bool is_rename_target(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
size_t delta_idx,
void **cache)
@@ -675,7 +677,7 @@ static bool is_rename_target(
}
static bool is_rename_source(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
size_t delta_idx,
void **cache)
@@ -745,25 +747,27 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
}
GIT_INLINE(void) delta_make_rename(
- git_diff_delta *to, const git_diff_delta *from, uint32_t similarity)
+ git_diff_delta *to, const git_diff_delta *from, uint16_t similarity)
{
to->status = GIT_DELTA_RENAMED;
to->similarity = similarity;
+ to->nfiles = 2;
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
}
typedef struct {
- uint32_t idx;
- uint32_t similarity;
+ size_t idx;
+ uint16_t similarity;
} diff_find_match;
int git_diff_find_similar(
- git_diff_list *diff,
- git_diff_find_options *given_opts)
+ git_diff *diff,
+ const git_diff_find_options *given_opts)
{
size_t s, t;
- int error = 0, similarity;
+ int error = 0, result;
+ uint16_t similarity;
git_diff_delta *src, *tgt;
git_diff_find_options opts;
size_t num_deltas, num_srcs = 0, num_tgts = 0;
@@ -839,17 +843,18 @@ find_best_matches:
/* calculate similarity for this pair and find best match */
if (s == t)
- similarity = -1; /* don't measure self-similarity here */
+ result = -1; /* don't measure self-similarity here */
else if ((error = similarity_measure(
- &similarity, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
+ &result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
goto cleanup;
- if (similarity < 0)
+ if (result < 0)
continue;
+ similarity = (uint16_t)result;
/* is this a better rename? */
- if (tgt2src[t].similarity < (uint32_t)similarity &&
- src2tgt[s].similarity < (uint32_t)similarity)
+ if (tgt2src[t].similarity < similarity &&
+ src2tgt[s].similarity < similarity)
{
/* eject old mapping */
if (src2tgt[s].similarity > 0) {
@@ -862,18 +867,18 @@ find_best_matches:
}
/* write new mapping */
- tgt2src[t].idx = (uint32_t)s;
- tgt2src[t].similarity = (uint32_t)similarity;
- src2tgt[s].idx = (uint32_t)t;
- src2tgt[s].similarity = (uint32_t)similarity;
+ tgt2src[t].idx = s;
+ tgt2src[t].similarity = similarity;
+ src2tgt[s].idx = t;
+ src2tgt[s].similarity = similarity;
}
/* keep best absolute match for copies */
if (tgt2src_copy != NULL &&
- tgt2src_copy[t].similarity < (uint32_t)similarity)
+ tgt2src_copy[t].similarity < similarity)
{
- tgt2src_copy[t].idx = (uint32_t)s;
- tgt2src_copy[t].similarity = (uint32_t)similarity;
+ tgt2src_copy[t].idx = s;
+ tgt2src_copy[t].similarity = similarity;
}
if (++tried_srcs >= num_srcs)
@@ -943,7 +948,7 @@ find_best_matches:
delta_make_rename(tgt, src, best_match->similarity);
num_rewrites--;
- src->status = GIT_DELTA_DELETED;
+ assert(src->status == GIT_DELTA_DELETED);
memcpy(&src->old_file, &swap, sizeof(src->old_file));
memset(&src->new_file, 0, sizeof(src->new_file));
src->new_file.path = src->old_file.path;
@@ -953,7 +958,7 @@ find_best_matches:
if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
/* what used to be at src t is now at src s */
- tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
+ tgt2src[src2tgt[t].idx].idx = s;
}
}
}
@@ -969,6 +974,7 @@ find_best_matches:
src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
+ src->nfiles = 1;
memset(&src->old_file, 0, sizeof(src->old_file));
src->old_file.path = src->new_file.path;
src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -1006,7 +1012,7 @@ find_best_matches:
/* otherwise, if we just overwrote a source, update mapping */
else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
/* what used to be at src t is now at src s */
- tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
+ tgt2src[src2tgt[t].idx].idx = s;
}
num_updates++;
@@ -1026,6 +1032,7 @@ find_best_matches:
tgt->status = GIT_DELTA_COPIED;
tgt->similarity = best_match->similarity;
+ tgt->nfiles = 2;
memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
num_updates++;
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
index 7694fb996..2aca76f87 100644
--- a/src/diff_xdiff.c
+++ b/src/diff_xdiff.c
@@ -24,26 +24,26 @@ static int git_xdiff_scan_int(const char **str, int *value)
return (digits > 0) ? 0 : -1;
}
-static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
+static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
{
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
if (*header != '@')
return -1;
- if (git_xdiff_scan_int(&header, &range->old_start) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
return -1;
if (*header == ',') {
- if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
return -1;
} else
- range->old_lines = 1;
- if (git_xdiff_scan_int(&header, &range->new_start) < 0)
+ hunk->old_lines = 1;
+ if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
return -1;
if (*header == ',') {
- if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
return -1;
} else
- range->new_lines = 1;
- if (range->old_start < 0 || range->new_start < 0)
+ hunk->new_lines = 1;
+ if (hunk->old_start < 0 || hunk->new_start < 0)
return -1;
return 0;
@@ -51,38 +51,96 @@ static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
typedef struct {
git_xdiff_output *xo;
- git_diff_patch *patch;
- git_diff_range range;
+ git_patch *patch;
+ git_diff_hunk hunk;
+ int old_lineno, new_lineno;
} git_xdiff_info;
+static int diff_update_lines(
+ git_xdiff_info *info,
+ git_diff_line *line,
+ const char *content,
+ size_t content_len)
+{
+ const char *scan = content, *scan_end = content + content_len;
+
+ for (line->num_lines = 0; scan < scan_end; ++scan)
+ if (*scan == '\n')
+ ++line->num_lines;
+
+ line->content = content;
+ line->content_len = content_len;
+
+ /* expect " "/"-"/"+", then data */
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ line->old_lineno = -1;
+ line->new_lineno = info->new_lineno;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = -1;
+ info->old_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = info->new_lineno;
+ info->old_lineno += (int)line->num_lines;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ default:
+ giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
+ (unsigned int)line->origin);
+ return -1;
+ }
+
+ return 0;
+}
+
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
{
git_xdiff_info *info = priv;
- git_diff_patch *patch = info->patch;
- const git_diff_delta *delta = git_diff_patch_delta(patch);
+ git_patch *patch = info->patch;
+ const git_diff_delta *delta = git_patch_get_delta(patch);
git_diff_output *output = &info->xo->output;
+ git_diff_line line;
if (len == 1) {
- output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
+ output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
if (output->error < 0)
return output->error;
+ info->hunk.header_len = bufs[0].size;
+ if (info->hunk.header_len >= sizeof(info->hunk.header))
+ info->hunk.header_len = sizeof(info->hunk.header) - 1;
+ memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
+ info->hunk.header[info->hunk.header_len] = '\0';
+
if (output->hunk_cb != NULL &&
- output->hunk_cb(delta, &info->range,
- bufs[0].ptr, bufs[0].size, output->payload))
+ output->hunk_cb(delta, &info->hunk, output->payload))
output->error = GIT_EUSER;
+
+ info->old_lineno = info->hunk.old_start;
+ info->new_lineno = info->hunk.new_start;
}
if (len == 2 || len == 3) {
/* expect " "/"-"/"+", then data */
- char origin =
+ line.origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
GIT_DIFF_LINE_CONTEXT;
- if (output->data_cb != NULL &&
- output->data_cb(delta, &info->range,
- origin, bufs[1].ptr, bufs[1].size, output->payload))
+ output->error = diff_update_lines(
+ info, &line, bufs[1].ptr, bufs[1].size);
+
+ if (!output->error &&
+ output->data_cb != NULL &&
+ output->data_cb(delta, &info->hunk, &line, output->payload))
output->error = GIT_EUSER;
}
@@ -92,21 +150,24 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
* If we have a '-' and a third buf, then we have removed a line
* with out a newline but added a blank line, so ADD_EOFNL.
*/
- char origin =
+ line.origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
GIT_DIFF_LINE_CONTEXT_EOFNL;
- if (output->data_cb != NULL &&
- output->data_cb(delta, &info->range,
- origin, bufs[2].ptr, bufs[2].size, output->payload))
+ output->error = diff_update_lines(
+ info, &line, bufs[2].ptr, bufs[2].size);
+
+ if (!output->error &&
+ output->data_cb != NULL &&
+ output->data_cb(delta, &info->hunk, &line, output->payload))
output->error = GIT_EUSER;
}
return output->error;
}
-static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
+static int git_xdiff(git_diff_output *output, git_patch *patch)
{
git_xdiff_output *xo = (git_xdiff_output *)output;
git_xdiff_info info;
@@ -120,7 +181,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
xo->callback.priv = &info;
git_diff_find_context_init(
- &xo->config.find_func, &findctxt, git_diff_patch__driver(patch));
+ &xo->config.find_func, &findctxt, git_patch__driver(patch));
xo->config.find_func_priv = &findctxt;
if (xo->config.find_func != NULL)
@@ -132,8 +193,8 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
* updates are needed to xo->params.flags
*/
- git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
- git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
+ git_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
+ git_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
xdl_diff(&xd_old_data, &xd_new_data,
&xo->params, &xo->config, &xo->callback);
@@ -145,7 +206,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
{
- uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
+ uint32_t flags = opts ? opts->flags : 0;
xo->output.diff_cb = git_xdiff;
@@ -161,6 +222,11 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+ if (flags & GIT_DIFF_PATIENCE)
+ xo->params.flags |= XDF_PATIENCE_DIFF;
+ if (flags & GIT_DIFF_MINIMAL)
+ xo->params.flags |= XDF_NEED_MINIMAL;
+
memset(&xo->callback, 0, sizeof(xo->callback));
xo->callback.outf = git_xdiff_cb;
}
diff --git a/src/errors.c b/src/errors.c
index e2629f69e..c9d9e4e37 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -116,4 +116,3 @@ const git_error *giterr_last(void)
{
return GIT_GLOBAL->last_error;
}
-
diff --git a/src/fileops.c b/src/fileops.c
index 5a5041cc3..17007f823 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -78,11 +78,8 @@ int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, con
int git_futils_open_ro(const char *path)
{
int fd = p_open(path, O_RDONLY);
- if (fd < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- fd = GIT_ENOTFOUND;
- giterr_set(GITERR_OS, "Failed to open '%s'", path);
- }
+ if (fd < 0)
+ return git_path_set_error(errno, path, "open");
return fd;
}
@@ -138,7 +135,6 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
int git_futils_readbuffer_updated(
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
{
- int error = 0;
git_file fd;
struct stat st;
bool changed = false;
@@ -148,15 +144,16 @@ int git_futils_readbuffer_updated(
if (updated != NULL)
*updated = 0;
- if (p_stat(path, &st) < 0) {
- error = errno;
- giterr_set(GITERR_OS, "Failed to stat '%s'", path);
- if (error == ENOENT || error == ENOTDIR)
- return GIT_ENOTFOUND;
- return -1;
+ if (p_stat(path, &st) < 0)
+ return git_path_set_error(errno, path, "stat");
+
+
+ if (S_ISDIR(st.st_mode)) {
+ giterr_set(GITERR_INVALID, "requested file is a directory");
+ return GIT_ENOTFOUND;
}
- if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
+ if (!git__is_sizet(st.st_size+1)) {
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
return -1;
}
@@ -398,8 +395,11 @@ typedef struct {
size_t baselen;
uint32_t flags;
int error;
+ int depth;
} futils__rmdir_data;
+#define FUTILS_MAX_DEPTH 100
+
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
{
if (filemsg)
@@ -438,51 +438,60 @@ static int futils__rm_first_parent(git_buf *path, const char *ceiling)
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
{
- struct stat st;
futils__rmdir_data *data = opaque;
+ int error = data->error;
+ struct stat st;
+
+ if (data->depth > FUTILS_MAX_DEPTH)
+ error = futils__error_cannot_rmdir(
+ path->ptr, "directory nesting too deep");
- if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
+ else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
if (errno == ENOENT)
- data->error = 0;
+ error = 0;
else if (errno == ENOTDIR) {
/* asked to remove a/b/c/d/e and a/b is a normal file */
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
- data->error = futils__rm_first_parent(path, data->base);
+ error = futils__rm_first_parent(path, data->base);
else
futils__error_cannot_rmdir(
path->ptr, "parent is not directory");
}
else
- futils__error_cannot_rmdir(path->ptr, "cannot access");
+ error = git_path_set_error(errno, path->ptr, "rmdir");
}
else if (S_ISDIR(st.st_mode)) {
- int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
+ data->depth++;
+
+ error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
- data->error = p_rmdir(path->ptr);
+ data->depth--;
+
+ if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
+ return data->error;
- if (data->error < 0) {
+ if ((error = p_rmdir(path->ptr)) < 0) {
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
- data->error = 0;
+ error = 0;
else
- futils__error_cannot_rmdir(path->ptr, NULL);
+ error = git_path_set_error(errno, path->ptr, "rmdir");
}
}
else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
- data->error = p_unlink(path->ptr);
-
- if (data->error < 0)
- futils__error_cannot_rmdir(path->ptr, "cannot be removed");
+ if (p_unlink(path->ptr) < 0)
+ error = git_path_set_error(errno, path->ptr, "remove");
}
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
- data->error = futils__error_cannot_rmdir(path->ptr, "still present");
+ error = futils__error_cannot_rmdir(path->ptr, "still present");
- return data->error;
+ data->error = error;
+ return error;
}
static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
@@ -505,7 +514,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
giterr_clear();
error = GIT_ITEROVER;
} else {
- futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
+ error = git_path_set_error(errno, git_buf_cstr(path), "rmdir");
}
}
@@ -517,7 +526,7 @@ int git_futils_rmdir_r(
{
int error;
git_buf fullpath = GIT_BUF_INIT;
- futils__rmdir_data data;
+ futils__rmdir_data data = { 0 };
/* build path and find "root" where we should start calling mkdir */
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
@@ -526,7 +535,6 @@ int git_futils_rmdir_r(
data.base = base ? base : "";
data.baselen = base ? strlen(base) : 0;
data.flags = flags;
- data.error = 0;
error = futils__rmdir_recurs_foreach(&data, &fullpath);
@@ -544,41 +552,6 @@ int git_futils_rmdir_r(
return error;
}
-int git_futils_cleanupdir_r(const char *path)
-{
- int error;
- git_buf fullpath = GIT_BUF_INIT;
- futils__rmdir_data data;
-
- if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
- goto clean_up;
-
- data.base = "";
- data.baselen = 0;
- data.flags = GIT_RMDIR_REMOVE_FILES;
- data.error = 0;
-
- if (!git_path_exists(path)) {
- giterr_set(GITERR_OS, "Path does not exist: %s" , path);
- error = GIT_ERROR;
- goto clean_up;
- }
-
- if (!git_path_isdir(path)) {
- giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
- error = GIT_ERROR;
- goto clean_up;
- }
-
- error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
- if (error == GIT_EUSER)
- error = data.error;
-
-clean_up:
- git_buf_free(&fullpath);
- return error;
-}
-
static int git_futils_guess_system_dirs(git_buf *out)
{
@@ -836,11 +809,8 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
return ifd;
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- ofd = GIT_ENOTFOUND;
- giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
p_close(ifd);
- return ofd;
+ return git_path_set_error(errno, to, "open for writing");
}
return cp_by_fd(ifd, ofd, true);
@@ -923,15 +893,14 @@ static int _cp_r_callback(void *ref, git_buf *from)
goto exit;
}
- if (p_lstat(info->to.ptr, &to_st) < 0) {
- if (errno != ENOENT && errno != ENOTDIR) {
- giterr_set(GITERR_OS,
- "Could not access %s while copying files", info->to.ptr);
- error = -1;
- goto exit;
- }
- } else
+ if (!(error = git_path_lstat(info->to.ptr, &to_st)))
exists = true;
+ else if (error != GIT_ENOTFOUND)
+ goto exit;
+ else {
+ giterr_clear();
+ error = 0;
+ }
if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
goto exit;
@@ -948,9 +917,12 @@ static int _cp_r_callback(void *ref, git_buf *from)
error = _cp_r_mkdir(info, from);
/* recurse onto target directory */
- if (!error && (!exists || S_ISDIR(to_st.st_mode)) &&
- ((error = git_path_direach(from, _cp_r_callback, info)) == GIT_EUSER))
- error = info->error;
+ if (!error && (!exists || S_ISDIR(to_st.st_mode))) {
+ error = git_path_direach(from, 0, _cp_r_callback, info);
+
+ if (error == GIT_EUSER)
+ error = info->error;
+ }
if (oldmode != 0)
info->dirmode = oldmode;
diff --git a/src/fileops.h b/src/fileops.h
index 6bd693b99..1b2728e58 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -115,12 +115,7 @@ extern int git_futils_mkpath2file(const char *path, const mode_t mode);
* * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
* if removing this item leaves them empty
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
- *
- * The old values translate into the new as follows:
- *
- * * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
- * * GIT_DIRREMOVAL_FILES_AND_DIRS ~= GIT_RMDIR_REMOVE_FILES
- * * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
+ * * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
*/
typedef enum {
GIT_RMDIR_EMPTY_HIERARCHY = 0,
@@ -128,6 +123,7 @@ typedef enum {
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
+ GIT_RMDIR_SKIP_ROOT = (1 << 4),
} git_futils_rmdir_flags;
/**
@@ -141,14 +137,6 @@ typedef enum {
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
/**
- * Remove all files and directories beneath the specified path.
- *
- * @param path Path to the top level directory to process.
- * @return 0 on success; -1 on error.
- */
-extern int git_futils_cleanupdir_r(const char *path);
-
-/**
* Create and open a temporary file with a `_git2_` suffix.
* Writes the filename into path_out.
* @return On success, an open file descriptor, else an error code < 0.
diff --git a/src/global.c b/src/global.c
index 4f024f17e..7d39c6fa8 100644
--- a/src/global.c
+++ b/src/global.c
@@ -73,47 +73,69 @@ static void git__shutdown(void)
#if defined(GIT_THREADS) && defined(GIT_WIN32)
static DWORD _tls_index;
-static int _tls_init = 0;
+static DWORD _mutex = 0;
+static DWORD _n_inits = 0;
-int git_threads_init(void)
+static int synchronized_threads_init()
{
int error;
- if (_tls_init)
- return 0;
-
_tls_index = TlsAlloc();
if (git_mutex_init(&git__mwindow_mutex))
return -1;
/* Initialize any other subsystems that have global state */
- if ((error = git_hash_global_init()) >= 0 &&
- (error = git_futils_dirs_global_init()) >= 0)
- _tls_init = 1;
-
- GIT_MEMORY_BARRIER;
+ if ((error = git_hash_global_init()) >= 0)
+ error = git_futils_dirs_global_init();
win32_pthread_initialize();
return error;
}
-void git_threads_shutdown(void)
+int git_threads_init(void)
+{
+ int error = 0;
+
+ /* Enter the lock */
+ while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
+
+ /* Only do work on a 0 -> 1 transition of the refcount */
+ if (1 == ++_n_inits)
+ error = synchronized_threads_init();
+
+ /* Exit the lock */
+ InterlockedExchange(&_mutex, 0);
+
+ return error;
+}
+
+static void synchronized_threads_shutdown()
{
/* Shut down any subsystems that have global state */
git__shutdown();
-
TlsFree(_tls_index);
- _tls_init = 0;
-
git_mutex_free(&git__mwindow_mutex);
}
+void git_threads_shutdown(void)
+{
+ /* Enter the lock */
+ while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
+
+ /* Only do work on a 1 -> 0 transition of the refcount */
+ if (0 == --_n_inits)
+ synchronized_threads_shutdown();
+
+ /* Exit the lock */
+ InterlockedExchange(&_mutex, 0);
+}
+
git_global_st *git__global_state(void)
{
void *ptr;
- assert(_tls_init);
+ assert(_n_inits);
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
@@ -130,55 +152,58 @@ git_global_st *git__global_state(void)
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
static pthread_key_t _tls_key;
-static int _tls_init = 0;
+static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
+static git_atomic git__n_inits;
+int init_error = 0;
static void cb__free_status(void *st)
{
git__free(st);
}
-int git_threads_init(void)
+static void init_once(void)
{
- int error = 0;
-
- if (_tls_init)
- return 0;
-
- if (git_mutex_init(&git__mwindow_mutex))
- return -1;
+ if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
+ return;
pthread_key_create(&_tls_key, &cb__free_status);
/* Initialize any other subsystems that have global state */
- if ((error = git_hash_global_init()) >= 0 &&
- (error = git_futils_dirs_global_init()) >= 0)
- _tls_init = 1;
+ if ((init_error = git_hash_global_init()) >= 0)
+ init_error = git_futils_dirs_global_init();
GIT_MEMORY_BARRIER;
+}
- return error;
+int git_threads_init(void)
+{
+ pthread_once(&_once_init, init_once);
+ git_atomic_inc(&git__n_inits);
+ return init_error;
}
void git_threads_shutdown(void)
{
+ pthread_once_t new_once = PTHREAD_ONCE_INIT;
+
+ if (git_atomic_dec(&git__n_inits) > 0) return;
+
/* Shut down any subsystems that have global state */
git__shutdown();
- if (_tls_init) {
- void *ptr = pthread_getspecific(_tls_key);
- pthread_setspecific(_tls_key, NULL);
- git__free(ptr);
- }
+ 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);
+ _once_init = new_once;
}
git_global_st *git__global_state(void)
{
void *ptr;
- assert(_tls_init);
+ assert(git__n_inits.val);
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
diff --git a/src/ignore.c b/src/ignore.c
index 0c35d0431..27d7c7ec4 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -1,4 +1,5 @@
#include "git2/ignore.h"
+#include "common.h"
#include "ignore.h"
#include "attr.h"
#include "path.h"
diff --git a/src/index.c b/src/index.c
index 21a8d31d1..5cdd40aaa 100644
--- a/src/index.c
+++ b/src/index.c
@@ -461,9 +461,10 @@ int git_index_read(git_index *index)
return create_index_error(-1,
"Failed to read index: The index is in-memory only");
- if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
+ index->on_disk = git_path_exists(index->index_file_path);
+
+ if (!index->on_disk) {
git_index_clear(index);
- index->on_disk = 0;
return 0;
}
@@ -579,7 +580,8 @@ const git_index_entry *git_index_get_bypath(
return git_index_get_byindex(index, pos);
}
-void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
+void git_index_entry__init_from_stat(
+ git_index_entry *entry, struct stat *st, bool trust_mode)
{
entry->ctime.seconds = (git_time_t)st->st_ctime;
entry->mtime.seconds = (git_time_t)st->st_mtime;
@@ -587,7 +589,8 @@ void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
/* entry->ctime.nanoseconds = st->st_ctimensec; */
entry->dev = st->st_rdev;
entry->ino = st->st_ino;
- entry->mode = index_create_mode(st->st_mode);
+ entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
+ index_create_mode(0666) : index_create_mode(st->st_mode);
entry->uid = st->st_uid;
entry->gid = st->st_gid;
entry->file_size = st->st_size;
@@ -631,7 +634,7 @@ static int index_entry_init(
entry = git__calloc(1, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry);
- git_index_entry__init_from_stat(entry, &st);
+ git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
entry->oid = oid;
entry->path = git__strdup(rel_path);
diff --git a/src/index.h b/src/index.h
index 40577e105..4c448fabf 100644
--- a/src/index.h
+++ b/src/index.h
@@ -48,7 +48,7 @@ struct git_index_conflict_iterator {
};
extern void git_index_entry__init_from_stat(
- git_index_entry *entry, struct stat *st);
+ git_index_entry *entry, struct stat *st, bool trust_mode);
extern size_t git_index__prefix_position(git_index *index, const char *path);
diff --git a/src/indexer.c b/src/indexer.c
index 3b160df5d..9f55c8b2c 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -18,6 +18,7 @@
#include "filebuf.h"
#include "oid.h"
#include "oidmap.h"
+#include "compress.h"
#define UINT31_MAX (0x7FFFFFFF)
@@ -33,6 +34,7 @@ struct git_indexer_stream {
opened_pack :1,
have_stream :1,
have_delta :1;
+ struct git_pack_header hdr;
struct git_pack_file *pack;
git_filebuf pack_file;
git_off_t off;
@@ -48,9 +50,12 @@ struct git_indexer_stream {
void *progress_payload;
char objbuf[8*1024];
+ /* Needed to look up objects which we want to inject to fix a thin pack */
+ git_odb *odb;
+
/* Fields for calculating the packfile trailer (hash of everything before it) */
char inbuf[GIT_OID_RAWSZ];
- int inbuf_len;
+ size_t inbuf_len;
git_hash_ctx trailer;
};
@@ -114,6 +119,7 @@ static int objects_cmp(const void *a, const void *b)
int git_indexer_stream_new(
git_indexer_stream **out,
const char *prefix,
+ git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_payload)
{
@@ -124,6 +130,7 @@ int git_indexer_stream_new(
idx = git__calloc(1, sizeof(git_indexer_stream));
GITERR_CHECK_ALLOC(idx);
+ idx->odb = odb;
idx->progress_cb = progress_cb;
idx->progress_payload = progress_payload;
git_hash_ctx_init(&idx->trailer);
@@ -309,17 +316,10 @@ on_error:
return -1;
}
-static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+static int save_entry(git_indexer_stream *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start)
{
int i, error;
khiter_t k;
- git_oid oid;
- size_t entry_size;
- struct entry *entry;
- struct git_pack_entry *pentry;
-
- entry = git__calloc(1, sizeof(*entry));
- GITERR_CHECK_ALLOC(entry);
if (entry_start > UINT31_MAX) {
entry->offset = UINT32_MAX;
@@ -328,6 +328,34 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
entry->offset = (uint32_t)entry_start;
}
+ pentry->offset = entry_start;
+ k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+ if (!error)
+ return -1;
+
+ kh_value(idx->pack->idx_cache, k) = pentry;
+
+ /* Add the object to the list */
+ if (git_vector_insert(&idx->objects, entry) < 0)
+ return -1;
+
+ for (i = entry->oid.id[0]; i < 256; ++i) {
+ idx->fanout[i]++;
+ }
+
+ return 0;
+}
+
+static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+{
+ git_oid oid;
+ size_t entry_size;
+ struct entry *entry;
+ struct git_pack_entry *pentry;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
+
if (git_odb__hashobj(&oid, obj) < 0) {
giterr_set(GITERR_INDEXER, "Failed to hash object");
goto on_error;
@@ -337,15 +365,6 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
GITERR_CHECK_ALLOC(pentry);
git_oid_cpy(&pentry->sha1, &oid);
- pentry->offset = entry_start;
- k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
- if (!error) {
- git__free(pentry);
- goto on_error;
- }
-
- kh_value(idx->pack->idx_cache, k) = pentry;
-
git_oid_cpy(&entry->oid, &oid);
entry->crc = crc32(0L, Z_NULL, 0);
@@ -353,15 +372,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
goto on_error;
- /* Add the object to the list */
- if (git_vector_insert(&idx->objects, entry) < 0)
- goto on_error;
-
- for (i = oid.id[0]; i < 256; ++i) {
- idx->fanout[i]++;
- }
-
- return 0;
+ return save_entry(idx, entry, pentry, entry_start);
on_error:
git__free(entry);
@@ -378,13 +389,13 @@ static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *
/* Hash everything but the last 20B of input */
static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t size)
{
- int to_expell, to_keep;
+ size_t to_expell, to_keep;
if (size == 0)
return;
/* Easy case, dump the buffer and the data minus the last 20 bytes */
- if (size >= 20) {
+ if (size >= GIT_OID_RAWSZ) {
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
@@ -402,8 +413,8 @@ static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t
}
/* We need to partially drain the buffer and then append */
- to_expell = abs(size - (GIT_OID_RAWSZ - idx->inbuf_len));
- to_keep = abs(idx->inbuf_len - to_expell);
+ to_keep = GIT_OID_RAWSZ - size;
+ to_expell = idx->inbuf_len - to_keep;
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
@@ -415,8 +426,8 @@ static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t
int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
{
int error = -1;
- struct git_pack_header hdr;
size_t processed;
+ struct git_pack_header *hdr = &idx->hdr;
git_mwindow_file *mwf = &idx->pack->mwf;
assert(idx && data && stats);
@@ -443,14 +454,14 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (!idx->parsed_header) {
unsigned int total_objects;
- if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
+ if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
return 0;
- if (parse_header(&hdr, idx->pack) < 0)
+ if (parse_header(&idx->hdr, idx->pack) < 0)
return -1;
idx->parsed_header = 1;
- idx->nr_objects = ntohl(hdr.hdr_entries);
+ idx->nr_objects = ntohl(hdr->hdr_entries);
idx->off = sizeof(struct git_pack_header);
/* for now, limit to 2^32 objects */
@@ -471,6 +482,9 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
return -1;
stats->received_objects = 0;
+ stats->local_objects = 0;
+ stats->total_deltas = 0;
+ stats->indexed_deltas = 0;
processed = stats->indexed_objects = 0;
stats->total_objects = total_objects;
do_progress_callback(idx, stats);
@@ -556,6 +570,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
stats->received_objects++;
if (do_progress_callback(idx, stats) != 0) {
+ giterr_clear();
error = GIT_EUSER;
goto on_error;
}
@@ -590,24 +605,229 @@ static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char
return git_buf_oom(path) ? -1 : 0;
}
-static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
+/**
+ * Rewind the packfile by the trailer, as we might need to fix the
+ * packfile by injecting objects at the tail and must overwrite it.
+ */
+static git_off_t seek_back_trailer(git_indexer_stream *idx)
+{
+ git_off_t off;
+
+ if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
+ return -1;
+
+ idx->pack->mwf.size -= GIT_OID_RAWSZ;
+ git_mwindow_free_all(&idx->pack->mwf);
+
+ return off;
+}
+
+static int inject_object(git_indexer_stream *idx, git_oid *id)
+{
+ git_odb_object *obj;
+ struct entry *entry;
+ struct git_pack_entry *pentry;
+ git_oid foo = {{0}};
+ unsigned char hdr[64];
+ git_buf buf = GIT_BUF_INIT;
+ git_off_t entry_start;
+ const void *data;
+ size_t len, hdr_len;
+ int error;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ entry_start = seek_back_trailer(idx);
+
+ if (git_odb_read(&obj, idx->odb, id) < 0)
+ return -1;
+
+ data = git_odb_object_data(obj);
+ len = git_odb_object_size(obj);
+
+ entry->crc = crc32(0L, Z_NULL, 0);
+
+ /* Write out the object header */
+ hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
+ git_filebuf_write(&idx->pack_file, hdr, hdr_len);
+ idx->pack->mwf.size += hdr_len;
+ entry->crc = crc32(entry->crc, hdr, hdr_len);
+
+ if ((error = git__compress(&buf, data, len)) < 0)
+ goto cleanup;
+
+ /* And then the compressed object */
+ git_filebuf_write(&idx->pack_file, buf.ptr, buf.size);
+ idx->pack->mwf.size += buf.size;
+ entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, buf.size));
+ git_buf_free(&buf);
+
+ /* Write a fake trailer so the pack functions play ball */
+ if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
+ goto cleanup;
+
+ idx->pack->mwf.size += GIT_OID_RAWSZ;
+
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
+ GITERR_CHECK_ALLOC(pentry);
+
+ git_oid_cpy(&pentry->sha1, id);
+ git_oid_cpy(&entry->oid, id);
+ idx->off = entry_start + hdr_len + len;
+
+ if ((error = save_entry(idx, entry, pentry, entry_start)) < 0)
+ git__free(pentry);
+
+cleanup:
+ git_odb_object_free(obj);
+ return error;
+}
+
+static int fix_thin_pack(git_indexer_stream *idx, git_transfer_progress *stats)
{
+ int error, found_ref_delta = 0;
unsigned int i;
struct delta_info *delta;
+ size_t size;
+ git_otype type;
+ git_mwindow *w = NULL;
+ git_off_t curpos;
+ unsigned char *base_info;
+ unsigned int left = 0;
+ git_oid base;
+
+ assert(git_vector_length(&idx->deltas) > 0);
+ if (idx->odb == NULL) {
+ giterr_set(GITERR_INDEXER, "cannot fix a thin pack without an ODB");
+ return -1;
+ }
+
+ /* Loop until we find the first REF delta */
git_vector_foreach(&idx->deltas, i, delta) {
- git_rawobj obj;
+ curpos = delta->delta_off;
+ error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
+ git_mwindow_close(&w);
+ if (error < 0)
+ return error;
+
+ if (type == GIT_OBJ_REF_DELTA) {
+ found_ref_delta = 1;
+ break;
+ }
+ }
+
+ if (!found_ref_delta) {
+ giterr_set(GITERR_INDEXER, "no REF_DELTA found, cannot inject object");
+ return -1;
+ }
- idx->off = delta->delta_off;
- if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+ /* curpos now points to the base information, which is an OID */
+ base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left);
+ if (base_info == NULL) {
+ giterr_set(GITERR_INDEXER, "failed to map delta information");
+ return -1;
+ }
+
+ git_oid_fromraw(&base, base_info);
+ git_mwindow_close(&w);
+
+ if (inject_object(idx, &base) < 0)
+ return -1;
+
+ stats->local_objects++;
+
+ return 0;
+}
+
+static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
+{
+ unsigned int i;
+ struct delta_info *delta;
+ int progressed = 0;
+
+ while (idx->deltas.length > 0) {
+ progressed = 0;
+ git_vector_foreach(&idx->deltas, i, delta) {
+ git_rawobj obj;
+
+ idx->off = delta->delta_off;
+ if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+ continue;
+
+ if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ continue;
+
+ git__free(obj.data);
+ stats->indexed_objects++;
+ stats->indexed_deltas++;
+ progressed = 1;
+ do_progress_callback(idx, stats);
+
+ /*
+ * Remove this delta from the list and
+ * decrease i so we don't skip over the next
+ * delta.
+ */
+ git_vector_remove(&idx->deltas, i);
+ git__free(delta);
+ i--;
+ }
+
+ if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
+ giterr_set(GITERR_INDEXER, "missing delta bases");
return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int update_header_and_rehash(git_indexer_stream *idx, git_transfer_progress *stats)
+{
+ void *ptr;
+ size_t chunk = 1024*1024;
+ git_off_t hashed = 0;
+ git_mwindow *w = NULL;
+ git_mwindow_file *mwf;
+ unsigned int left;
+ git_hash_ctx *ctx;
+
+ mwf = &idx->pack->mwf;
+ ctx = &idx->trailer;
+
+ git_hash_ctx_init(ctx);
+ git_mwindow_free_all(mwf);
+
+ /* Update the header to include the numer of local objects we injected */
+ idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
+ if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
+ giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
+ return -1;
+ }
+
+ if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
+ giterr_set(GITERR_OS, "failed to update the pack header");
+ return -1;
+ }
- if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ /*
+ * We now use the same technique as before to determine the
+ * hash. We keep reading up to the end and let
+ * hash_partially() keep the existing trailer out of the
+ * calculation.
+ */
+ idx->inbuf_len = 0;
+ while (hashed < mwf->size) {
+ ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
+ if (ptr == NULL)
return -1;
- git__free(obj.data);
- stats->indexed_objects++;
- do_progress_callback(idx, stats);
+ hash_partially(idx, ptr, left);
+ hashed += left;
+
+ git_mwindow_close(&w);
}
return 0;
@@ -650,15 +870,31 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
return -1;
}
- if (idx->deltas.length > 0)
- if (resolve_deltas(idx, stats) < 0)
- return -1;
+ /* Freeze the number of deltas */
+ stats->total_deltas = stats->total_objects - stats->indexed_objects;
+
+ if (resolve_deltas(idx, stats) < 0)
+ return -1;
if (stats->indexed_objects != stats->total_objects) {
giterr_set(GITERR_INDEXER, "early EOF");
return -1;
}
+ if (stats->local_objects > 0) {
+ if (update_header_and_rehash(idx, stats) < 0)
+ return -1;
+
+ git_hash_final(&trailer_hash, &idx->trailer);
+ if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
+ return -1;
+
+ if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
+ giterr_set(GITERR_OS, "failed to update pack trailer");
+ return -1;
+ }
+ }
+
git_vector_sort(&idx->objects);
git_buf_sets(&filename, idx->pack->pack_name);
diff --git a/src/iterator.c b/src/iterator.c
index bdc98d22b..c0d7862ff 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -893,6 +893,7 @@ struct fs_iterator {
git_index_entry entry;
git_buf path;
size_t root_len;
+ uint32_t dirload_flags;
int depth;
int (*enter_dir_cb)(fs_iterator *self);
@@ -986,7 +987,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
GITERR_CHECK_ALLOC(ff);
error = git_path_dirload_with_stat(
- fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
+ fi->path.ptr, fi->root_len, fi->dirload_flags,
fi->base.start, fi->base.end, &ff->entries);
if (error < 0) {
@@ -1174,7 +1175,7 @@ static int fs_iterator__update_entry(fs_iterator *fi)
return GIT_ITEROVER;
fi->entry.path = ps->path;
- git_index_entry__init_from_stat(&fi->entry, &ps->st);
+ git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
/* need different mode here to keep directories during iteration */
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
@@ -1207,6 +1208,11 @@ static int fs_iterator__initialize(
}
fi->root_len = fi->path.size;
+ fi->dirload_flags =
+ (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
+ (iterator__flag(fi, PRECOMPOSE_UNICODE) ?
+ GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
+
if ((error = fs_iterator__expand_dir(fi)) < 0) {
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
giterr_clear();
@@ -1329,7 +1335,7 @@ int git_iterator_for_workdir_ext(
const char *start,
const char *end)
{
- int error;
+ int error, precompose = 0;
workdir_iterator *wi;
if (!repo_workdir) {
@@ -1356,6 +1362,12 @@ int git_iterator_for_workdir_ext(
return error;
}
+ /* try to look up precompose and set flag if appropriate */
+ if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
+ giterr_clear();
+ else if (precompose)
+ wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
}
diff --git a/src/iterator.h b/src/iterator.h
index ea88fa6a2..751e139d0 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -24,13 +24,15 @@ typedef enum {
typedef enum {
/** ignore case for entry sort order */
- GIT_ITERATOR_IGNORE_CASE = (1 << 0),
+ GIT_ITERATOR_IGNORE_CASE = (1u << 0),
/** force case sensitivity for entry sort order */
- GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
+ GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
/** return tree items in addition to blob items */
- GIT_ITERATOR_INCLUDE_TREES = (1 << 2),
+ GIT_ITERATOR_INCLUDE_TREES = (1u << 2),
/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
- GIT_ITERATOR_DONT_AUTOEXPAND = (1 << 3),
+ GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
+ /** convert precomposed unicode to decomposed unicode */
+ GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
} git_iterator_flag_t;
typedef struct {
diff --git a/src/merge_file.c b/src/merge_file.c
index c3477ccb9..48fc46e57 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -47,7 +47,7 @@ GIT_INLINE(int) merge_file_best_mode(
* assume executable. Otherwise, if any mode changed from the ancestor,
* use that one.
*/
- if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
+ 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;
diff --git a/src/odb.c b/src/odb.c
index b2c138aae..2d6eda881 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -124,6 +124,13 @@ git_otype git_odb_object_type(git_odb_object *object)
return object->cached.type;
}
+int git_odb_object_dup(git_odb_object **dest, git_odb_object *source)
+{
+ git_cached_obj_incref(source);
+ *dest = source;
+ return 0;
+}
+
void git_odb_object_free(git_odb_object *object)
{
if (object == NULL)
@@ -988,7 +995,7 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer
if (b->writepack != NULL) {
++writes;
- error = b->writepack(out, b, progress_cb, progress_payload);
+ error = b->writepack(out, b, db, progress_cb, progress_payload);
}
}
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 4ff57158d..07dfae539 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -544,7 +544,7 @@ static int locate_object_short_oid(
/* Explore directory to find a unique object matching short_oid */
error = git_path_direach(
- object_location, fn_locate_object_short_oid, &state);
+ object_location, 0, fn_locate_object_short_oid, &state);
if (error && error != GIT_EUSER)
return error;
@@ -745,7 +745,7 @@ static int foreach_cb(void *_state, git_buf *path)
{
struct foreach_state *state = (struct foreach_state *) _state;
- return git_path_direach(path, foreach_object_dir_cb, state);
+ return git_path_direach(path, 0, foreach_object_dir_cb, state);
}
static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
@@ -768,7 +768,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb
state.data = data;
state.dir_len = git_buf_len(&buf);
- error = git_path_direach(&buf, foreach_cb, &state);
+ error = git_path_direach(&buf, 0, foreach_cb, &state);
git_buf_free(&buf);
diff --git a/src/odb_pack.c b/src/odb_pack.c
index cadc93a65..4c7c82253 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -331,7 +331,7 @@ static int pack_backend__refresh(git_odb_backend *_backend)
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
- error = git_path_direach(&path, packfile_load__cb, (void *)backend);
+ error = git_path_direach(&path, 0, packfile_load__cb, backend);
git_buf_free(&path);
@@ -541,6 +541,7 @@ static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
static int pack_backend__writepack(struct git_odb_writepack **out,
git_odb_backend *_backend,
+ git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_payload)
{
@@ -557,7 +558,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
GITERR_CHECK_ALLOC(writepack);
if (git_indexer_stream_new(&writepack->indexer_stream,
- backend->pack_folder, progress_cb, progress_payload) < 0) {
+ backend->pack_folder, odb, progress_cb, progress_payload) < 0) {
git__free(writepack);
return -1;
}
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 2a2f36223..93541e974 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -216,52 +216,22 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
assert(ret != 0);
kh_value(pb->object_ix, pos) = po;
+ pb->done = false;
+
if (pb->progress_cb) {
double current_time = git__timer();
if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
pb->last_progress_report_time = current_time;
- pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload);
+ if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) {
+ giterr_clear();
+ return GIT_EUSER;
+ }
}
}
- pb->done = false;
return 0;
}
-/*
- * The per-object header is a pretty dense thing, which is
- * - first byte: low four bits are "size",
- * then three bits of "type",
- * with the high bit being "size continues".
- * - each byte afterwards: low seven bits are size continuation,
- * with the high bit being "size continues"
- */
-static int gen_pack_object_header(
- unsigned char *hdr,
- unsigned long size,
- git_otype type)
-{
- unsigned char *hdr_base;
- unsigned char c;
-
- assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
-
- /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
-
- c = (unsigned char)((type << 4) | (size & 15));
- size >>= 4;
- hdr_base = hdr;
-
- while (size) {
- *hdr++ = c | 0x80;
- c = size & 0x7f;
- size >>= 7;
- }
- *hdr++ = c;
-
- return (int)(hdr - hdr_base);
-}
-
static int get_delta(void **out, git_odb *odb, git_pobject *po)
{
git_odb_object *src = NULL, *trg = NULL;
@@ -323,7 +293,7 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po)
}
/* Write header */
- hdr_len = gen_pack_object_header(hdr, size, type);
+ hdr_len = git_packfile__object_header(hdr, size, type);
if (git_buf_put(buf, (char *)hdr, hdr_len) < 0)
goto on_error;
@@ -591,49 +561,50 @@ static int write_pack(git_packbuilder *pb,
enum write_one_status status;
struct git_pack_header ph;
unsigned int i = 0;
+ int error = 0;
write_order = compute_write_order(pb);
- if (write_order == NULL)
- goto on_error;
+ if (write_order == NULL) {
+ error = -1;
+ goto done;
+ }
/* Write pack header */
ph.hdr_signature = htonl(PACK_SIGNATURE);
ph.hdr_version = htonl(PACK_VERSION);
ph.hdr_entries = htonl(pb->nr_objects);
- if (cb(&ph, sizeof(ph), data) < 0)
- goto on_error;
+ if ((error = cb(&ph, sizeof(ph), data)) < 0)
+ goto done;
- if (git_hash_update(&pb->ctx, &ph, sizeof(ph)) < 0)
- goto on_error;
+ if ((error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
+ goto done;
pb->nr_remaining = pb->nr_objects;
do {
pb->nr_written = 0;
for ( ; i < pb->nr_objects; ++i) {
po = write_order[i];
- if (write_one(&buf, pb, po, &status) < 0)
- goto on_error;
- if (cb(buf.ptr, buf.size, data) < 0)
- goto on_error;
+ if ((error = write_one(&buf, pb, po, &status)) < 0)
+ goto done;
+ if ((error = cb(buf.ptr, buf.size, data)) < 0)
+ goto done;
git_buf_clear(&buf);
}
pb->nr_remaining -= pb->nr_written;
} while (pb->nr_remaining && i < pb->nr_objects);
- git__free(write_order);
- git_buf_free(&buf);
- if (git_hash_final(&pb->pack_oid, &pb->ctx) < 0)
- goto on_error;
+ if ((error = git_hash_final(&pb->pack_oid, &pb->ctx)) < 0)
+ goto done;
- return cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
+ error = cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
-on_error:
+done:
git__free(write_order);
git_buf_free(&buf);
- return -1;
+ return error;
}
static int write_pack_buf(void *buf, size_t size, void *data)
@@ -1287,7 +1258,7 @@ int git_packbuilder_write(
PREPARE_PACK;
if (git_indexer_stream_new(
- &indexer, path, progress_cb, progress_cb_payload) < 0)
+ &indexer, path, pb->odb, progress_cb, progress_cb_payload) < 0)
return -1;
ctx.indexer = indexer;
diff --git a/src/pack.c b/src/pack.c
index e7fb9f1ae..5df0f50b9 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -364,6 +364,38 @@ static unsigned char *pack_window_open(
return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
}
+/*
+ * The per-object header is a pretty dense thing, which is
+ * - first byte: low four bits are "size",
+ * then three bits of "type",
+ * with the high bit being "size continues".
+ * - each byte afterwards: low seven bits are size continuation,
+ * with the high bit being "size continues"
+ */
+int git_packfile__object_header(unsigned char *hdr, unsigned long size, git_otype type)
+{
+ unsigned char *hdr_base;
+ unsigned char c;
+
+ assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
+
+ /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
+
+ c = (unsigned char)((type << 4) | (size & 15));
+ size >>= 4;
+ hdr_base = hdr;
+
+ while (size) {
+ *hdr++ = c | 0x80;
+ c = size & 0x7f;
+ size >>= 7;
+ }
+ *hdr++ = c;
+
+ return (int)(hdr - hdr_base);
+}
+
+
static int packfile_unpack_header1(
unsigned long *usedp,
size_t *sizep,
diff --git a/src/pack.h b/src/pack.h
index aeeac9ce1..ddeefea1d 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -112,6 +112,8 @@ typedef struct git_packfile_stream {
git_mwindow *mw;
} git_packfile_stream;
+int git_packfile__object_header(unsigned char *hdr, unsigned long size, git_otype type);
+
int git_packfile_unpack_header(
size_t *size_p,
git_otype *type_p,
diff --git a/src/path.c b/src/path.c
index 9d8e90361..d45751cd1 100644
--- a/src/path.c
+++ b/src/path.c
@@ -483,23 +483,23 @@ bool git_path_isfile(const char *path)
bool git_path_is_empty_dir(const char *path)
{
- git_buf pathbuf = GIT_BUF_INIT;
HANDLE hFind = INVALID_HANDLE_VALUE;
git_win32_path wbuf;
+ int wbufsz;
WIN32_FIND_DATAW ffd;
bool retval = true;
- if (!git_path_isdir(path)) return false;
+ if (!git_path_isdir(path))
+ return false;
- git_buf_printf(&pathbuf, "%s\\*", path);
- git_win32_path_from_c(wbuf, git_buf_cstr(&pathbuf));
+ wbufsz = git_win32_path_from_c(wbuf, path);
+ if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16)
+ return false;
+ memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t));
hFind = FindFirstFileW(wbuf, &ffd);
- if (INVALID_HANDLE_VALUE == hFind) {
- giterr_set(GITERR_OS, "Couldn't open '%s'", path);
- git_buf_free(&pathbuf);
+ if (INVALID_HANDLE_VALUE == hFind)
return false;
- }
do {
if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
@@ -509,50 +509,64 @@ bool git_path_is_empty_dir(const char *path)
} while (FindNextFileW(hFind, &ffd) != 0);
FindClose(hFind);
- git_buf_free(&pathbuf);
return retval;
}
#else
-bool git_path_is_empty_dir(const char *path)
+static int path_found_entry(void *payload, git_buf *path)
{
- DIR *dir = NULL;
- struct dirent *e;
- bool retval = true;
+ GIT_UNUSED(payload);
+ return !git_path_is_dot_or_dotdot(path->ptr);
+}
- if (!git_path_isdir(path)) return false;
+bool git_path_is_empty_dir(const char *path)
+{
+ int error;
+ git_buf dir = GIT_BUF_INIT;
- dir = opendir(path);
- if (!dir) {
- giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+ if (!git_path_isdir(path))
return false;
- }
- while ((e = readdir(dir)) != NULL) {
- if (!git_path_is_dot_or_dotdot(e->d_name)) {
- giterr_set(GITERR_INVALID,
- "'%s' exists and is not an empty directory", path);
- retval = false;
- break;
- }
- }
- closedir(dir);
+ if (!(error = git_buf_sets(&dir, path)))
+ error = git_path_direach(&dir, 0, path_found_entry, NULL);
- return retval;
+ git_buf_free(&dir);
+
+ return !error;
}
+
#endif
-int git_path_lstat(const char *path, struct stat *st)
+int git_path_set_error(int errno_value, const char *path, const char *action)
{
- int err = 0;
-
- if (p_lstat(path, st) < 0) {
- err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
- giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+ switch (errno_value) {
+ case ENOENT:
+ case ENOTDIR:
+ giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
+ return GIT_ENOTFOUND;
+
+ case EINVAL:
+ case ENAMETOOLONG:
+ giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
+ return GIT_EINVALIDSPEC;
+
+ case EEXIST:
+ giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
+ return GIT_EEXISTS;
+
+ default:
+ giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
+ return -1;
}
+}
+
+int git_path_lstat(const char *path, struct stat *st)
+{
+ if (p_lstat(path, st) == 0)
+ return 0;
- return err;
+ return git_path_set_error(errno, path, "stat");
}
static bool _check_dir_contents(
@@ -724,14 +738,103 @@ int git_path_cmp(
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
+bool git_path_has_non_ascii(const char *path, size_t pathlen)
+{
+ const uint8_t *scan = (const uint8_t *)path, *end;
+
+ for (end = scan + pathlen; scan < end; ++scan)
+ if (*scan & 0x80)
+ return true;
+
+ return false;
+}
+
+#ifdef GIT_USE_ICONV
+
+int git_path_iconv_init_precompose(git_path_iconv_t *ic)
+{
+ git_buf_init(&ic->buf, 0);
+ ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
+ return 0;
+}
+
+void git_path_iconv_clear(git_path_iconv_t *ic)
+{
+ if (ic) {
+ if (ic->map != (iconv_t)-1)
+ iconv_close(ic->map);
+ git_buf_free(&ic->buf);
+ }
+}
+
+int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
+{
+ char *nfd = *in, *nfc;
+ size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, rv;
+ int retry = 1;
+
+ if (!ic || ic->map == (iconv_t)-1 ||
+ !git_path_has_non_ascii(*in, *inlen))
+ return 0;
+
+ while (1) {
+ if (git_buf_grow(&ic->buf, wantlen) < 0)
+ return -1;
+
+ nfc = ic->buf.ptr + ic->buf.size;
+ nfclen = ic->buf.asize - ic->buf.size;
+
+ rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
+
+ ic->buf.size = (nfc - ic->buf.ptr);
+
+ if (rv != (size_t)-1)
+ break;
+
+ if (errno != E2BIG)
+ goto fail;
+
+ /* make space for 2x the remaining data to be converted
+ * (with per retry overhead to avoid infinite loops)
+ */
+ wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
+
+ if (retry++ > 4)
+ goto fail;
+ }
+
+ ic->buf.ptr[ic->buf.size] = '\0';
+
+ *in = ic->buf.ptr;
+ *inlen = ic->buf.size;
+
+ return 0;
+
+fail:
+ giterr_set(GITERR_OS, "Unable to convert unicode path data");
+ return -1;
+}
+
+#endif
+
+#if defined(__sun) || defined(__GNU__)
+typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
+#else
+typedef struct dirent path_dirent_data;
+#endif
+
int git_path_direach(
git_buf *path,
+ uint32_t flags,
int (*fn)(void *, git_buf *),
void *arg)
{
+ int error = 0;
ssize_t wd_len;
DIR *dir;
- struct dirent *de, *de_buf;
+ path_dirent_data de_data;
+ struct dirent *de, *de_buf = (struct dirent *)&de_data;
+ git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
if (git_path_to_dir(path) < 0)
return -1;
@@ -743,99 +846,98 @@ int git_path_direach(
return -1;
}
-#if defined(__sun) || defined(__GNU__)
- de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
-#else
- de_buf = git__malloc(sizeof(struct dirent));
-#endif
+ if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_path_iconv_init_precompose(&ic);
while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
- int result;
+ char *de_path = de->d_name;
+ size_t de_len = strlen(de_path);
- if (git_path_is_dot_or_dotdot(de->d_name))
+ if (git_path_is_dot_or_dotdot(de_path))
continue;
- if (git_buf_puts(path, de->d_name) < 0) {
- closedir(dir);
- git__free(de_buf);
- return -1;
- }
+ if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0 ||
+ (error = git_buf_put(path, de_path, de_len)) < 0)
+ break;
- result = fn(arg, path);
+ error = fn(arg, path);
git_buf_truncate(path, wd_len); /* restore path */
- if (result) {
- closedir(dir);
- git__free(de_buf);
- return GIT_EUSER;
+ if (error) {
+ error = GIT_EUSER;
+ break;
}
}
closedir(dir);
- git__free(de_buf);
- return 0;
+ git_path_iconv_clear(&ic);
+
+ return error;
}
int git_path_dirload(
const char *path,
size_t prefix_len,
size_t alloc_extra,
+ unsigned int flags,
git_vector *contents)
{
int error, need_slash;
DIR *dir;
- struct dirent *de, *de_buf;
size_t path_len;
+ path_dirent_data de_data;
+ struct dirent *de, *de_buf = (struct dirent *)&de_data;
+ git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+
+ assert(path && contents);
- assert(path != NULL && contents != NULL);
path_len = strlen(path);
- assert(path_len > 0 && path_len >= prefix_len);
+ if (!path_len || path_len < prefix_len) {
+ giterr_set(GITERR_INVALID, "Invalid directory path '%s'", path);
+ return -1;
+ }
if ((dir = opendir(path)) == NULL) {
giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
return -1;
}
-#if defined(__sun) || defined(__GNU__)
- de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
-#else
- de_buf = git__malloc(sizeof(struct dirent));
-#endif
+ if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_path_iconv_init_precompose(&ic);
path += prefix_len;
path_len -= prefix_len;
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
- char *entry_path;
- size_t entry_len;
+ char *entry_path, *de_path = de->d_name;
+ size_t alloc_size, de_len = strlen(de_path);
- if (git_path_is_dot_or_dotdot(de->d_name))
+ if (git_path_is_dot_or_dotdot(de_path))
continue;
- entry_len = strlen(de->d_name);
+ if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
+ break;
- entry_path = git__malloc(
- path_len + need_slash + entry_len + 1 + alloc_extra);
- GITERR_CHECK_ALLOC(entry_path);
+ alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
+ if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
+ error = -1;
+ break;
+ }
if (path_len)
memcpy(entry_path, path, path_len);
if (need_slash)
entry_path[path_len] = '/';
- memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
- entry_path[path_len + need_slash + entry_len] = '\0';
+ memcpy(&entry_path[path_len + need_slash], de_path, de_len);
- if (git_vector_insert(contents, entry_path) < 0) {
- closedir(dir);
- git__free(de_buf);
- return -1;
- }
+ if ((error = git_vector_insert(contents, entry_path)) < 0)
+ break;
}
closedir(dir);
- git__free(de_buf);
+ git_path_iconv_clear(&ic);
if (error != 0)
giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
@@ -858,7 +960,7 @@ int git_path_with_stat_cmp_icase(const void *a, const void *b)
int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
- bool ignore_case,
+ unsigned int flags,
const char *start_stat,
const char *end_stat,
git_vector *contents)
@@ -875,13 +977,14 @@ int git_path_dirload_with_stat(
return -1;
error = git_path_dirload(
- path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
+ path, prefix_len, sizeof(git_path_with_stat) + 1, flags, contents);
if (error < 0) {
git_buf_free(&full);
return error;
}
- strncomp = ignore_case ? git__strncasecmp : git__strncmp;
+ strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
+ git__strncasecmp : git__strncmp;
/* stat struct at start of git_path_with_stat, so shift path text */
git_vector_foreach(contents, i, ps) {
diff --git a/src/path.h b/src/path.h
index b2899e97f..17f4f7726 100644
--- a/src/path.h
+++ b/src/path.h
@@ -242,21 +242,28 @@ extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
*/
extern int git_path_apply_relative(git_buf *target, const char *relpath);
+enum {
+ GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
+ GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
+};
+
/**
* Walk each directory entry, except '.' and '..', calling fn(state).
*
- * @param pathbuf buffer the function reads the initial directory
+ * @param pathbuf Buffer the function reads the initial directory
* path from, and updates with each successive entry's name.
- * @param fn function to invoke with each entry. The first arg is
- * the input state and the second arg is pathbuf. The function
- * may modify the pathbuf, but only by appending new text.
- * @param state to pass to fn as the first arg.
+ * @param flags Combination of GIT_PATH_DIR flags.
+ * @param callback Callback for each entry. Passed the `payload` and each
+ * successive path inside the directory as a full path. This may
+ * safely append text to the pathbuf if needed.
+ * @param payload Passed to callback as first argument.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/
extern int git_path_direach(
git_buf *pathbuf,
- int (*fn)(void *, git_buf *),
- void *state);
+ uint32_t flags,
+ int (*callback)(void *payload, git_buf *path),
+ void *payload);
/**
* Sort function to order two paths
@@ -276,19 +283,19 @@ extern int git_path_cmp(
* @param pathbuf Buffer the function reads the directory from and
* and updates with each successive name.
* @param ceiling Prefix of path at which to stop walking up. If NULL,
- * this will walk all the way up to the root. If not a prefix of
- * pathbuf, the callback will be invoked a single time on the
- * original input path.
- * @param fn Function to invoke on each path. The first arg is the
- * input satte and the second arg is the pathbuf. The function
- * should not modify the pathbuf.
+ * this will walk all the way up to the root. If not a prefix of
+ * pathbuf, the callback will be invoked a single time on the
+ * original input path.
+ * @param callback Function to invoke on each path. Passed the `payload`
+ * and the buffer containing the current path. The path should not
+ * be modified in any way.
* @param state Passed to fn as the first ath.
*/
extern int git_path_walk_up(
git_buf *pathbuf,
const char *ceiling,
- int (*fn)(void *state, git_buf *),
- void *state);
+ int (*callback)(void *payload, git_buf *path),
+ void *payload);
/**
* Load all directory entries (except '.' and '..') into a vector.
@@ -304,12 +311,14 @@ extern int git_path_walk_up(
* prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
* @param alloc_extra Extra bytes to add to each string allocation in
* case you want to append anything funny.
+ * @param flags Combination of GIT_PATH_DIR flags.
* @param contents Vector to fill with directory entry names.
*/
extern int git_path_dirload(
const char *path,
size_t prefix_len,
size_t alloc_extra,
+ uint32_t flags,
git_vector *contents);
@@ -336,7 +345,7 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
*
* @param path The directory to read from
* @param prefix_len The trailing part of path to prefix to entry paths
- * @param ignore_case How to sort and compare paths with start/end limits
+ * @param flags GIT_PATH_DIR flags from above
* @param start_stat As optimization, only stat values after this prefix
* @param end_stat As optimization, only stat values before this prefix
* @param contents Vector to fill with git_path_with_stat structures
@@ -344,9 +353,88 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
extern int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
- bool ignore_case,
+ uint32_t flags,
const char *start_stat,
const char *end_stat,
git_vector *contents);
+enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
+
+/*
+ * Determines if a path is equal to or potentially a child of another.
+ * @param parent The possible parent
+ * @param child The possible child
+ */
+GIT_INLINE(int) git_path_equal_or_prefixed(
+ const char *parent,
+ const char *child)
+{
+ const char *p = parent, *c = child;
+
+ while (*p && *c) {
+ if (*p++ != *c++)
+ return GIT_PATH_NOTEQUAL;
+ }
+
+ if (*p != '\0')
+ return GIT_PATH_NOTEQUAL;
+ if (*c == '\0')
+ return GIT_PATH_EQUAL;
+ if (*c == '/')
+ return GIT_PATH_PREFIX;
+
+ return GIT_PATH_NOTEQUAL;
+}
+
+/* translate errno to libgit2 error code and set error message */
+extern int git_path_set_error(
+ int errno_value, const char *path, const char *action);
+
+/* check if non-ascii characters are present in filename */
+extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
+
+#define GIT_PATH_REPO_ENCODING "UTF-8"
+
+#ifdef __APPLE__
+#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
+#else
+#define GIT_PATH_NATIVE_ENCODING "UTF-8"
+#endif
+
+#ifdef GIT_USE_ICONV
+
+#include <iconv.h>
+
+typedef struct {
+ iconv_t map;
+ git_buf buf;
+} git_path_iconv_t;
+
+#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
+
+/* Init iconv data for converting decomposed UTF-8 to precomposed */
+extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
+
+/* Clear allocated iconv data */
+extern void git_path_iconv_clear(git_path_iconv_t *ic);
+
+/*
+ * Rewrite `in` buffer using iconv map if necessary, replacing `in`
+ * pointer internal iconv buffer if rewrite happened. The `in` pointer
+ * will be left unchanged if no rewrite was needed.
+ */
+extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
+
+#else
+
+typedef struct {
+ int unused;
+} git_path_iconv_t;
+#define GIT_PATH_ICONV_INIT { 0 }
+#define git_path_iconv_init_precompose(X) 0
+#define git_path_iconv_clear(X) (void)(X)
+#define git_path_iconv(X,Y,Z) 0
+
+#endif /* GIT_USE_ICONV */
+
#endif
diff --git a/src/pathspec.c b/src/pathspec.c
index d56d03918..1e7e65e90 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -585,7 +585,7 @@ int git_pathspec_match_tree(
int git_pathspec_match_diff(
git_pathspec_match_list **out,
- git_diff_list *diff,
+ git_diff *diff,
uint32_t flags,
git_pathspec *ps)
{
diff --git a/src/push.c b/src/push.c
index 698079253..a799db8d0 100644
--- a/src/push.c
+++ b/src/push.c
@@ -582,7 +582,7 @@ static int calculate_work(git_push *push)
static int do_push(git_push *push)
{
- int error;
+ int error = 0;
git_transport *transport = push->remote->transport;
if (!transport->push) {
@@ -611,8 +611,6 @@ static int do_push(git_push *push)
(error = transport->push(transport, push)) < 0)
goto on_error;
- error = 0;
-
on_error:
git_packbuilder_free(push->pb);
return error;
diff --git a/src/refdb.c b/src/refdb.c
index 7df61a577..adb58806e 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -16,6 +16,7 @@
#include "hash.h"
#include "refdb.h"
#include "refs.h"
+#include "reflog.h"
int git_refdb_new(git_refdb **out, git_repository *repo)
{
@@ -203,3 +204,18 @@ int git_refdb_delete(struct git_refdb *db, const char *ref_name)
assert(db && db->backend);
return db->backend->del(db->backend, ref_name);
}
+
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
+{
+ int error;
+
+ assert(db && db->backend);
+
+ if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(db);
+ (*out)->db = db;
+
+ return 0;
+}
diff --git a/src/refdb.h b/src/refdb.h
index 3aea37b62..0ee60d911 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -43,4 +43,8 @@ void git_refdb_iterator_free(git_reference_iterator *iter);
int git_refdb_write(git_refdb *refdb, git_reference *ref, int force);
int git_refdb_delete(git_refdb *refdb, const char *ref_name);
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
+int git_refdb_reflog_write(git_reflog *reflog);
+
+
#endif
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 0ba711eb7..7ce09ba55 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -16,12 +16,14 @@
#include "refdb_fs.h"
#include "iterator.h"
#include "sortedcache.h"
+#include "signature.h"
#include <git2/tag.h>
#include <git2/object.h>
#include <git2/refdb.h>
#include <git2/sys/refdb_backend.h>
#include <git2/sys/refs.h>
+#include <git2/sys/reflog.h>
GIT__USE_STRMAP;
@@ -56,6 +58,8 @@ typedef struct refdb_fs_backend {
git_sortedcache *refcache;
int peeling_mode;
+ git_iterator_flag_t iterator_flags;
+ uint32_t direach_flags;
} refdb_fs_backend;
static int packref_cmp(const void *a_, const void *b_)
@@ -269,7 +273,8 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
return 0;
if (git_path_isdir(full_path->ptr))
- return git_path_direach(full_path, _dirent_loose_load, backend);
+ return git_path_direach(
+ full_path, backend->direach_flags, _dirent_loose_load, backend);
file_path = full_path->ptr + strlen(backend->path);
@@ -295,7 +300,8 @@ static int packed_loadloose(refdb_fs_backend *backend)
* This will overwrite any old packed entries with their
* updated loose versions
*/
- error = git_path_direach(&refs_path, _dirent_loose_load, backend);
+ error = git_path_direach(
+ &refs_path, backend->direach_flags, _dirent_loose_load, backend);
git_buf_free(&refs_path);
@@ -468,7 +474,7 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
(error = git_iterator_for_filesystem(
- &fsit, git_buf_cstr(&path), 0, NULL, NULL)) < 0) {
+ &fsit, path.ptr, backend->iterator_flags, NULL, NULL)) < 0) {
git_buf_free(&path);
return error;
}
@@ -1067,10 +1073,350 @@ static int setup_namespace(git_buf *path, git_repository *repo)
return 0;
}
+static int reflog_alloc(git_reflog **reflog, const char *name)
+{
+ git_reflog *log;
+
+ *reflog = NULL;
+
+ log = git__calloc(1, sizeof(git_reflog));
+ GITERR_CHECK_ALLOC(log);
+
+ log->ref_name = git__strdup(name);
+ GITERR_CHECK_ALLOC(log->ref_name);
+
+ if (git_vector_init(&log->entries, 0, NULL) < 0) {
+ git__free(log->ref_name);
+ git__free(log);
+ return -1;
+ }
+
+ *reflog = log;
+
+ return 0;
+}
+
+static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
+{
+ const char *ptr;
+ git_reflog_entry *entry;
+
+#define seek_forward(_increase) do { \
+ if (_increase >= buf_size) { \
+ giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+ goto fail; \
+ } \
+ buf += _increase; \
+ buf_size -= _increase; \
+ } while (0)
+
+ while (buf_size > GIT_REFLOG_SIZE_MIN) {
+ entry = git__calloc(1, sizeof(git_reflog_entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ entry->committer = git__malloc(sizeof(git_signature));
+ GITERR_CHECK_ALLOC(entry->committer);
+
+ if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
+ seek_forward(GIT_OID_HEXSZ + 1);
+
+ if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
+ seek_forward(GIT_OID_HEXSZ + 1);
+
+ ptr = buf;
+
+ /* Seek forward to the end of the signature. */
+ while (*buf && *buf != '\t' && *buf != '\n')
+ seek_forward(1);
+
+ if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
+ goto fail;
+
+ if (*buf == '\t') {
+ /* We got a message. Read everything till we reach LF. */
+ seek_forward(1);
+ ptr = buf;
+
+ while (*buf && *buf != '\n')
+ seek_forward(1);
+
+ entry->msg = git__strndup(ptr, buf - ptr);
+ GITERR_CHECK_ALLOC(entry->msg);
+ } else
+ entry->msg = NULL;
+
+ while (*buf && *buf == '\n' && buf_size > 1)
+ seek_forward(1);
+
+ if (git_vector_insert(&log->entries, entry) < 0)
+ goto fail;
+ }
+
+ return 0;
+
+#undef seek_forward
+
+fail:
+ if (entry)
+ git_reflog_entry__free(entry);
+
+ return -1;
+}
+
+static int create_new_reflog_file(const char *filepath)
+{
+ int fd, error;
+
+ if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+ return error;
+
+ if ((fd = p_open(filepath,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ GIT_REFLOG_FILE_MODE)) < 0)
+ return -1;
+
+ return p_close(fd);
+}
+
+GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
+{
+ return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name);
+}
+
+static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
+{
+ int error = -1;
+ git_buf log_path = GIT_BUF_INIT;
+ git_buf log_file = GIT_BUF_INIT;
+ git_reflog *log = NULL;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ assert(out && _backend && name);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ if (reflog_alloc(&log, name) < 0)
+ return -1;
+
+ if (retrieve_reflog_path(&log_path, repo, name) < 0)
+ goto cleanup;
+
+ error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if ((error == GIT_ENOTFOUND) &&
+ ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
+ goto cleanup;
+
+ if ((error = reflog_parse(log,
+ git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
+ goto cleanup;
+
+ *out = log;
+ goto success;
+
+cleanup:
+ git_reflog_free(log);
+
+success:
+ git_buf_free(&log_file);
+ git_buf_free(&log_path);
+
+ return error;
+}
+
+static int serialize_reflog_entry(
+ git_buf *buf,
+ const git_oid *oid_old,
+ const git_oid *oid_new,
+ const git_signature *committer,
+ const char *msg)
+{
+ char raw_old[GIT_OID_HEXSZ+1];
+ char raw_new[GIT_OID_HEXSZ+1];
+
+ git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
+ git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
+
+ git_buf_clear(buf);
+
+ git_buf_puts(buf, raw_old);
+ git_buf_putc(buf, ' ');
+ git_buf_puts(buf, raw_new);
+
+ git_signature__writebuf(buf, " ", committer);
+
+ /* drop trailing LF */
+ git_buf_rtrim(buf);
+
+ if (msg) {
+ git_buf_putc(buf, '\t');
+ git_buf_puts(buf, msg);
+ }
+
+ git_buf_putc(buf, '\n');
+
+ return git_buf_oom(buf);
+}
+
+static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
+{
+ int error = -1;
+ unsigned int i;
+ git_reflog_entry *entry;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+ git_buf log_path = GIT_BUF_INIT;
+ git_buf log = GIT_BUF_INIT;
+ git_filebuf fbuf = GIT_FILEBUF_INIT;
+
+ assert(_backend && reflog);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0)
+ return -1;
+
+ if (!git_path_isfile(git_buf_cstr(&log_path))) {
+ giterr_set(GITERR_INVALID,
+ "Log file for reference '%s' doesn't exist.", reflog->ref_name);
+ goto cleanup;
+ }
+
+ if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
+ goto cleanup;
+
+ git_vector_foreach(&reflog->entries, i, entry) {
+ if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
+ goto cleanup;
+
+ if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+ goto cleanup;
+ }
+
+ error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
+ goto success;
+
+cleanup:
+ git_filebuf_cleanup(&fbuf);
+
+success:
+ git_buf_free(&log);
+ git_buf_free(&log_path);
+ return error;
+}
+
+static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
+{
+ int error = 0, fd;
+ git_buf old_path = GIT_BUF_INIT;
+ git_buf new_path = GIT_BUF_INIT;
+ git_buf temp_path = GIT_BUF_INIT;
+ git_buf normalized = GIT_BUF_INIT;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ assert(_backend && old_name && new_name);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ if ((error = git_reference__normalize_name(
+ &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
+ return error;
+
+ if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0)
+ return -1;
+
+ if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
+ return -1;
+
+ if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
+ return -1;
+
+ /*
+ * Move the reflog to a temporary place. This two-phase renaming is required
+ * in order to cope with funny renaming use cases when one tries to move a reference
+ * to a partially colliding namespace:
+ * - a/b -> a/b/c
+ * - a/b/c/d -> a/b/c
+ */
+ if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
+ return -1;
+
+ if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ p_close(fd);
+
+ if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_path_isdir(git_buf_cstr(&new_path)) &&
+ (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+ error = -1;
+ }
+
+cleanup:
+ git_buf_free(&temp_path);
+ git_buf_free(&old_path);
+ git_buf_free(&new_path);
+ git_buf_free(&normalized);
+
+ return error;
+}
+
+static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
+{
+ int error;
+ git_buf path = GIT_BUF_INIT;
+
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ assert(_backend && name);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ error = retrieve_reflog_path(&path, repo, name);
+
+ if (!error && git_path_exists(path.ptr))
+ error = p_unlink(path.ptr);
+
+ git_buf_free(&path);
+
+ return error;
+
+}
+
int git_refdb_backend_fs(
git_refdb_backend **backend_out,
git_repository *repository)
{
+ int t = 0;
git_buf path = GIT_BUF_INIT;
refdb_fs_backend *backend;
@@ -1092,6 +1438,15 @@ int git_refdb_backend_fs(
git_buf_free(&path);
+ if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
+ backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
+ backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
+ }
+ if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
+ backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+ backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
+ }
+
backend->parent.exists = &refdb_fs_backend__exists;
backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator;
@@ -1100,6 +1455,10 @@ int git_refdb_backend_fs(
backend->parent.rename = &refdb_fs_backend__rename;
backend->parent.compress = &refdb_fs_backend__compress;
backend->parent.free = &refdb_fs_backend__free;
+ backend->parent.reflog_read = &refdb_reflog_fs__read;
+ backend->parent.reflog_write = &refdb_reflog_fs__write;
+ backend->parent.reflog_rename = &refdb_reflog_fs__rename;
+ backend->parent.reflog_delete = &refdb_reflog_fs__delete;
*backend_out = (git_refdb_backend *)backend;
return 0;
diff --git a/src/reflog.c b/src/reflog.c
index a6752f618..cebb87d86 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -9,82 +9,16 @@
#include "repository.h"
#include "filebuf.h"
#include "signature.h"
+#include "refdb.h"
-static int reflog_init(git_reflog **reflog, const git_reference *ref)
-{
- git_reflog *log;
-
- *reflog = NULL;
-
- log = git__calloc(1, sizeof(git_reflog));
- GITERR_CHECK_ALLOC(log);
-
- log->ref_name = git__strdup(ref->name);
- GITERR_CHECK_ALLOC(log->ref_name);
-
- if (git_vector_init(&log->entries, 0, NULL) < 0) {
- git__free(log->ref_name);
- git__free(log);
- return -1;
- }
-
- log->owner = git_reference_owner(ref);
- *reflog = log;
+#include <git2/sys/refdb_backend.h>
- return 0;
-}
-
-static int serialize_reflog_entry(
- git_buf *buf,
- const git_oid *oid_old,
- const git_oid *oid_new,
- const git_signature *committer,
- const char *msg)
+git_reflog_entry *git_reflog_entry__alloc(void)
{
- char raw_old[GIT_OID_HEXSZ+1];
- char raw_new[GIT_OID_HEXSZ+1];
-
- git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
- git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
-
- git_buf_clear(buf);
-
- git_buf_puts(buf, raw_old);
- git_buf_putc(buf, ' ');
- git_buf_puts(buf, raw_new);
-
- git_signature__writebuf(buf, " ", committer);
-
- /* drop trailing LF */
- git_buf_rtrim(buf);
-
- if (msg) {
- git_buf_putc(buf, '\t');
- git_buf_puts(buf, msg);
- }
-
- git_buf_putc(buf, '\n');
-
- return git_buf_oom(buf);
-}
-
-static int reflog_entry_new(git_reflog_entry **entry)
-{
- git_reflog_entry *e;
-
- assert(entry);
-
- e = git__malloc(sizeof(git_reflog_entry));
- GITERR_CHECK_ALLOC(e);
-
- memset(e, 0, sizeof(git_reflog_entry));
-
- *entry = e;
-
- return 0;
+ return git__calloc(1, sizeof(git_reflog_entry));
}
-static void reflog_entry_free(git_reflog_entry *entry)
+void git_reflog_entry__free(git_reflog_entry *entry)
{
git_signature_free(entry->committer);
@@ -92,75 +26,6 @@ static void reflog_entry_free(git_reflog_entry *entry)
git__free(entry);
}
-static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
-{
- const char *ptr;
- git_reflog_entry *entry;
-
-#define seek_forward(_increase) do { \
- if (_increase >= buf_size) { \
- giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
- goto fail; \
- } \
- buf += _increase; \
- buf_size -= _increase; \
- } while (0)
-
- while (buf_size > GIT_REFLOG_SIZE_MIN) {
- if (reflog_entry_new(&entry) < 0)
- return -1;
-
- entry->committer = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(entry->committer);
-
- if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
- goto fail;
- seek_forward(GIT_OID_HEXSZ + 1);
-
- if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
- goto fail;
- seek_forward(GIT_OID_HEXSZ + 1);
-
- ptr = buf;
-
- /* Seek forward to the end of the signature. */
- while (*buf && *buf != '\t' && *buf != '\n')
- seek_forward(1);
-
- if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
- goto fail;
-
- if (*buf == '\t') {
- /* We got a message. Read everything till we reach LF. */
- seek_forward(1);
- ptr = buf;
-
- while (*buf && *buf != '\n')
- seek_forward(1);
-
- entry->msg = git__strndup(ptr, buf - ptr);
- GITERR_CHECK_ALLOC(entry->msg);
- } else
- entry->msg = NULL;
-
- while (*buf && *buf == '\n' && buf_size > 1)
- seek_forward(1);
-
- if (git_vector_insert(&log->entries, entry) < 0)
- goto fail;
- }
-
- return 0;
-
-#undef seek_forward
-
-fail:
- if (entry)
- reflog_entry_free(entry);
-
- return -1;
-}
-
void git_reflog_free(git_reflog *reflog)
{
size_t i;
@@ -169,10 +34,13 @@ void git_reflog_free(git_reflog *reflog)
if (reflog == NULL)
return;
+ if (reflog->db)
+ GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
+
for (i=0; i < reflog->entries.length; i++) {
entry = git_vector_get(&reflog->entries, i);
- reflog_entry_free(entry);
+ git_reflog_entry__free(entry);
}
git_vector_free(&reflog->entries);
@@ -180,115 +48,30 @@ void git_reflog_free(git_reflog *reflog)
git__free(reflog);
}
-static int retrieve_reflog_path(git_buf *path, const git_reference *ref)
+int git_reflog_read(git_reflog **reflog, git_repository *repo, const char *name)
{
- return git_buf_join_n(path, '/', 3,
- git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR, ref->name);
-}
+ git_refdb *refdb;
+ int error;
-static int create_new_reflog_file(const char *filepath)
-{
- int fd, error;
+ assert(reflog && repo && name);
- if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
- if ((fd = p_open(filepath,
- O_WRONLY | O_CREAT | O_TRUNC,
- GIT_REFLOG_FILE_MODE)) < 0)
- return -1;
-
- return p_close(fd);
-}
-
-int git_reflog_read(git_reflog **reflog, const git_reference *ref)
-{
- int error = -1;
- git_buf log_path = GIT_BUF_INIT;
- git_buf log_file = GIT_BUF_INIT;
- git_reflog *log = NULL;
-
- assert(reflog && ref);
-
- *reflog = NULL;
-
- if (reflog_init(&log, ref) < 0)
- return -1;
-
- if (retrieve_reflog_path(&log_path, ref) < 0)
- goto cleanup;
-
- error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if ((error == GIT_ENOTFOUND) &&
- ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
- goto cleanup;
-
- if ((error = reflog_parse(log,
- git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
- goto cleanup;
-
- *reflog = log;
- goto success;
-
-cleanup:
- git_reflog_free(log);
-
-success:
- git_buf_free(&log_file);
- git_buf_free(&log_path);
-
- return error;
+ return git_refdb_reflog_read(reflog, refdb, name);
}
int git_reflog_write(git_reflog *reflog)
{
- int error = -1;
- unsigned int i;
- git_reflog_entry *entry;
- git_buf log_path = GIT_BUF_INIT;
- git_buf log = GIT_BUF_INIT;
- git_filebuf fbuf = GIT_FILEBUF_INIT;
+ git_refdb *db;
- assert(reflog);
+ assert(reflog && reflog->db);
- if (git_buf_join_n(&log_path, '/', 3,
- git_repository_path(reflog->owner), GIT_REFLOG_DIR, reflog->ref_name) < 0)
- return -1;
-
- if (!git_path_isfile(git_buf_cstr(&log_path))) {
- giterr_set(GITERR_INVALID,
- "Log file for reference '%s' doesn't exist.", reflog->ref_name);
- goto cleanup;
- }
-
- if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
- goto cleanup;
-
- git_vector_foreach(&reflog->entries, i, entry) {
- if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
- goto cleanup;
-
- if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
- goto cleanup;
- }
-
- error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
- goto success;
-
-cleanup:
- git_filebuf_cleanup(&fbuf);
-
-success:
- git_buf_free(&log);
- git_buf_free(&log_path);
- return error;
+ db = reflog->db;
+ return db->backend->reflog_write(db->backend, reflog);
}
-int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
- const git_signature *committer, const char *msg)
+int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
{
git_reflog_entry *entry;
const git_reflog_entry *previous;
@@ -296,8 +79,8 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
assert(reflog && new_oid && committer);
- if (reflog_entry_new(&entry) < 0)
- return -1;
+ entry = git__calloc(1, sizeof(git_reflog_entry));
+ GITERR_CHECK_ALLOC(entry);
if ((entry->committer = git_signature_dup(committer)) == NULL)
goto cleanup;
@@ -333,94 +116,30 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
return 0;
cleanup:
- reflog_entry_free(entry);
+ git_reflog_entry__free(entry);
return -1;
}
-int git_reflog_rename(git_reference *ref, const char *new_name)
+int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
{
- int error = 0, fd;
- git_buf old_path = GIT_BUF_INIT;
- git_buf new_path = GIT_BUF_INIT;
- git_buf temp_path = GIT_BUF_INIT;
- git_buf normalized = GIT_BUF_INIT;
-
- assert(ref && new_name);
-
- if ((error = git_reference__normalize_name(
- &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
- return error;
-
- if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0)
- return -1;
-
- if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0)
- return -1;
-
- if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
- return -1;
+ git_refdb *refdb;
+ int error;
- /*
- * Move the reflog to a temporary place. This two-phase renaming is required
- * in order to cope with funny renaming use cases when one tries to move a reference
- * to a partially colliding namespace:
- * - a/b -> a/b/c
- * - a/b/c/d -> a/b/c
- */
- if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return -1;
- if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
- error = -1;
- goto cleanup;
- }
-
- p_close(fd);
-
- if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
- giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
- error = -1;
- goto cleanup;
- }
-
- if (git_path_isdir(git_buf_cstr(&new_path)) &&
- (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
- error = -1;
- goto cleanup;
- }
-
- if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
- error = -1;
- goto cleanup;
- }
-
- if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
- giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
- error = -1;
- }
-
-cleanup:
- git_buf_free(&temp_path);
- git_buf_free(&old_path);
- git_buf_free(&new_path);
- git_buf_free(&normalized);
-
- return error;
+ return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
}
-int git_reflog_delete(git_reference *ref)
+int git_reflog_delete(git_repository *repo, const char *name)
{
+ git_refdb *refdb;
int error;
- git_buf path = GIT_BUF_INIT;
-
- error = retrieve_reflog_path(&path, ref);
- if (!error && git_path_exists(path.ptr))
- error = p_unlink(path.ptr);
-
- git_buf_free(&path);
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return -1;
- return error;
+ return refdb->backend->reflog_delete(refdb->backend, name);
}
size_t git_reflog_entrycount(git_reflog *reflog)
@@ -429,11 +148,6 @@ size_t git_reflog_entrycount(git_reflog *reflog)
return reflog->entries.length;
}
-GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
-{
- return (total - 1) - idx;
-}
-
const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx)
{
assert(reflog);
@@ -469,26 +183,21 @@ const char * git_reflog_entry_message(const git_reflog_entry *entry)
return entry->msg;
}
-int git_reflog_drop(
- git_reflog *reflog,
- size_t idx,
- int rewrite_previous_entry)
+int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
{
size_t entrycount;
git_reflog_entry *entry, *previous;
- assert(reflog);
-
entrycount = git_reflog_entrycount(reflog);
entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
if (entry == NULL) {
- giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx);
+ giterr_set(GITERR_REFERENCE, "No reflog entry at index "PRIuZ, idx);
return GIT_ENOTFOUND;
}
- reflog_entry_free(entry);
+ git_reflog_entry__free(entry);
if (git_vector_remove(
&reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
@@ -521,3 +230,22 @@ int git_reflog_drop(
return 0;
}
+
+int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id,
+ const git_signature *committer, const char *msg)
+{
+ int error;
+ git_reflog *reflog;
+
+ if ((error = git_reflog_read(&reflog, repo, name)) < 0)
+ return error;
+
+ if ((error = git_reflog_append(reflog, id, committer, msg)) < 0)
+ goto cleanup;
+
+ error = git_reflog_write(reflog);
+
+cleanup:
+ git_reflog_free(reflog);
+ return error;
+}
diff --git a/src/reflog.h b/src/reflog.h
index 9444ebd10..2d31ae47d 100644
--- a/src/reflog.h
+++ b/src/reflog.h
@@ -27,9 +27,14 @@ struct git_reflog_entry {
};
struct git_reflog {
+ git_refdb *db;
char *ref_name;
- git_repository *owner;
git_vector entries;
};
+GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
+{
+ return (total - 1) - idx;
+}
+
#endif /* INCLUDE_reflog_h__ */
diff --git a/src/refs.c b/src/refs.c
index c045ab9dc..269c5f54e 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -138,6 +138,22 @@ int git_reference_name_to_id(
return 0;
}
+static int reference_normalize_for_repo(
+ char *out,
+ size_t out_size,
+ git_repository *repo,
+ const char *name)
+{
+ int precompose;
+ unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
+
+ if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
+ precompose)
+ flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
+
+ return git_reference_normalize_name(out, out_size, name, flags);
+}
+
int git_reference_lookup_resolved(
git_reference **ref_out,
git_repository *repo,
@@ -159,13 +175,13 @@ int git_reference_lookup_resolved(
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;
+ if ((error = reference_normalize_for_repo(
+ scan_name, sizeof(scan_name), repo, name)) < 0)
+ return error;
- if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
for (nesting = max_nesting;
@@ -173,7 +189,7 @@ int git_reference_lookup_resolved(
nesting--)
{
if (nesting != max_nesting) {
- strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
+ strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
git_reference_free(ref);
}
@@ -467,7 +483,7 @@ int git_reference_rename(
if (reference_has_log < 0)
return reference_has_log;
- if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
+ if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0)
return error;
return 0;
@@ -711,17 +727,18 @@ static bool is_all_caps_and_underscore(const char *name, size_t len)
return true;
}
+/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
int git_reference__normalize_name(
git_buf *buf,
const char *name,
unsigned int flags)
{
- // Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
-
char *current;
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
unsigned int process_flags;
bool normalize = (buf != NULL);
+ git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+
assert(name);
process_flags = flags;
@@ -733,6 +750,14 @@ int git_reference__normalize_name(
if (normalize)
git_buf_clear(buf);
+ if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
+ size_t namelen = strlen(current);
+ if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
+ (error = git_path_iconv(&ic, &current, &namelen)) < 0)
+ goto cleanup;
+ error = GIT_EINVALIDSPEC;
+ }
+
while (true) {
segment_len = ensure_segment_validity(current);
if (segment_len < 0) {
@@ -809,6 +834,8 @@ cleanup:
if (error && normalize)
git_buf_free(buf);
+ git_path_iconv_clear(&ic);
+
return error;
}
@@ -983,9 +1010,9 @@ static int peel_error(int error, git_reference *ref, const char* msg)
}
int git_reference_peel(
- git_object **peeled,
- git_reference *ref,
- git_otype target_type)
+ git_object **peeled,
+ git_reference *ref,
+ git_otype target_type)
{
git_reference *resolved = NULL;
git_object *target = NULL;
@@ -1027,24 +1054,19 @@ cleanup:
return error;
}
-int git_reference__is_valid_name(
- const char *refname,
- unsigned int flags)
+int git_reference__is_valid_name(const char *refname, unsigned int flags)
{
- int error;
-
- error = git_reference__normalize_name(NULL, refname, flags) == 0;
- giterr_clear();
+ if (git_reference__normalize_name(NULL, refname, flags) < 0) {
+ giterr_clear();
+ return false;
+ }
- return error;
+ return true;
}
-int git_reference_is_valid_name(
- const char *refname)
+int git_reference_is_valid_name(const char *refname)
{
- return git_reference__is_valid_name(
- refname,
- GIT_REF_FORMAT_ALLOW_ONELEVEL);
+ return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
}
const char *git_reference_shorthand(git_reference *ref)
diff --git a/src/refs.h b/src/refs.h
index 4b91c25e8..80c7703fc 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -46,6 +46,8 @@
#define GIT_STASH_FILE "stash"
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
+#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
+
#define GIT_REFNAME_MAX 1024
struct git_reference {
diff --git a/src/remote.c b/src/remote.c
index ccedf2386..bdfa08642 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -10,6 +10,7 @@
#include "git2/oid.h"
#include "git2/net.h"
+#include "common.h"
#include "config.h"
#include "repository.h"
#include "remote.h"
diff --git a/src/repository.c b/src/repository.c
index 0d7c09484..c5ce8425f 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -843,10 +843,6 @@ fail:
static bool is_chmod_supported(const char *file_path)
{
struct stat st1, st2;
- static int _is_supported = -1;
-
- if (_is_supported > -1)
- return _is_supported;
if (p_stat(file_path, &st1) < 0)
return false;
@@ -857,27 +853,19 @@ static bool is_chmod_supported(const char *file_path)
if (p_stat(file_path, &st2) < 0)
return false;
- _is_supported = (st1.st_mode != st2.st_mode);
-
- return _is_supported;
+ return (st1.st_mode != st2.st_mode);
}
static bool is_filesystem_case_insensitive(const char *gitdir_path)
{
git_buf path = GIT_BUF_INIT;
- static int _is_insensitive = -1;
+ int is_insensitive = -1;
- if (_is_insensitive > -1)
- return _is_insensitive;
-
- if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
- goto cleanup;
+ if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
+ is_insensitive = git_path_exists(git_buf_cstr(&path));
- _is_insensitive = git_path_exists(git_buf_cstr(&path));
-
-cleanup:
git_buf_free(&path);
- return _is_insensitive;
+ return is_insensitive;
}
static bool are_symlinks_supported(const char *wd_path)
@@ -885,26 +873,77 @@ static bool are_symlinks_supported(const char *wd_path)
git_buf path = GIT_BUF_INIT;
int fd;
struct stat st;
- static int _symlinks_supported = -1;
-
- if (_symlinks_supported > -1)
- return _symlinks_supported;
+ int symlinks_supported = -1;
if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
p_close(fd) < 0 ||
p_unlink(path.ptr) < 0 ||
p_symlink("testing", path.ptr) < 0 ||
p_lstat(path.ptr, &st) < 0)
- _symlinks_supported = false;
+ symlinks_supported = false;
else
- _symlinks_supported = (S_ISLNK(st.st_mode) != 0);
+ symlinks_supported = (S_ISLNK(st.st_mode) != 0);
(void)p_unlink(path.ptr);
git_buf_free(&path);
- return _symlinks_supported;
+ return symlinks_supported;
+}
+
+#ifdef GIT_USE_ICONV
+
+static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
+static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
+
+/* Check if the platform is decomposing unicode data for us. We will
+ * emulate core Git and prefer to use precomposed unicode data internally
+ * on these platforms, composing the decomposed unicode on the fly.
+ *
+ * This mainly happens on the Mac where HDFS stores filenames as
+ * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
+ * return decomposed unicode from readdir() even when the actual
+ * filesystem is storing precomposed unicode.
+ */
+static bool does_fs_decompose_unicode_paths(const char *wd_path)
+{
+ git_buf path = GIT_BUF_INIT;
+ int fd;
+ bool found_decomposed = false;
+ char tmp[6];
+
+ /* Create a file using a precomposed path and then try to find it
+ * using the decomposed name. If the lookup fails, then we will mark
+ * that we should precompose unicode for this repository.
+ */
+ if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
+ (fd = p_mkstemp(path.ptr)) < 0)
+ goto done;
+ p_close(fd);
+
+ /* record trailing digits generated by mkstemp */
+ memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
+
+ /* try to look up as NFD path */
+ if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
+ goto done;
+ memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+ found_decomposed = git_path_exists(path.ptr);
+
+ /* remove temporary file (using original precomposed path) */
+ if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
+ goto done;
+ memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+ (void)p_unlink(path.ptr);
+
+done:
+ git_buf_free(&path);
+ return found_decomposed;
}
+#endif
+
static int create_empty_file(const char *path, mode_t mode)
{
int fd;
@@ -922,71 +961,131 @@ static int create_empty_file(const char *path, mode_t mode)
return 0;
}
+static int repo_local_config(
+ git_config **out,
+ git_buf *config_dir,
+ git_repository *repo,
+ const char *repo_dir)
+{
+ int error = 0;
+ git_config *parent;
+ const char *cfg_path;
+
+ if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ return -1;
+ cfg_path = git_buf_cstr(config_dir);
+
+ /* make LOCAL config if missing */
+ if (!git_path_isfile(cfg_path) &&
+ (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
+ return error;
+
+ /* if no repo, just open that file directly */
+ if (!repo)
+ return git_config_open_ondisk(out, cfg_path);
+
+ /* otherwise, open parent config and get that level */
+ if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
+ return error;
+
+ if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+ giterr_clear();
+
+ if (!(error = git_config_add_file_ondisk(
+ parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
+ error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
+ }
+
+ git_config_free(parent);
+
+ return error;
+}
+
+static int repo_init_fs_configs(
+ git_config *cfg,
+ const char *cfg_path,
+ const char *repo_dir,
+ const char *work_dir,
+ bool update_ignorecase)
+{
+ int error = 0;
+
+ if (!work_dir)
+ work_dir = repo_dir;
+
+ if ((error = git_config_set_bool(
+ cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
+ return error;
+
+ if (!are_symlinks_supported(work_dir)) {
+ if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
+ return error;
+ } else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
+ giterr_clear();
+
+ if (update_ignorecase) {
+ if (is_filesystem_case_insensitive(repo_dir)) {
+ if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
+ return error;
+ } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
+ giterr_clear();
+ }
+
+#ifdef GIT_USE_ICONV
+ if ((error = git_config_set_bool(
+ cfg, "core.precomposeunicode",
+ does_fs_decompose_unicode_paths(work_dir))) < 0)
+ return error;
+#endif
+
+ return 0;
+}
+
static int repo_init_config(
const char *repo_dir,
const char *work_dir,
- git_repository_init_options *opts)
+ uint32_t flags,
+ uint32_t mode)
{
int error = 0;
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
+ bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
+ bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
-#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
- if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
- goto cleanup; } while (0)
+ if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
+ goto cleanup;
- if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
- return -1;
+ if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
+ goto cleanup;
- if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
- create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
- git_buf_free(&cfg_path);
- return -1;
- }
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
+ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
+ goto cleanup; } while (0)
- if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
- git_buf_free(&cfg_path);
- return -1;
- }
+ SET_REPO_CONFIG(bool, "core.bare", is_bare);
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
- if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
- (error = check_repositoryformatversion(config)) < 0)
+ if ((error = repo_init_fs_configs(
+ config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
goto cleanup;
- SET_REPO_CONFIG(
- bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
- SET_REPO_CONFIG(
- int32, "core.repositoryformatversion", GIT_REPO_VERSION);
- SET_REPO_CONFIG(
- bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
-
- if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
+ if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
- if (!are_symlinks_supported(work_dir))
- SET_REPO_CONFIG(bool, "core.symlinks", false);
-
- if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
SET_REPO_CONFIG(string, "core.worktree", work_dir);
- }
- else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
+ else if (is_reinit) {
if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear();
}
- } else {
- if (!are_symlinks_supported(repo_dir))
- SET_REPO_CONFIG(bool, "core.symlinks", false);
}
- if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
- is_filesystem_case_insensitive(repo_dir))
- SET_REPO_CONFIG(bool, "core.ignorecase", true);
-
- if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
+ if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
- else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
+ else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
@@ -998,6 +1097,41 @@ cleanup:
return error;
}
+static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
+{
+ git_repository *smrepo = NULL;
+ GIT_UNUSED(n); GIT_UNUSED(p);
+
+ if (git_submodule_open(&smrepo, sm) < 0 ||
+ git_repository_reinit_filesystem(smrepo, true) < 0)
+ giterr_clear();
+ git_repository_free(smrepo);
+
+ return 0;
+}
+
+int git_repository_reinit_filesystem(git_repository *repo, int recurse)
+{
+ int error = 0;
+ git_buf path = GIT_BUF_INIT;
+ git_config *config = NULL;
+ const char *repo_dir = git_repository_path(repo);
+
+ if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
+ error = repo_init_fs_configs(
+ config, path.ptr, repo_dir, git_repository_workdir(repo), true);
+
+ git_config_free(config);
+ git_buf_free(&path);
+
+ git_repository__cvar_cache_clear(repo);
+
+ if (!repo->is_bare && recurse)
+ (void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
+
+ return error;
+}
+
static int repo_write_template(
const char *git_dir,
bool allow_overwrite,
@@ -1386,22 +1520,22 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
error = repo_init_config(
- git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
+ repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
/* TODO: reinitialize the templates */
}
else {
if (!(error = repo_init_structure(
- git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
+ repo_path.ptr, wd_path.ptr, opts)) &&
!(error = repo_init_config(
- git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
+ repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
error = repo_init_create_head(
- git_buf_cstr(&repo_path), opts->initial_head);
+ repo_path.ptr, opts->initial_head);
}
if (error < 0)
goto cleanup;
- error = git_repository_open(out, git_buf_cstr(&repo_path));
+ error = git_repository_open(out, repo_path.ptr);
if (!error && opts->origin_url)
error = repo_init_create_origin(*out, opts->origin_url);
diff --git a/src/repository.h b/src/repository.h
index 12dc50d51..832df3bd2 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -37,6 +37,7 @@ typedef enum {
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
GIT_CVAR_ABBREV, /* core.abbrev */
+ GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_CACHE_MAX
} git_cvar_cached;
@@ -86,6 +87,8 @@ typedef enum {
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
/* core.abbrev */
GIT_ABBREV_DEFAULT = 7,
+ /* core.precomposeunicode */
+ GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
} git_cvar_value;
diff --git a/src/reset.c b/src/reset.c
index cea212a93..a9780bfbc 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -24,10 +24,9 @@ int git_reset_default(
{
git_object *commit = NULL;
git_tree *tree = NULL;
- git_diff_list *diff = NULL;
+ git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- size_t i;
- git_diff_delta *delta;
+ size_t i, max_i;
git_index_entry entry;
int error;
git_index *index = NULL;
@@ -58,7 +57,9 @@ int git_reset_default(
&diff, repo, tree, index, &opts)) < 0)
goto cleanup;
- git_vector_foreach(&diff->deltas, i, delta) {
+ for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, i);
+
if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0)
goto cleanup;
@@ -85,7 +86,7 @@ cleanup:
git_object_free(commit);
git_tree_free(tree);
git_index_free(index);
- git_diff_list_free(diff);
+ git_diff_free(diff);
return error;
}
@@ -135,7 +136,7 @@ int git_reset(
if (reset_type == GIT_RESET_HARD) {
/* overwrite working directory with HEAD */
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_SKIP_UNMERGED;
if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
goto cleanup;
diff --git a/src/revparse.c b/src/revparse.c
index e470a954d..c120b466f 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -160,7 +160,7 @@ static int retrieve_previously_checked_out_branch_or_revision(git_object **out,
if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
goto cleanup;
- if (git_reflog_read(&reflog, ref) < 0)
+ if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
goto cleanup;
numentries = git_reflog_entrycount(reflog);
@@ -208,7 +208,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
const git_reflog_entry *entry;
bool search_by_pos = (identifier <= 100000000);
- if (git_reflog_read(&reflog, ref) < 0)
+ if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
return -1;
numentries = git_reflog_entrycount(reflog);
diff --git a/src/stash.c b/src/stash.c
index 7742eee19..083c2a4cd 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -153,65 +153,61 @@ cleanup:
return error;
}
-struct cb_data {
- git_index *index;
-
- int error;
-
+struct stash_update_rules {
bool include_changed;
bool include_untracked;
bool include_ignored;
};
-static int update_index_cb(
- const git_diff_delta *delta,
- float progress,
- void *payload)
+static int stash_update_index_from_diff(
+ git_index *index,
+ const git_diff *diff,
+ struct stash_update_rules *data)
{
- struct cb_data *data = (struct cb_data *)payload;
- const char *add_path = NULL;
-
- GIT_UNUSED(progress);
-
- switch (delta->status) {
- case GIT_DELTA_IGNORED:
- if (data->include_ignored)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_UNTRACKED:
- if (data->include_untracked)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_ADDED:
- case GIT_DELTA_MODIFIED:
- if (data->include_changed)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_DELETED:
- if (!data->include_changed)
+ int error = 0;
+ size_t d, max_d = git_diff_num_deltas(diff);
+
+ for (d = 0; !error && d < max_d; ++d) {
+ const char *add_path = NULL;
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+
+ switch (delta->status) {
+ case GIT_DELTA_IGNORED:
+ if (data->include_ignored)
+ add_path = delta->new_file.path;
break;
- if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
- data->error = git_index_remove(
- data->index, delta->old_file.path, 0);
- break;
-
- default:
- /* Unimplemented */
- giterr_set(
- GITERR_INVALID,
- "Cannot update index. Unimplemented status (%d)",
- delta->status);
- data->error = -1;
- break;
- }
- if (add_path != NULL)
- data->error = git_index_add_bypath(data->index, add_path);
+ case GIT_DELTA_UNTRACKED:
+ if (data->include_untracked)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_MODIFIED:
+ if (data->include_changed)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_DELETED:
+ if (data->include_changed &&
+ !git_index_find(NULL, index, delta->old_file.path))
+ error = git_index_remove(index, delta->old_file.path, 0);
+ break;
+
+ default:
+ /* Unimplemented */
+ giterr_set(
+ GITERR_INVALID,
+ "Cannot update index. Unimplemented status (%d)",
+ delta->status);
+ return -1;
+ }
- return data->error;
+ if (add_path != NULL)
+ error = git_index_add_bypath(index, add_path);
+ }
+
+ return error;
}
static int build_untracked_tree(
@@ -221,15 +217,13 @@ static int build_untracked_tree(
uint32_t flags)
{
git_tree *i_tree = NULL;
- git_diff_list *diff = NULL;
+ git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct cb_data data = {0};
+ struct stash_update_rules data = {0};
int error;
git_index_clear(index);
- data.index = index;
-
if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_RECURSE_UNTRACKED_DIRS;
@@ -248,18 +242,13 @@ static int build_untracked_tree(
&diff, git_index_owner(index), i_tree, &opts)) < 0)
goto cleanup;
- if ((error = git_diff_foreach(
- diff, update_index_cb, NULL, NULL, &data)) < 0)
- {
- if (error == GIT_EUSER)
- error = data.error;
+ if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
goto cleanup;
- }
error = build_tree_from_index(tree_out, index);
cleanup:
- git_diff_list_free(diff);
+ git_diff_free(diff);
git_tree_free(i_tree);
return error;
}
@@ -311,9 +300,9 @@ static int build_workdir_tree(
{
git_repository *repo = git_index_owner(index);
git_tree *b_tree = NULL;
- git_diff_list *diff = NULL, *diff2 = NULL;
+ git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct cb_data data = {0};
+ struct stash_update_rules data = {0};
int error;
opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
@@ -321,33 +310,19 @@ static int build_workdir_tree(
if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
goto cleanup;
- if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
- goto cleanup;
-
- if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
- goto cleanup;
-
- if ((error = git_diff_merge(diff, diff2)) < 0)
+ if ((error = git_diff_tree_to_workdir_with_index(
+ &diff, repo, b_tree, &opts)) < 0)
goto cleanup;
- data.index = index;
data.include_changed = true;
- if ((error = git_diff_foreach(
- diff, update_index_cb, NULL, NULL, &data)) < 0)
- {
- if (error == GIT_EUSER)
- error = data.error;
+ if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
goto cleanup;
- }
-
- if ((error = build_tree_from_index(tree_out, index)) < 0)
- goto cleanup;
+ error = build_tree_from_index(tree_out, index);
cleanup:
- git_diff_list_free(diff);
- git_diff_list_free(diff2);
+ git_diff_free(diff);
git_tree_free(b_tree);
return error;
@@ -436,14 +411,16 @@ static int update_reflog(
const git_signature *stasher,
const char *message)
{
- git_reference *stash = NULL;
+ git_reference *stash;
git_reflog *reflog = NULL;
int error;
if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
goto cleanup;
- if ((error = git_reflog_read(&reflog, stash)) < 0)
+ git_reference_free(stash);
+
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0))
goto cleanup;
if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
@@ -453,7 +430,6 @@ static int update_reflog(
goto cleanup;
cleanup:
- git_reference_free(stash);
git_reflog_free(reflog);
return error;
}
@@ -599,7 +575,7 @@ int git_stash_foreach(
if (error < 0)
goto cleanup;
- if ((error = git_reflog_read(&reflog, stash)) < 0)
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup;
max = git_reflog_entrycount(reflog);
@@ -633,7 +609,7 @@ int git_stash_drop(
if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
return error;
- if ((error = git_reflog_read(&reflog, stash)) < 0)
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup;
max = git_reflog_entrycount(reflog);
diff --git a/src/status.c b/src/status.c
index be40b9f83..2b84794b5 100644
--- a/src/status.c
+++ b/src/status.c
@@ -52,7 +52,7 @@ static unsigned int index_delta2status(const git_diff_delta *head2idx)
}
static unsigned int workdir_delta2status(
- git_diff_list *diff, git_diff_delta *idx2wd)
+ git_diff *diff, git_diff_delta *idx2wd)
{
git_status_t st = GIT_STATUS_CURRENT;
@@ -361,8 +361,8 @@ void git_status_list_free(git_status_list *status)
if (status == NULL)
return;
- git_diff_list_free(status->head2idx);
- git_diff_list_free(status->idx2wd);
+ git_diff_free(status->head2idx);
+ git_diff_free(status->idx2wd);
git_vector_foreach(&status->paired, i, status_entry)
git__free(status_entry);
diff --git a/src/status.h b/src/status.h
index b58e0ebd6..33008b89c 100644
--- a/src/status.h
+++ b/src/status.h
@@ -14,8 +14,8 @@
struct git_status_list {
git_status_options opts;
- git_diff_list *head2idx;
- git_diff_list *idx2wd;
+ git_diff *head2idx;
+ git_diff *idx2wd;
git_vector paired;
};
diff --git a/src/submodule.c b/src/submodule.c
index 121383b9c..18d80f0a9 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -372,7 +372,8 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
memset(&entry, 0, sizeof(entry));
entry.path = sm->path;
- git_index_entry__init_from_stat(&entry, &st);
+ git_index_entry__init_from_stat(
+ &entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
/* calling git_submodule_open will have set sm->wd_oid if possible */
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
@@ -1527,7 +1528,7 @@ static void submodule_get_wd_status(
(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
git_tree *sm_head = NULL;
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff;
+ git_diff *diff;
*status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
@@ -1567,7 +1568,7 @@ static void submodule_get_wd_status(
else {
if (git_diff_num_deltas(diff) > 0)
*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
- git_diff_list_free(diff);
+ git_diff_free(diff);
diff = NULL;
}
@@ -1587,7 +1588,7 @@ static void submodule_get_wd_status(
if (git_diff_num_deltas(diff) != untracked)
*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
- git_diff_list_free(diff);
+ git_diff_free(diff);
diff = NULL;
}
}
diff --git a/src/transports/cred.c b/src/transports/cred.c
index 35aaf4f91..cc7fdab4b 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -19,13 +19,13 @@ int git_cred_has_username(git_cred *cred)
ret = !!c->username;
break;
}
- case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
- git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+ case GIT_CREDTYPE_SSH_KEY: {
+ git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
ret = !!c->username;
break;
}
- case GIT_CREDTYPE_SSH_PUBLICKEY: {
- git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+ case GIT_CREDTYPE_SSH_CUSTOM: {
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
ret = !!c->username;
break;
}
@@ -58,7 +58,7 @@ int git_cred_userpass_plaintext_new(
{
git_cred_userpass_plaintext *c;
- assert(cred);
+ assert(cred && username && password);
c = git__malloc(sizeof(git_cred_userpass_plaintext));
GITERR_CHECK_ALLOC(c);
@@ -84,10 +84,10 @@ int git_cred_userpass_plaintext_new(
return 0;
}
-static void ssh_keyfile_passphrase_free(struct git_cred *cred)
+static void ssh_key_free(struct git_cred *cred)
{
- git_cred_ssh_keyfile_passphrase *c =
- (git_cred_ssh_keyfile_passphrase *)cred;
+ git_cred_ssh_key *c =
+ (git_cred_ssh_key *)cred;
git__free(c->username);
git__free(c->publickey);
@@ -104,9 +104,9 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred)
git__free(c);
}
-static void ssh_publickey_free(struct git_cred *cred)
+static void ssh_custom_free(struct git_cred *cred)
{
- git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
git__free(c->username);
git__free(c->publickey);
@@ -115,22 +115,22 @@ static void ssh_publickey_free(struct git_cred *cred)
git__free(c);
}
-int git_cred_ssh_keyfile_passphrase_new(
+int git_cred_ssh_key_new(
git_cred **cred,
const char *username,
const char *publickey,
const char *privatekey,
const char *passphrase)
{
- git_cred_ssh_keyfile_passphrase *c;
+ git_cred_ssh_key *c;
assert(cred && privatekey);
- c = git__calloc(1, sizeof(git_cred_ssh_keyfile_passphrase));
+ c = git__calloc(1, sizeof(git_cred_ssh_key));
GITERR_CHECK_ALLOC(c);
- c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
- c->parent.free = ssh_keyfile_passphrase_free;
+ c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
+ c->parent.free = ssh_key_free;
if (username) {
c->username = git__strdup(username);
@@ -154,7 +154,7 @@ int git_cred_ssh_keyfile_passphrase_new(
return 0;
}
-int git_cred_ssh_publickey_new(
+int git_cred_ssh_custom_new(
git_cred **cred,
const char *username,
const char *publickey,
@@ -162,15 +162,15 @@ int git_cred_ssh_publickey_new(
git_cred_sign_callback sign_callback,
void *sign_data)
{
- git_cred_ssh_publickey *c;
+ git_cred_ssh_custom *c;
assert(cred);
- c = git__calloc(1, sizeof(git_cred_ssh_publickey));
+ c = git__calloc(1, sizeof(git_cred_ssh_custom));
GITERR_CHECK_ALLOC(c);
- c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
- c->parent.free = ssh_publickey_free;
+ c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM;
+ c->parent.free = ssh_custom_free;
if (username) {
c->username = git__strdup(username);
diff --git a/src/transports/smart.c b/src/transports/smart.c
index 416eb221f..a681d5f40 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -23,8 +23,13 @@ static int git_smart__recv_cb(gitno_buffer *buf)
buf->offset += bytes_read;
- if (t->packetsize_cb)
- t->packetsize_cb(bytes_read, t->packetsize_payload);
+ if (t->packetsize_cb && !t->cancelled.val)
+ if (t->packetsize_cb(bytes_read, t->packetsize_payload)) {
+ git_atomic_set(&t->cancelled, 1);
+
+ giterr_clear();
+ return GIT_EUSER;
+ }
return (int)(buf->offset - old_len);
}
diff --git a/src/transports/smart.h b/src/transports/smart.h
index c52401a3c..7fda27d4e 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -21,6 +21,7 @@
#define GIT_CAP_INCLUDE_TAG "include-tag"
#define GIT_CAP_DELETE_REFS "delete-refs"
#define GIT_CAP_REPORT_STATUS "report-status"
+#define GIT_CAP_THIN_PACK "thin-pack"
enum git_pkt_type {
GIT_PKT_CMD,
@@ -116,10 +117,11 @@ typedef struct transport_smart_caps {
side_band_64k:1,
include_tag:1,
delete_refs:1,
- report_status:1;
+ report_status:1,
+ thin_pack:1;
} transport_smart_caps;
-typedef void (*packetsize_cb)(size_t received, void *payload);
+typedef int (*packetsize_cb)(size_t received, void *payload);
typedef struct {
git_transport parent;
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 99da37567..a1f623c78 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -472,6 +472,9 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (caps->include_tag)
git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
+ if (caps->thin_pack)
+ git_buf_puts(&str, GIT_CAP_THIN_PACK " ");
+
if (git_buf_oom(&str))
return -1;
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 156b69e1f..af6e35fa1 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -128,6 +128,12 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
+ caps->common = caps->thin_pack = 1;
+ ptr += strlen(GIT_CAP_THIN_PACK);
+ continue;
+ }
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
@@ -425,7 +431,7 @@ struct network_packetsize_payload
size_t last_fired_bytes;
};
-static void network_packetsize(size_t received, void *payload)
+static int network_packetsize(size_t received, void *payload)
{
struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
@@ -435,8 +441,12 @@ static void network_packetsize(size_t received, void *payload)
/* Fire notification if the threshold is reached */
if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
npp->last_fired_bytes = npp->stats->received_bytes;
- npp->callback(npp->stats, npp->payload);
+
+ if (npp->callback(npp->stats, npp->payload))
+ return GIT_EUSER;
}
+
+ return 0;
}
int git_smart__download_pack(
@@ -450,7 +460,7 @@ int git_smart__download_pack(
gitno_buffer *buf = &t->buffer;
git_odb *odb;
struct git_odb_writepack *writepack = NULL;
- int error = -1;
+ int error = 0;
struct network_packetsize_payload npp = {0};
memset(stats, 0, sizeof(git_transfer_progress));
@@ -463,13 +473,14 @@ int git_smart__download_pack(
t->packetsize_payload = &npp;
/* We might have something in the buffer already from negotiate_fetch */
- if (t->buffer.offset > 0)
- t->packetsize_cb(t->buffer.offset, t->packetsize_payload);
+ if (t->buffer.offset > 0 && !t->cancelled.val)
+ if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
+ git_atomic_set(&t->cancelled, 1);
}
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
- goto on_error;
+ goto done;
/*
* If the remote doesn't support the side-band, we can feed
@@ -477,28 +488,37 @@ int git_smart__download_pack(
* check which one belongs there.
*/
if (!t->caps.side_band && !t->caps.side_band_64k) {
- if (no_sideband(t, writepack, buf, stats) < 0)
- goto on_error;
-
- goto on_success;
+ error = no_sideband(t, writepack, buf, stats);
+ goto done;
}
do {
git_pkt *pkt;
+ /* Check cancellation before network call */
if (t->cancelled.val) {
giterr_set(GITERR_NET, "The fetch was cancelled by the user");
error = GIT_EUSER;
- goto on_error;
+ goto done;
}
- if (recv_pkt(&pkt, buf) < 0)
- goto on_error;
+ if ((error = recv_pkt(&pkt, buf)) < 0)
+ goto done;
+
+ /* Check cancellation after network call */
+ if (t->cancelled.val) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ error = GIT_EUSER;
+ goto done;
+ }
if (pkt->type == GIT_PKT_PROGRESS) {
if (t->progress_cb) {
git_pkt_progress *p = (git_pkt_progress *) pkt;
- t->progress_cb(p->data, p->len, t->message_cb_payload);
+ if (t->progress_cb(p->data, p->len, t->message_cb_payload)) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ return GIT_EUSER;
+ }
}
git__free(pkt);
} else if (pkt->type == GIT_PKT_DATA) {
@@ -507,7 +527,7 @@ int git_smart__download_pack(
git__free(pkt);
if (error < 0)
- goto on_error;
+ goto done;
} else if (pkt->type == GIT_PKT_FLUSH) {
/* A flush indicates the end of the packfile */
git__free(pkt);
@@ -515,13 +535,9 @@ int git_smart__download_pack(
}
} while (1);
- if (writepack->commit(writepack, stats) < 0)
- goto on_error;
-
-on_success:
- error = 0;
+ error = writepack->commit(writepack, stats);
-on_error:
+done:
if (writepack)
writepack->free(writepack);
@@ -828,7 +844,10 @@ static int stream_thunk(void *buf, size_t size, void *data)
if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
payload->last_progress_report_time = current_time;
- payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
+ if (payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload)) {
+ giterr_clear();
+ error = GIT_EUSER;
+ }
}
}
@@ -840,7 +859,7 @@ int git_smart__push(git_transport *transport, git_push *push)
transport_smart *t = (transport_smart *)transport;
struct push_packbuilder_payload packbuilder_payload = {0};
git_buf pktline = GIT_BUF_INIT;
- int error = -1, need_pack = 0;
+ int error = 0, need_pack = 0;
push_spec *spec;
unsigned int i;
@@ -882,34 +901,31 @@ int git_smart__push(git_transport *transport, git_push *push)
}
}
- if (git_smart__get_push_stream(t, &packbuilder_payload.stream) < 0 ||
- gen_pktline(&pktline, push) < 0 ||
- packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
- goto on_error;
+ if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
+ (error = gen_pktline(&pktline, push)) < 0 ||
+ (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
+ goto done;
- if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload) < 0)
- goto on_error;
+ if (need_pack &&
+ (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
+ goto done;
/* If we sent nothing or the server doesn't support report-status, then
* we consider the pack to have been unpacked successfully */
if (!push->specs.length || !push->report_status)
push->unpack_ok = 1;
- else if (parse_report(&t->buffer, push) < 0)
- goto on_error;
+ else if ((error = parse_report(&t->buffer, push)) < 0)
+ goto done;
/* If progress is being reported write the final report */
if (push->transfer_progress_cb) {
push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
}
- if (push->status.length &&
- update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
- goto on_error;
-
- error = 0;
+ if (push->status.length)
+ error = update_refs_from_report(&t->refs, &push->specs, &push->status);
-on_error:
+done:
git_buf_free(&pktline);
-
return error;
}
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 62f3f0bbf..6ce673d5e 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -249,15 +249,15 @@ static int _git_ssh_authenticate_session(
rc = libssh2_userauth_password(session, user, c->password);
break;
}
- case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
- git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
+ case GIT_CREDTYPE_SSH_KEY: {
+ git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
user = c->username ? c->username : user;
rc = libssh2_userauth_publickey_fromfile(
session, c->username, c->publickey, c->privatekey, c->passphrase);
break;
}
- case GIT_CREDTYPE_SSH_PUBLICKEY: {
- git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+ case GIT_CREDTYPE_SSH_CUSTOM: {
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
user = c->username ? c->username : user;
rc = libssh2_userauth_publickey(
@@ -349,7 +349,8 @@ static int _git_ssh_setup_conn(
if (t->owner->cred_acquire_cb(
&t->cred, t->owner->url, user,
GIT_CREDTYPE_USERPASS_PLAINTEXT |
- GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
+ GIT_CREDTYPE_SSH_KEY |
+ GIT_CREDTYPE_SSH_CUSTOM,
t->owner->cred_acquire_payload) < 0)
goto on_error;
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 067d6fcc3..a25fa301e 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -265,15 +265,32 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
- /* Send Content-Type header -- only necessary on a POST */
if (post_verb == s->verb) {
+ /* Send Content-Type and Accept headers -- only necessary on a POST */
git_buf_clear(&buf);
- if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0)
+ if (git_buf_printf(&buf,
+ "Content-Type: application/x-git-%s-request",
+ s->service) < 0)
goto on_error;
git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
- if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
+ goto on_error;
+ }
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf,
+ "Accept: application/x-git-%s-result",
+ s->service) < 0)
+ goto on_error;
+
+ git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
+
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
giterr_set(GITERR_OS, "Failed to add a header to the request");
goto on_error;
}
diff --git a/src/tree.c b/src/tree.c
index 0bdf9a93e..bb59ff82b 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -237,7 +237,12 @@ void git_tree__free(void *_tree)
git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
{
- return (git_filemode_t)entry->attr;
+ return normalize_filemode(entry->attr);
+}
+
+git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry)
+{
+ return entry->attr;
}
const char *git_tree_entry_name(const git_tree_entry *entry)
@@ -386,8 +391,6 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj)
if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer)
return tree_error("Failed to parse tree. Can't parse filemode", NULL);
- attr = normalize_filemode(attr); /* make sure to normalize the filemode */
-
if (*buffer++ != ' ')
return tree_error("Failed to parse tree. Object is corrupted", NULL);
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 2f490529c..18f717b0f 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -125,8 +125,8 @@ static int do_lstat(
errno = ENOENT;
- /* We need POSIX behavior, then ENOTDIR must set when any of the folders in the
- * file path is a regular file,otherwise ENOENT must be set.
+ /* To match POSIX behavior, set ENOTDIR when any of the folders in the
+ * file path is a regular file, otherwise set ENOENT.
*/
if (posix_enotdir) {
/* scan up path until we find an existing item */