diff --git a/include/git2/diff.h b/include/git2/diff.h index 121c40307..24556db73 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -49,6 +49,7 @@ enum { GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11), GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12), GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13), + GIT_DIFF_DONT_SPLIT_TYPECHANGE = (1 << 14), }; /** @@ -116,7 +117,8 @@ typedef enum { GIT_DELTA_RENAMED = 4, GIT_DELTA_COPIED = 5, GIT_DELTA_IGNORED = 6, - GIT_DELTA_UNTRACKED = 7 + GIT_DELTA_UNTRACKED = 7, + GIT_DELTA_TYPECHANGE = 8, } git_delta_t; /** diff --git a/include/git2/status.h b/include/git2/status.h index cc94d7680..725e3ef59 100644 --- a/include/git2/status.h +++ b/include/git2/status.h @@ -19,19 +19,22 @@ */ GIT_BEGIN_DECL -enum { - GIT_STATUS_CURRENT = 0, +typedef enum { + GIT_STATUS_CURRENT = 0, - GIT_STATUS_INDEX_NEW = (1 << 0), - GIT_STATUS_INDEX_MODIFIED = (1 << 1), - GIT_STATUS_INDEX_DELETED = (1 << 2), + GIT_STATUS_INDEX_NEW = (1u << 0), + GIT_STATUS_INDEX_MODIFIED = (1u << 1), + GIT_STATUS_INDEX_DELETED = (1u << 2), + GIT_STATUS_INDEX_RENAMED = (1u << 3), + GIT_STATUS_INDEX_TYPECHANGE = (1u << 4), - GIT_STATUS_WT_NEW = (1 << 3), - GIT_STATUS_WT_MODIFIED = (1 << 4), - GIT_STATUS_WT_DELETED = (1 << 5), + GIT_STATUS_WT_NEW = (1u << 7), + GIT_STATUS_WT_MODIFIED = (1u << 8), + GIT_STATUS_WT_DELETED = (1u << 9), + GIT_STATUS_WT_TYPECHANGE = (1u << 10), - GIT_STATUS_IGNORED = (1 << 6), -}; + GIT_STATUS_IGNORED = (1u << 14), +} git_status_t; /** * Gather file statuses and run a callback for each one. diff --git a/src/diff.c b/src/diff.c index 8ab8af3a1..f88bda4aa 100644 --- a/src/diff.c +++ b/src/diff.c @@ -508,6 +508,7 @@ static int maybe_modified( git_delta_t status = GIT_DELTA_MODIFIED; unsigned int omode = oitem->mode; unsigned int nmode = nitem->mode; + bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR); GIT_UNUSED(old_iter); @@ -515,15 +516,14 @@ static int maybe_modified( return 0; /* on platforms with no symlinks, preserve mode of existing symlinks */ - if (S_ISLNK(omode) && S_ISREG(nmode) && - !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS) && - new_iter->type == GIT_ITERATOR_WORKDIR) + if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir && + !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS)) nmode = omode; /* 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) + new_is_workdir) nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK); /* support "assume unchanged" (poorly, b/c we still stat everything) */ @@ -537,10 +537,14 @@ static int maybe_modified( /* if basic type of file changed, then split into delete and add */ else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { - if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || - diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) - return -1; - return 0; + if ((diff->opts.flags & GIT_DIFF_DONT_SPLIT_TYPECHANGE) != 0) + status = GIT_DELTA_TYPECHANGE; + else { + if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || + diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) + return -1; + return 0; + } } /* if oids and modes match, then file is unmodified */ @@ -551,9 +555,7 @@ static int maybe_modified( /* if we have an unknown OID and a workdir iterator, then check some * circumstances that can accelerate things or need special handling */ - else if (git_oid_iszero(&nitem->oid) && - new_iter->type == GIT_ITERATOR_WORKDIR) - { + else if (git_oid_iszero(&nitem->oid) && new_is_workdir) { /* TODO: add check against index file st_mtime to avoid racy-git */ /* if the stat data looks exactly alike, then assume the same */ @@ -600,7 +602,7 @@ static int maybe_modified( /* 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 */ - if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) { + if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) { if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0) return -1; else if (omode == nmode && git_oid_equal(&oitem->oid, &noid)) diff --git a/src/status.c b/src/status.c index 3a0ed075f..e39006e93 100644 --- a/src/status.c +++ b/src/status.c @@ -25,7 +25,6 @@ static unsigned int index_delta2status(git_delta_t index_status) switch (index_status) { case GIT_DELTA_ADDED: case GIT_DELTA_COPIED: - case GIT_DELTA_RENAMED: st = GIT_STATUS_INDEX_NEW; break; case GIT_DELTA_DELETED: @@ -34,6 +33,12 @@ static unsigned int index_delta2status(git_delta_t index_status) case GIT_DELTA_MODIFIED: st = GIT_STATUS_INDEX_MODIFIED; break; + case GIT_DELTA_RENAMED: + st = GIT_STATUS_INDEX_RENAMED; + break; + case GIT_DELTA_TYPECHANGE: + st = GIT_STATUS_INDEX_TYPECHANGE; + break; default: break; } @@ -47,8 +52,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) switch (workdir_status) { case GIT_DELTA_ADDED: - case GIT_DELTA_COPIED: case GIT_DELTA_RENAMED: + case GIT_DELTA_COPIED: case GIT_DELTA_UNTRACKED: st = GIT_STATUS_WT_NEW; break; @@ -61,6 +66,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) case GIT_DELTA_IGNORED: st = GIT_STATUS_IGNORED; break; + case GIT_DELTA_TYPECHANGE: + st = GIT_STATUS_WT_TYPECHANGE; + break; default: break; } @@ -92,6 +100,8 @@ int git_status_foreach_ext( memset(&diffopt, 0, sizeof(diffopt)); memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); + diffopt.flags = GIT_DIFF_DONT_SPLIT_TYPECHANGE; + if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)