diff options
| author | Russell Belfer <rb@github.com> | 2012-06-08 15:17:41 -0700 |
|---|---|---|
| committer | Russell Belfer <rb@github.com> | 2012-06-08 15:17:41 -0700 |
| commit | e0b110edb1d0863ad454bfce0b0c3ac1ceef3392 (patch) | |
| tree | 11e245f0a0ef17dc043029575f1594d1ca558156 /src | |
| parent | 80c03754ae57ad387a6c02a5e58a59a327d44638 (diff) | |
| parent | ac971ecfdb11af8fa36c15eb9afb0fd8545daf08 (diff) | |
| download | libgit2-e0b110edb1d0863ad454bfce0b0c3ac1ceef3392.tar.gz | |
Merge pull request #744 from arrbee/fix-filemodes
Fix filemode comparison in diffs
Diffstat (limited to 'src')
| -rw-r--r-- | src/diff.c | 104 | ||||
| -rw-r--r-- | src/diff.h | 5 | ||||
| -rw-r--r-- | src/diff_output.c | 24 | ||||
| -rw-r--r-- | src/path.c | 10 |
4 files changed, 94 insertions, 49 deletions
diff --git a/src/diff.c b/src/diff.c index 90baa9588..02b89b46e 100644 --- a/src/diff.c +++ b/src/diff.c @@ -130,37 +130,50 @@ fail: 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 = diff_delta__dup(a, pool); - if (!dup) - return NULL; - - if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0) - return dup; - - git_oid_cpy(&dup->new_file.oid, &b->new_file.oid); - - dup->new_file.mode = b->new_file.mode; - dup->new_file.size = b->new_file.size; - dup->new_file.flags = b->new_file.flags; + 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 (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) { - if (dup->status == GIT_DELTA_DELETED) - /* preserve pending delete info */; - else if (b->status == GIT_DELTA_UNTRACKED || - b->status == GIT_DELTA_IGNORED) - dup->status = b->status; - else + + /* 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; } - else if (dup->status == GIT_DELTA_UNMODIFIED || - b->status == GIT_DELTA_DELETED) - dup->status = b->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; } @@ -214,7 +227,9 @@ static int diff_delta__from_two( git_diff_list *diff, git_delta_t status, const git_index_entry *old_entry, + uint32_t old_mode, const git_index_entry *new_entry, + uint32_t new_mode, git_oid *new_oid) { git_diff_delta *delta; @@ -224,19 +239,22 @@ static int diff_delta__from_two( return 0; if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) { - const git_index_entry *temp = old_entry; + uint32_t temp_mode = old_mode; + const git_index_entry *temp_entry = old_entry; old_entry = new_entry; - new_entry = temp; + new_entry = temp_entry; + old_mode = new_mode; + new_mode = temp_mode; } delta = diff_delta__alloc(diff, status, old_entry->path); GITERR_CHECK_ALLOC(delta); - delta->old_file.mode = old_entry->mode; + delta->old_file.mode = old_mode; git_oid_cpy(&delta->old_file.oid, &old_entry->oid); delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID; - delta->new_file.mode = new_entry->mode; + delta->new_file.mode = new_mode; git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid); if (new_oid || !git_oid_iszero(&new_entry->oid)) delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID; @@ -300,7 +318,7 @@ static git_diff_list *git_diff_list_alloc( if (config_bool(cfg, "core.ignorestat", 0)) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED; if (config_bool(cfg, "core.filemode", 1)) - diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT; + diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS; if (config_bool(cfg, "core.trustctime", 1)) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ @@ -419,7 +437,7 @@ static int oid_for_workdir_item( return result; } -#define EXEC_BIT_MASK 0000111 +#define MODE_BITS_MASK 0000777 static int maybe_modified( git_iterator *old_iter, @@ -443,13 +461,13 @@ static int maybe_modified( !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS)) nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK); - /* on platforms with no execmode, clear exec bit from comparisons */ - if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) { - omode = omode & ~EXEC_BIT_MASK; - nmode = nmode & ~EXEC_BIT_MASK; - } + /* on platforms with no execmode, just preserve old mode */ + if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) && + (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) && + new_iter->type == GIT_ITERATOR_WORKDIR) + nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); - /* support "assume unchanged" (badly, b/c we still stat everything) */ + /* support "assume unchanged" (poorly, b/c we still stat everything) */ if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0) status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ? GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED; @@ -471,8 +489,13 @@ static int maybe_modified( omode == nmode) status = GIT_DELTA_UNMODIFIED; - /* if we have a workdir item with an unknown oid, check deeper */ - else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) { + /* if modes match and we have an unknown OID and a workdir iterator, + * then check deeper for matching + */ + else if (omode == nmode && + git_oid_iszero(&nitem->oid) && + new_iter->type == GIT_ITERATOR_WORKDIR) + { /* TODO: add check against index file st_mtime to avoid racy-git */ /* if they files look exactly alike, then we'll assume the same */ @@ -517,7 +540,8 @@ static int maybe_modified( use_noid = &noid; } - return diff_delta__from_two(diff, status, oitem, nitem, use_noid); + return diff_delta__from_two( + diff, status, oitem, omode, nitem, nmode, use_noid); } static int diff_from_iterators( @@ -772,6 +796,12 @@ int git_diff_merge( 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(&onto->pool, onto->opts.old_prefix); + onto->opts.new_prefix = + git_pool_strdup(&onto->pool, onto->opts.new_prefix); } git_vector_foreach(&onto_new, i, delta) diff --git a/src/diff.h b/src/diff.h index ac2457956..6cc854fbd 100644 --- a/src/diff.h +++ b/src/diff.h @@ -20,7 +20,7 @@ enum { GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */ GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */ - GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */ + GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */ }; @@ -36,5 +36,8 @@ struct git_diff_list { uint32_t diffcaps; }; +extern void git_diff__cleanup_modes( + uint32_t diffcaps, uint32_t *omode, uint32_t *nmode); + #endif diff --git a/src/diff_output.c b/src/diff_output.c index 1c65e1bb8..92f7f8f2f 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -83,12 +83,13 @@ static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len) info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0) return -1; - /* deal with adding and removing newline at EOF */ + /* This should only happen if we are adding a line that does not + * have a newline at the end and the old code did. In that case, + * we have a ADD with a DEL_EOFNL as a pair. + */ if (len == 3) { - if (origin == GIT_DIFF_LINE_ADDITION) - origin = GIT_DIFF_LINE_ADD_EOFNL; - else - origin = GIT_DIFF_LINE_DEL_EOFNL; + origin = (origin == GIT_DIFF_LINE_ADDITION) ? + GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL; return info->line_cb( info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size); @@ -359,7 +360,7 @@ int git_diff_foreach( /* map files */ if (delta->binary != 1 && - (hunk_cb || line_cb) && + (hunk_cb || line_cb || git_oid_iszero(&delta->old_file.oid)) && (delta->status == GIT_DELTA_DELETED || delta->status == GIT_DELTA_MODIFIED)) { @@ -397,7 +398,9 @@ int git_diff_foreach( /* since we did not have the definitive oid, we may have * incorrect status and need to skip this item. */ - if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) { + if (delta->old_file.mode == delta->new_file.mode && + !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)) + { delta->status = GIT_DELTA_UNMODIFIED; if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0) goto cleanup; @@ -420,7 +423,8 @@ int git_diff_foreach( */ if (file_cb != NULL) { - error = file_cb(data, delta, (float)info.index / diff->deltas.length); + error = file_cb( + data, delta, (float)info.index / diff->deltas.length); if (error < 0) goto cleanup; } @@ -433,6 +437,10 @@ int git_diff_foreach( if (!old_data.len && !new_data.len) goto cleanup; + /* nothing to do if only diff was a mode change */ + if (!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid)) + goto cleanup; + assert(hunk_cb || line_cb); info.delta = delta; diff --git a/src/path.c b/src/path.c index 84edf6d89..1d85559a9 100644 --- a/src/path.c +++ b/src/path.c @@ -17,6 +17,10 @@ #include <stdio.h> #include <ctype.h> +#ifdef GIT_WIN32 +#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':') +#endif + /* * Based on the Android implementation, BSD licensed. * Check http://android.git.kernel.org/ @@ -105,7 +109,7 @@ int git_path_dirname_r(git_buf *buffer, const char *path) /* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return 'C:/' here */ - if (len == 2 && isalpha(path[0]) && path[1] == ':') { + if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) { len = 3; goto Exit; } @@ -170,7 +174,7 @@ int git_path_root(const char *path) #ifdef GIT_WIN32 /* Does the root of the path look like a windows drive ? */ - if (isalpha(path[0]) && (path[1] == ':')) + if (LOOKS_LIKE_DRIVE_PREFIX(path)) offset += 2; /* Are we dealing with a windows network path? */ @@ -210,7 +214,7 @@ int git_path_prettify(git_buf *path_out, const char *path, const char *base) giterr_set(GITERR_OS, "Failed to resolve path '%s'", path); git_buf_clear(path_out); - + return error; } |
