diff options
author | Vicent Martà <vicent@github.com> | 2012-11-02 10:00:28 -0700 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2012-11-02 10:00:28 -0700 |
commit | 1362a983165abb5e85e66c2b0474157ba697966a (patch) | |
tree | 2dcd99e991224a26eb860d36151405219ff202bd /src/diff.c | |
parent | 473a7a1e70d05ec125309304d56f060c3e3ad40e (diff) | |
parent | db106d01f093b3e61170e3738d6651a2866cb76e (diff) | |
download | libgit2-1362a983165abb5e85e66c2b0474157ba697966a.tar.gz |
Merge pull request #1014 from arrbee/diff-rename-detection
Initial implementation of diff rename detection
Diffstat (limited to 'src/diff.c')
-rw-r--r-- | src/diff.c | 217 |
1 files changed, 34 insertions, 183 deletions
diff --git a/src/diff.c b/src/diff.c index 9f693bebf..55f6ee7d5 100644 --- a/src/diff.c +++ b/src/diff.c @@ -110,85 +110,6 @@ static git_diff_delta *diff_delta__alloc( return delta; } -static git_diff_delta *diff_delta__dup( - const git_diff_delta *d, git_pool *pool) -{ - git_diff_delta *delta = git__malloc(sizeof(git_diff_delta)); - if (!delta) - return NULL; - - memcpy(delta, d, sizeof(git_diff_delta)); - - delta->old_file.path = git_pool_strdup(pool, d->old_file.path); - if (delta->old_file.path == NULL) - goto fail; - - if (d->new_file.path != d->old_file.path) { - delta->new_file.path = git_pool_strdup(pool, d->new_file.path); - if (delta->new_file.path == NULL) - goto fail; - } else { - delta->new_file.path = delta->old_file.path; - } - - return delta; - -fail: - git__free(delta); - return NULL; -} - -static git_diff_delta *diff_delta__merge_like_cgit( - const git_diff_delta *a, const git_diff_delta *b, git_pool *pool) -{ - git_diff_delta *dup; - - /* Emulate C git for merging two diffs (a la 'git diff <sha>'). - * - * When C git does a diff between the work dir and a tree, it actually - * diffs with the index but uses the workdir contents. This emulates - * those choices so we can emulate the type of diff. - * - * We have three file descriptions here, let's call them: - * f1 = a->old_file - * f2 = a->new_file AND b->old_file - * f3 = b->new_file - */ - - /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */ - if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED) - return diff_delta__dup(a, pool); - - /* otherwise, base this diff on the 'b' diff */ - if ((dup = diff_delta__dup(b, pool)) == NULL) - return NULL; - - /* If 'a' status is uninteresting, then we're done */ - if (a->status == GIT_DELTA_UNMODIFIED) - return dup; - - assert(a->status != GIT_DELTA_UNMODIFIED); - assert(b->status != GIT_DELTA_UNMODIFIED); - - /* A cgit exception is that the diff of a file that is only in the - * index (i.e. not in HEAD nor workdir) is given as empty. - */ - if (dup->status == GIT_DELTA_DELETED) { - if (a->status == GIT_DELTA_ADDED) - dup->status = GIT_DELTA_UNMODIFIED; - /* else don't overwrite DELETE status */ - } else { - dup->status = a->status; - } - - git_oid_cpy(&dup->old_file.oid, &a->old_file.oid); - dup->old_file.mode = a->old_file.mode; - dup->old_file.size = a->old_file.size; - dup->old_file.flags = a->old_file.flags; - - return dup; -} - static int diff_delta__from_one( git_diff_list *diff, git_delta_t status, @@ -332,13 +253,34 @@ static char *diff_strdup_prefix(git_pool *pool, const char *prefix) return git_pool_strndup(pool, prefix, len + 1); } -static int diff_delta__cmp(const void *a, const void *b) +int git_diff_delta__cmp(const void *a, const void *b) { const git_diff_delta *da = a, *db = b; int val = strcmp(da->old_file.path, db->old_file.path); return val ? val : ((int)da->status - (int)db->status); } +bool git_diff_delta__should_skip( + const git_diff_options *opts, const git_diff_delta *delta) +{ + uint32_t flags = opts ? opts->flags : 0; + + if (delta->status == GIT_DELTA_UNMODIFIED && + (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) + return true; + + if (delta->status == GIT_DELTA_IGNORED && + (flags & GIT_DIFF_INCLUDE_IGNORED) == 0) + return true; + + if (delta->status == GIT_DELTA_UNTRACKED && + (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) + return true; + + return false; +} + + static int config_bool(git_config *cfg, const char *name, int defvalue) { int val = defvalue; @@ -361,7 +303,7 @@ static git_diff_list *git_diff_list_alloc( GIT_REFCOUNT_INC(diff); diff->repo = repo; - if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 || + if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 || git_pool_init(&diff->pool, 1, 0) < 0) goto fail; @@ -378,12 +320,23 @@ static git_diff_list *git_diff_list_alloc( diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ + /* TODO: there are certain config settings where even if we were + * not given an options structure, we need the diff list to have one + * so that we can store the altered default values. + * + * - diff.ignoreSubmodules + * - diff.mnemonicprefix + * - diff.noprefix + */ + if (opts == NULL) return diff; memcpy(&diff->opts, opts, sizeof(git_diff_options)); memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec)); + /* TODO: handle config diff.mnemonicprefix, diff.noprefix */ + diff->opts.old_prefix = diff_strdup_prefix(&diff->pool, opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT); diff->opts.new_prefix = diff_strdup_prefix(&diff->pool, @@ -980,105 +933,3 @@ on_error: git_iterator_free(a); return error; } - - -bool git_diff_delta__should_skip( - const git_diff_options *opts, const git_diff_delta *delta) -{ - uint32_t flags = opts ? opts->flags : 0; - - if (delta->status == GIT_DELTA_UNMODIFIED && - (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) - return true; - - if (delta->status == GIT_DELTA_IGNORED && - (flags & GIT_DIFF_INCLUDE_IGNORED) == 0) - return true; - - if (delta->status == GIT_DELTA_UNTRACKED && - (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) - return true; - - return false; -} - - -int git_diff_merge( - git_diff_list *onto, - const git_diff_list *from) -{ - int error = 0; - git_pool onto_pool; - git_vector onto_new; - git_diff_delta *delta; - bool ignore_case = false; - unsigned int i, j; - - assert(onto && from); - - if (!from->deltas.length) - return 0; - - if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 || - 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) - { - 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); - } - - for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) { - git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i); - const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j); - int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path); - - if (cmp < 0) { - delta = diff_delta__dup(o, &onto_pool); - i++; - } else if (cmp > 0) { - delta = diff_delta__dup(f, &onto_pool); - j++; - } else { - delta = diff_delta__merge_like_cgit(o, f, &onto_pool); - i++; - j++; - } - - /* the ignore rules for the target may not match the source - * or the result of a merged delta could be skippable... - */ - if (git_diff_delta__should_skip(&onto->opts, delta)) { - git__free(delta); - continue; - } - - if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0) - break; - } - - if (!error) { - git_vector_swap(&onto->deltas, &onto_new); - git_pool_swap(&onto->pool, &onto_pool); - onto->new_src = from->new_src; - - /* prefix strings also come from old pool, so recreate those.*/ - onto->opts.old_prefix = - git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix); - onto->opts.new_prefix = - git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); - } - - git_vector_foreach(&onto_new, i, delta) - git__free(delta); - git_vector_free(&onto_new); - git_pool_clear(&onto_pool); - - return error; -} |