Commit 55cbd05b by Russell Belfer

Some diff refactorings to help code reuse

There are some diff functions that are useful in a rewritten
checkout and this lays some groundwork for that.  This contains
three main things:

1. Share the function diff uses to calculate the OID for a file
   in the working directory (now named `git_diff__oid_for_file`
2. Add a `git_diff__paired_foreach` function to iterator over
   two diff lists concurrently.  Convert status to use it.
3. Move all the string/prefix/index entry comparisons into
   function pointers inside the `git_diff_list` object so they
   can be switched between case sensitive and insensitive
   versions.  This makes them easier to reuse in various
   functions without replicating logic.  As part of this, move
   a couple of index functions out of diff.c and into index.c.
parent 2e3d4b96
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#ifndef INCLUDE_attr_file_h__ #ifndef INCLUDE_attr_file_h__
#define INCLUDE_attr_file_h__ #define INCLUDE_attr_file_h__
#include "git2/oid.h"
#include "git2/attr.h" #include "git2/attr.h"
#include "vector.h" #include "vector.h"
#include "pool.h" #include "pool.h"
......
...@@ -327,24 +327,39 @@ void git_diff_list_addref(git_diff_list *diff) ...@@ -327,24 +327,39 @@ void git_diff_list_addref(git_diff_list *diff)
GIT_REFCOUNT_INC(diff); GIT_REFCOUNT_INC(diff);
} }
static int oid_for_workdir_item( int git_diff__oid_for_file(
git_repository *repo, git_repository *repo,
const git_index_entry *item, const char *path,
uint16_t mode,
git_off_t size,
git_oid *oid) git_oid *oid)
{ {
int result = 0; int result = 0;
git_buf full_path = GIT_BUF_INIT; git_buf full_path = GIT_BUF_INIT;
if (git_buf_joinpath( if (git_buf_joinpath(
&full_path, git_repository_workdir(repo), item->path) < 0) &full_path, git_repository_workdir(repo), path) < 0)
return -1; return -1;
if (!mode) {
struct stat st;
if (p_stat(path, &st) < 0) {
giterr_set(GITERR_OS, "Could not stat '%s'", path);
result = -1;
goto cleanup;
}
mode = st.st_mode;
size = st.st_size;
}
/* calculate OID for file if possible */ /* calculate OID for file if possible */
if (S_ISGITLINK(item->mode)) { if (S_ISGITLINK(mode)) {
git_submodule *sm; git_submodule *sm;
const git_oid *sm_oid; const git_oid *sm_oid;
if (!git_submodule_lookup(&sm, repo, item->path) && if (!git_submodule_lookup(&sm, repo, path) &&
(sm_oid = git_submodule_wd_oid(sm)) != NULL) (sm_oid = git_submodule_wd_oid(sm)) != NULL)
git_oid_cpy(oid, sm_oid); git_oid_cpy(oid, sm_oid);
else { else {
...@@ -354,23 +369,22 @@ static int oid_for_workdir_item( ...@@ -354,23 +369,22 @@ static int oid_for_workdir_item(
giterr_clear(); giterr_clear();
memset(oid, 0, sizeof(*oid)); memset(oid, 0, sizeof(*oid));
} }
} else if (S_ISLNK(item->mode)) } else if (S_ISLNK(mode)) {
result = git_odb__hashlink(oid, full_path.ptr); result = git_odb__hashlink(oid, full_path.ptr);
else if (!git__is_sizet(item->file_size)) { } else if (!git__is_sizet(size)) {
giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
result = -1; result = -1;
} else { } else {
git_vector filters = GIT_VECTOR_INIT; git_vector filters = GIT_VECTOR_INIT;
result = git_filters_load( result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
&filters, repo, item->path, GIT_FILTER_TO_ODB);
if (result >= 0) { if (result >= 0) {
int fd = git_futils_open_ro(full_path.ptr); int fd = git_futils_open_ro(full_path.ptr);
if (fd < 0) if (fd < 0)
result = fd; result = fd;
else { else {
result = git_odb__hashfd_filtered( result = git_odb__hashfd_filtered(
oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB, &filters); oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
p_close(fd); p_close(fd);
} }
} }
...@@ -378,8 +392,8 @@ static int oid_for_workdir_item( ...@@ -378,8 +392,8 @@ static int oid_for_workdir_item(
git_filters_free(&filters); git_filters_free(&filters);
} }
cleanup:
git_buf_free(&full_path); git_buf_free(&full_path);
return result; return result;
} }
...@@ -439,8 +453,7 @@ static int maybe_modified( ...@@ -439,8 +453,7 @@ static int maybe_modified(
} }
/* if oids and modes match, then file is unmodified */ /* if oids and modes match, then file is unmodified */
else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 && else if (git_oid_equal(&oitem->oid, &nitem->oid) && omode == nmode)
omode == nmode)
status = GIT_DELTA_UNMODIFIED; status = GIT_DELTA_UNMODIFIED;
/* if we have an unknown OID and a workdir iterator, then check some /* if we have an unknown OID and a workdir iterator, then check some
...@@ -493,12 +506,14 @@ static int maybe_modified( ...@@ -493,12 +506,14 @@ static int maybe_modified(
/* if we got here and decided that the files are modified, but we /* if we got here and decided that the files are modified, but we
* haven't calculated the OID of the new item, then calculate it now * haven't calculated the OID of the new item, then calculate it now
*/ */
if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) { if (status != GIT_DELTA_UNMODIFIED &&
if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0) git_oid_iszero(&nitem->oid) && !use_noid)
{
if (git_diff__oid_for_file(diff->repo,
nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
return -1; return -1;
else if (omode == nmode && git_oid_equal(&oitem->oid, &noid)) if (omode == nmode && git_oid_equal(&oitem->oid, &noid))
status = GIT_DELTA_UNMODIFIED; status = GIT_DELTA_UNMODIFIED;
/* store calculated oid so we don't have to recalc later */ /* store calculated oid so we don't have to recalc later */
use_noid = &noid; use_noid = &noid;
} }
...@@ -507,31 +522,14 @@ static int maybe_modified( ...@@ -507,31 +522,14 @@ static int maybe_modified(
diff, status, oitem, omode, nitem, nmode, use_noid); diff, status, oitem, omode, nitem, nmode, use_noid);
} }
static int git_index_entry_cmp_case(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcmp(entry_a->path, entry_b->path);
}
static int git_index_entry_cmp_icase(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcasecmp(entry_a->path, entry_b->path);
}
static bool entry_is_prefixed( static bool entry_is_prefixed(
git_diff_list *diff,
const git_index_entry *item, const git_index_entry *item,
git_iterator *prefix_iterator,
const git_index_entry *prefix_item) const git_index_entry *prefix_item)
{ {
size_t pathlen; size_t pathlen;
if (!prefix_item || if (!prefix_item || diff->prefixcmp(prefix_item->path, item->path))
ITERATOR_PREFIXCMP(*prefix_iterator, prefix_item->path, item->path))
return false; return false;
pathlen = strlen(item->path); pathlen = strlen(item->path);
...@@ -541,6 +539,35 @@ static bool entry_is_prefixed( ...@@ -541,6 +539,35 @@ static bool entry_is_prefixed(
prefix_item->path[pathlen] == '/'); prefix_item->path[pathlen] == '/');
} }
static int diff_list_init_from_iterators(
git_diff_list *diff,
git_iterator *old_iter,
git_iterator *new_iter)
{
diff->old_src = old_iter->type;
diff->new_src = new_iter->type;
/* Use case-insensitive compare if either iterator has
* the ignore_case bit set */
if (!old_iter->ignore_case && !new_iter->ignore_case) {
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
diff->strcmp = strcmp;
diff->strncmp = strncmp;
diff->prefixcmp = git__prefixcmp;
diff->entrycmp = git_index_entry__cmp;
} else {
diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
diff->strcmp = strcasecmp;
diff->strncmp = strncasecmp;
diff->prefixcmp = git__prefixcmp_icase;
diff->entrycmp = git_index_entry__cmp_icase;
}
return 0;
}
static int diff_from_iterators( static int diff_from_iterators(
git_repository *repo, git_repository *repo,
const git_diff_options *opts, /**< can be NULL for defaults */ const git_diff_options *opts, /**< can be NULL for defaults */
...@@ -548,38 +575,32 @@ static int diff_from_iterators( ...@@ -548,38 +575,32 @@ static int diff_from_iterators(
git_iterator *new_iter, git_iterator *new_iter,
git_diff_list **diff_ptr) git_diff_list **diff_ptr)
{ {
int error = 0;
const git_index_entry *oitem, *nitem; const git_index_entry *oitem, *nitem;
git_buf ignore_prefix = GIT_BUF_INIT; git_buf ignore_prefix = GIT_BUF_INIT;
git_diff_list *diff = git_diff_list_alloc(repo, opts); git_diff_list *diff = git_diff_list_alloc(repo, opts);
git_vector_cmp entry_compare;
if (!diff)
goto fail;
diff->old_src = old_iter->type; *diff_ptr = NULL;
diff->new_src = new_iter->type;
/* Use case-insensitive compare if either iterator has if (!diff ||
* the ignore_case bit set */ diff_list_init_from_iterators(diff, old_iter, new_iter) < 0)
if (!old_iter->ignore_case && !new_iter->ignore_case) { goto fail;
entry_compare = git_index_entry_cmp_case;
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
} else {
entry_compare = git_index_entry_cmp_icase;
diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
if (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
/* If one of the iterators doesn't have ignore_case set, /* If one of the iterators doesn't have ignore_case set,
* then that's unfortunate because we'll have to spool * then that's unfortunate because we'll have to spool
* its data, sort it icase, and then use that for our * its data, sort it icase, and then use that for our
* merge join to the other iterator that is icase sorted */ * merge join to the other iterator that is icase sorted */
if (!old_iter->ignore_case) { if (!old_iter->ignore_case &&
if (git_iterator_spoolandsort(&old_iter, old_iter, git_index_entry_cmp_icase, true) < 0) git_iterator_spoolandsort(
&old_iter, old_iter, diff->entrycmp, true) < 0)
goto fail; goto fail;
} else if (!new_iter->ignore_case) {
if (git_iterator_spoolandsort(&new_iter, new_iter, git_index_entry_cmp_icase, true) < 0) if (!new_iter->ignore_case &&
git_iterator_spoolandsort(
&new_iter, new_iter, diff->entrycmp, true) < 0)
goto fail; goto fail;
} }
}
if (git_iterator_current(old_iter, &oitem) < 0 || if (git_iterator_current(old_iter, &oitem) < 0 ||
git_iterator_current(new_iter, &nitem) < 0) git_iterator_current(new_iter, &nitem) < 0)
...@@ -589,7 +610,7 @@ static int diff_from_iterators( ...@@ -589,7 +610,7 @@ static int diff_from_iterators(
while (oitem || nitem) { while (oitem || nitem) {
/* create DELETED records for old items not matched in new */ /* create DELETED records for old items not matched in new */
if (oitem && (!nitem || entry_compare(oitem, nitem) < 0)) { if (oitem && (!nitem || diff->entrycmp(oitem, nitem) < 0)) {
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0) if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
goto fail; goto fail;
...@@ -597,7 +618,7 @@ static int diff_from_iterators( ...@@ -597,7 +618,7 @@ static int diff_from_iterators(
* instead of just generating a DELETE record * instead of just generating a DELETE record
*/ */
if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 && if ((diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
entry_is_prefixed(oitem, new_iter, nitem)) entry_is_prefixed(diff, oitem, nitem))
{ {
/* this entry has become a tree! convert to TYPECHANGE */ /* this entry has become a tree! convert to TYPECHANGE */
git_diff_delta *last = diff_delta__last_for_item(diff, oitem); git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
...@@ -614,13 +635,12 @@ static int diff_from_iterators( ...@@ -614,13 +635,12 @@ static int diff_from_iterators(
/* create ADDED, TRACKED, or IGNORED records for new items not /* create ADDED, TRACKED, or IGNORED records for new items not
* matched in old (and/or descend into directories as needed) * matched in old (and/or descend into directories as needed)
*/ */
else if (nitem && (!oitem || entry_compare(oitem, nitem) > 0)) { else if (nitem && (!oitem || diff->entrycmp(oitem, nitem) > 0)) {
git_delta_t delta_type = GIT_DELTA_UNTRACKED; git_delta_t delta_type = GIT_DELTA_UNTRACKED;
/* check if contained in ignored parent directory */ /* check if contained in ignored parent directory */
if (git_buf_len(&ignore_prefix) && if (git_buf_len(&ignore_prefix) &&
ITERATOR_PREFIXCMP(*old_iter, nitem->path, diff->prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
git_buf_cstr(&ignore_prefix)) == 0)
delta_type = GIT_DELTA_IGNORED; delta_type = GIT_DELTA_IGNORED;
if (S_ISDIR(nitem->mode)) { if (S_ISDIR(nitem->mode)) {
...@@ -629,7 +649,7 @@ static int diff_from_iterators( ...@@ -629,7 +649,7 @@ static int diff_from_iterators(
* directories and it is not under an ignored directory. * directories and it is not under an ignored directory.
*/ */
bool contains_tracked = bool contains_tracked =
entry_is_prefixed(nitem, old_iter, oitem); entry_is_prefixed(diff, nitem, oitem);
bool recurse_untracked = bool recurse_untracked =
(delta_type == GIT_DELTA_UNTRACKED && (delta_type == GIT_DELTA_UNTRACKED &&
(diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0); (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0);
...@@ -693,7 +713,7 @@ static int diff_from_iterators( ...@@ -693,7 +713,7 @@ static int diff_from_iterators(
*/ */
if (delta_type != GIT_DELTA_IGNORED && if (delta_type != GIT_DELTA_IGNORED &&
(diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 && (diff->opts.flags & GIT_DIFF_INCLUDE_TYPECHANGE_TREES) != 0 &&
entry_is_prefixed(nitem, old_iter, oitem)) entry_is_prefixed(diff, nitem, oitem))
{ {
/* this entry was a tree! convert to TYPECHANGE */ /* this entry was a tree! convert to TYPECHANGE */
git_diff_delta *last = diff_delta__last_for_item(diff, oitem); git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
...@@ -711,7 +731,7 @@ static int diff_from_iterators( ...@@ -711,7 +731,7 @@ static int diff_from_iterators(
* (or ADDED and DELETED pair if type changed) * (or ADDED and DELETED pair if type changed)
*/ */
else { else {
assert(oitem && nitem && entry_compare(oitem, nitem) == 0); assert(oitem && nitem && diff->entrycmp(oitem, nitem) == 0);
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 || if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
git_iterator_advance(old_iter, &oitem) < 0 || git_iterator_advance(old_iter, &oitem) < 0 ||
...@@ -720,21 +740,19 @@ static int diff_from_iterators( ...@@ -720,21 +740,19 @@ static int diff_from_iterators(
} }
} }
git_iterator_free(old_iter);
git_iterator_free(new_iter);
git_buf_free(&ignore_prefix);
*diff_ptr = diff; *diff_ptr = diff;
return 0;
fail: fail:
if (!*diff_ptr) {
git_diff_list_free(diff);
error = -1;
}
git_iterator_free(old_iter); git_iterator_free(old_iter);
git_iterator_free(new_iter); git_iterator_free(new_iter);
git_buf_free(&ignore_prefix); git_buf_free(&ignore_prefix);
git_diff_list_free(diff); return error;
*diff_ptr = NULL;
return -1;
} }
......
...@@ -41,6 +41,11 @@ struct git_diff_list { ...@@ -41,6 +41,11 @@ struct git_diff_list {
git_iterator_type_t old_src; git_iterator_type_t old_src;
git_iterator_type_t new_src; git_iterator_type_t new_src;
uint32_t diffcaps; uint32_t diffcaps;
int (*strcmp)(const char *, const char *);
int (*strncmp)(const char *, const char *, size_t);
int (*prefixcmp)(const char *str, const char *pfx);
int (*entrycmp)(const void *a, const void *b);
}; };
extern void git_diff__cleanup_modes( extern void git_diff__cleanup_modes(
...@@ -53,5 +58,8 @@ extern int git_diff_delta__cmp(const void *a, const void *b); ...@@ -53,5 +58,8 @@ extern int git_diff_delta__cmp(const void *a, const void *b);
extern bool git_diff_delta__should_skip( extern bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta); const git_diff_options *opts, const git_diff_delta *delta);
extern int git_diff__oid_for_file(
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
#endif #endif
...@@ -1573,3 +1573,58 @@ int git_diff_patch_to_str( ...@@ -1573,3 +1573,58 @@ int git_diff_patch_to_str(
return error; return error;
} }
int git_diff__paired_foreach(
git_diff_list *idx2head,
git_diff_list *wd2idx,
int (*cb)(void *cbref, git_diff_delta *i2h, git_diff_delta *w2i),
void *cbref)
{
int cmp;
git_diff_delta *i2h, *w2i;
size_t i, j, i_max, j_max;
bool icase = false;
i_max = idx2head ? idx2head->deltas.length : 0;
j_max = wd2idx ? wd2idx->deltas.length : 0;
if (idx2head && wd2idx &&
(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
{
/* Then use the ignore-case sorter... */
icase = true;
/* and assert that both are ignore-case sorted. If this function
* ever needs to support merge joining result sets that are not sorted
* by the same function, then it will need to be extended to do a spool
* and sort on one of the results before merge joining */
assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
}
for (i = 0, j = 0; i < i_max || j < j_max; ) {
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
cmp = !w2i ? -1 : !i2h ? 1 :
STRCMP_CASESELECT(icase, i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) {
if (cb(cbref, i2h, NULL))
return GIT_EUSER;
i++;
} else if (cmp > 0) {
if (cb(cbref, NULL, w2i))
return GIT_EUSER;
j++;
} else {
if (cb(cbref, i2h, w2i))
return GIT_EUSER;
i++; j++;
}
}
return 0;
}
...@@ -83,4 +83,10 @@ typedef struct { ...@@ -83,4 +83,10 @@ typedef struct {
uint32_t diffed : 1; uint32_t diffed : 1;
} diff_delta_context; } diff_delta_context;
extern int git_diff__paired_foreach(
git_diff_list *idx2head,
git_diff_list *wd2idx,
int (*cb)(void *cbref, git_diff_delta *i2h, git_diff_delta *w2i),
void *cbref);
#endif #endif
...@@ -516,7 +516,7 @@ git_index_entry *git_index_get_bypath(git_index *index, const char *path, int st ...@@ -516,7 +516,7 @@ git_index_entry *git_index_get_bypath(git_index *index, const char *path, int st
return git_index_get_byindex(index, pos); return git_index_get_byindex(index, pos);
} }
void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry) void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
{ {
entry->ctime.seconds = (git_time_t)st->st_ctime; entry->ctime.seconds = (git_time_t)st->st_ctime;
entry->mtime.seconds = (git_time_t)st->st_mtime; entry->mtime.seconds = (git_time_t)st->st_mtime;
...@@ -530,6 +530,22 @@ void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry) ...@@ -530,6 +530,22 @@ void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
entry->file_size = st->st_size; entry->file_size = st->st_size;
} }
int git_index_entry__cmp(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcmp(entry_a->path, entry_b->path);
}
int git_index_entry__cmp_icase(const void *a, const void *b)
{
const git_index_entry *entry_a = a;
const git_index_entry *entry_b = b;
return strcasecmp(entry_a->path, entry_b->path);
}
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path) static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
{ {
git_index_entry *entry = NULL; git_index_entry *entry = NULL;
...@@ -568,7 +584,7 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const ...@@ -568,7 +584,7 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
entry = git__calloc(1, sizeof(git_index_entry)); entry = git__calloc(1, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry); GITERR_CHECK_ALLOC(entry);
git_index__init_entry_from_stat(&st, entry); git_index_entry__init_from_stat(entry, &st);
entry->oid = oid; entry->oid = oid;
entry->path = git__strdup(rel_path); entry->path = git__strdup(rel_path);
......
...@@ -41,8 +41,11 @@ struct git_index { ...@@ -41,8 +41,11 @@ struct git_index {
git_vector_cmp reuc_search; git_vector_cmp reuc_search;
}; };
extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry); extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
extern unsigned int git_index__prefix_position(git_index *index, const char *path); extern unsigned int git_index__prefix_position(git_index *index, const char *path);
extern int git_index_entry__cmp(const void *a, const void *b);
extern int git_index_entry__cmp_icase(const void *a, const void *b);
#endif #endif
...@@ -648,7 +648,7 @@ static int workdir_iterator__update_entry(workdir_iterator *wi) ...@@ -648,7 +648,7 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
wi->is_ignored = -1; wi->is_ignored = -1;
git_index__init_entry_from_stat(&ps->st, &wi->entry); git_index_entry__init_from_stat(&wi->entry, &ps->st);
/* need different mode here to keep directories during iteration */ /* need different mode here to keep directories during iteration */
wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode); wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "git2/diff.h" #include "git2/diff.h"
#include "diff.h" #include "diff.h"
#include "diff_output.h"
static unsigned int index_delta2status(git_delta_t index_status) static unsigned int index_delta2status(git_delta_t index_status)
{ {
...@@ -76,21 +77,43 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) ...@@ -76,21 +77,43 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
return st; return st;
} }
typedef struct {
int (*cb)(const char *, unsigned int, void *);
void *cbdata;
} status_user_callback;
static int status_invoke_cb(
void *cbref, git_diff_delta *i2h, git_diff_delta *w2i)
{
status_user_callback *usercb = cbref;
const char *path = NULL;
unsigned int status = 0;
if (w2i) {
path = w2i->old_file.path;
status |= workdir_delta2status(w2i->status);
}
if (i2h) {
path = i2h->old_file.path;
status |= index_delta2status(i2h->status);
}
return usercb->cb(path, status, usercb->cbdata);
}
int git_status_foreach_ext( int git_status_foreach_ext(
git_repository *repo, git_repository *repo,
const git_status_options *opts, const git_status_options *opts,
int (*cb)(const char *, unsigned int, void *), int (*cb)(const char *, unsigned int, void *),
void *cbdata) void *cbdata)
{ {
int err = 0, cmp; int err = 0;
git_diff_options diffopt; git_diff_options diffopt;
git_diff_list *idx2head = NULL, *wd2idx = NULL; git_diff_list *idx2head = NULL, *wd2idx = NULL;
git_tree *head = NULL; git_tree *head = NULL;
git_status_show_t show = git_status_show_t show =
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR; opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
git_diff_delta *i2h, *w2i; status_user_callback usercb;
size_t i, j, i_max, j_max;
bool ignore_case = false;
assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR); assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
...@@ -126,55 +149,19 @@ int git_status_foreach_ext( ...@@ -126,55 +149,19 @@ int git_status_foreach_ext(
(err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0) (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
goto cleanup; goto cleanup;
usercb.cb = cb;
usercb.cbdata = cbdata;
if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) { if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
for (i = 0; !err && i < idx2head->deltas.length; i++) { if ((err = git_diff__paired_foreach(
i2h = GIT_VECTOR_GET(&idx2head->deltas, i); idx2head, NULL, status_invoke_cb, &usercb)) < 0)
if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata)) goto cleanup;
err = GIT_EUSER;
}
git_diff_list_free(idx2head); git_diff_list_free(idx2head);
idx2head = NULL; idx2head = NULL;
} }
i_max = idx2head ? idx2head->deltas.length : 0; err = git_diff__paired_foreach(idx2head, wd2idx, status_invoke_cb, &usercb);
j_max = wd2idx ? wd2idx->deltas.length : 0;
if (idx2head && wd2idx &&
(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) ||
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)))
{
/* Then use the ignore-case sorter... */
ignore_case = true;
/* and assert that both are ignore-case sorted. If this function
* ever needs to support merge joining result sets that are not sorted
* by the same function, then it will need to be extended to do a spool
* and sort on one of the results before merge joining */
assert(0 != (idx2head->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) &&
0 != (wd2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE));
}
for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
cmp = !w2i ? -1 : !i2h ? 1 : STRCMP_CASESELECT(ignore_case, i2h->old_file.path, w2i->old_file.path);
if (cmp < 0) {
if (cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata))
err = GIT_EUSER;
i++;
} else if (cmp > 0) {
if (cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata))
err = GIT_EUSER;
j++;
} else {
if (cb(i2h->old_file.path, index_delta2status(i2h->status) |
workdir_delta2status(w2i->status), cbdata))
err = GIT_EUSER;
i++; j++;
}
}
cleanup: cleanup:
git_tree_free(head); git_tree_free(head);
......
...@@ -371,7 +371,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index) ...@@ -371,7 +371,7 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
memset(&entry, 0, sizeof(entry)); memset(&entry, 0, sizeof(entry));
entry.path = sm->path; entry.path = sm->path;
git_index__init_entry_from_stat(&st, &entry); git_index_entry__init_from_stat(&entry, &st);
/* calling git_submodule_open will have set sm->wd_oid if possible */ /* calling git_submodule_open will have set sm->wd_oid if possible */
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) { if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
......
...@@ -81,6 +81,11 @@ extern int git__prefixcmp(const char *str, const char *prefix); ...@@ -81,6 +81,11 @@ extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix); extern int git__prefixcmp_icase(const char *str, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix); extern int git__suffixcmp(const char *str, const char *suffix);
GIT_INLINE(int) git__signum(int val)
{
return ((val > 0) - (val < 0));
}
extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base); extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base); extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment