Commit bc16fd3e by Russell Belfer

Introduce status/diff TYPECHANGE flags

When I wrote the diff code, I based it on core git's diff output
which tends to split a type change into an add and a delete.  But
core git's status has the notion of a T (typechange) flag for a
file.  This introduces that into our status APIs and modifies the
diff code so it can be forced to not split type changes.
parent fade21db
...@@ -49,6 +49,7 @@ enum { ...@@ -49,6 +49,7 @@ enum {
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11), GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12), GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13), GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
GIT_DIFF_DONT_SPLIT_TYPECHANGE = (1 << 14),
}; };
/** /**
...@@ -116,7 +117,8 @@ typedef enum { ...@@ -116,7 +117,8 @@ typedef enum {
GIT_DELTA_RENAMED = 4, GIT_DELTA_RENAMED = 4,
GIT_DELTA_COPIED = 5, GIT_DELTA_COPIED = 5,
GIT_DELTA_IGNORED = 6, GIT_DELTA_IGNORED = 6,
GIT_DELTA_UNTRACKED = 7 GIT_DELTA_UNTRACKED = 7,
GIT_DELTA_TYPECHANGE = 8,
} git_delta_t; } git_delta_t;
/** /**
......
...@@ -19,19 +19,22 @@ ...@@ -19,19 +19,22 @@
*/ */
GIT_BEGIN_DECL GIT_BEGIN_DECL
enum { typedef enum {
GIT_STATUS_CURRENT = 0, GIT_STATUS_CURRENT = 0,
GIT_STATUS_INDEX_NEW = (1 << 0), GIT_STATUS_INDEX_NEW = (1u << 0),
GIT_STATUS_INDEX_MODIFIED = (1 << 1), GIT_STATUS_INDEX_MODIFIED = (1u << 1),
GIT_STATUS_INDEX_DELETED = (1 << 2), 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_NEW = (1u << 7),
GIT_STATUS_WT_MODIFIED = (1 << 4), GIT_STATUS_WT_MODIFIED = (1u << 8),
GIT_STATUS_WT_DELETED = (1 << 5), 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. * Gather file statuses and run a callback for each one.
......
...@@ -508,6 +508,7 @@ static int maybe_modified( ...@@ -508,6 +508,7 @@ static int maybe_modified(
git_delta_t status = GIT_DELTA_MODIFIED; git_delta_t status = GIT_DELTA_MODIFIED;
unsigned int omode = oitem->mode; unsigned int omode = oitem->mode;
unsigned int nmode = nitem->mode; unsigned int nmode = nitem->mode;
bool new_is_workdir = (new_iter->type == GIT_ITERATOR_WORKDIR);
GIT_UNUSED(old_iter); GIT_UNUSED(old_iter);
...@@ -515,15 +516,14 @@ static int maybe_modified( ...@@ -515,15 +516,14 @@ static int maybe_modified(
return 0; return 0;
/* on platforms with no symlinks, preserve mode of existing symlinks */ /* on platforms with no symlinks, preserve mode of existing symlinks */
if (S_ISLNK(omode) && S_ISREG(nmode) && if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS) && !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
new_iter->type == GIT_ITERATOR_WORKDIR)
nmode = omode; nmode = omode;
/* on platforms with no execmode, just preserve old mode */ /* on platforms with no execmode, just preserve old mode */
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) && if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
(nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) && (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); nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
/* support "assume unchanged" (poorly, b/c we still stat everything) */ /* support "assume unchanged" (poorly, b/c we still stat everything) */
...@@ -537,10 +537,14 @@ static int maybe_modified( ...@@ -537,10 +537,14 @@ static int maybe_modified(
/* if basic type of file changed, then split into delete and add */ /* if basic type of file changed, then split into delete and add */
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || if ((diff->opts.flags & GIT_DIFF_DONT_SPLIT_TYPECHANGE) != 0)
diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) status = GIT_DELTA_TYPECHANGE;
return -1; else {
return 0; 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 */ /* if oids and modes match, then file is unmodified */
...@@ -551,9 +555,7 @@ static int maybe_modified( ...@@ -551,9 +555,7 @@ static int maybe_modified(
/* 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
* circumstances that can accelerate things or need special handling * circumstances that can accelerate things or need special handling
*/ */
else if (git_oid_iszero(&nitem->oid) && else if (git_oid_iszero(&nitem->oid) && new_is_workdir) {
new_iter->type == GIT_ITERATOR_WORKDIR)
{
/* TODO: add check against index file st_mtime to avoid racy-git */ /* TODO: add check against index file st_mtime to avoid racy-git */
/* if the stat data looks exactly alike, then assume the same */ /* if the stat data looks exactly alike, then assume the same */
...@@ -600,7 +602,7 @@ static int maybe_modified( ...@@ -600,7 +602,7 @@ 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_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) if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
return -1; return -1;
else if (omode == nmode && git_oid_equal(&oitem->oid, &noid)) else if (omode == nmode && git_oid_equal(&oitem->oid, &noid))
......
...@@ -25,7 +25,6 @@ static unsigned int index_delta2status(git_delta_t index_status) ...@@ -25,7 +25,6 @@ static unsigned int index_delta2status(git_delta_t index_status)
switch (index_status) { switch (index_status) {
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
case GIT_DELTA_COPIED: case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED:
st = GIT_STATUS_INDEX_NEW; st = GIT_STATUS_INDEX_NEW;
break; break;
case GIT_DELTA_DELETED: case GIT_DELTA_DELETED:
...@@ -34,6 +33,12 @@ static unsigned int index_delta2status(git_delta_t index_status) ...@@ -34,6 +33,12 @@ static unsigned int index_delta2status(git_delta_t index_status)
case GIT_DELTA_MODIFIED: case GIT_DELTA_MODIFIED:
st = GIT_STATUS_INDEX_MODIFIED; st = GIT_STATUS_INDEX_MODIFIED;
break; break;
case GIT_DELTA_RENAMED:
st = GIT_STATUS_INDEX_RENAMED;
break;
case GIT_DELTA_TYPECHANGE:
st = GIT_STATUS_INDEX_TYPECHANGE;
break;
default: default:
break; break;
} }
...@@ -47,8 +52,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) ...@@ -47,8 +52,8 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
switch (workdir_status) { switch (workdir_status) {
case GIT_DELTA_ADDED: case GIT_DELTA_ADDED:
case GIT_DELTA_COPIED:
case GIT_DELTA_RENAMED: case GIT_DELTA_RENAMED:
case GIT_DELTA_COPIED:
case GIT_DELTA_UNTRACKED: case GIT_DELTA_UNTRACKED:
st = GIT_STATUS_WT_NEW; st = GIT_STATUS_WT_NEW;
break; break;
...@@ -61,6 +66,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status) ...@@ -61,6 +66,9 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
case GIT_DELTA_IGNORED: case GIT_DELTA_IGNORED:
st = GIT_STATUS_IGNORED; st = GIT_STATUS_IGNORED;
break; break;
case GIT_DELTA_TYPECHANGE:
st = GIT_STATUS_WT_TYPECHANGE;
break;
default: default:
break; break;
} }
...@@ -92,6 +100,8 @@ int git_status_foreach_ext( ...@@ -92,6 +100,8 @@ int git_status_foreach_ext(
memset(&diffopt, 0, sizeof(diffopt)); memset(&diffopt, 0, sizeof(diffopt));
memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec)); memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
diffopt.flags = GIT_DIFF_DONT_SPLIT_TYPECHANGE;
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0) if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED; diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0) if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
......
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