New status fixes This adds support for roughly-right tracking of submodules (although it does not recurse into submodules to detect internal modifications a la core git), and it adds support for including unmodified files in diff iteration if requested.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 0e7c02f..cb3ef4e 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -39,7 +39,8 @@ enum {
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
GIT_DIFF_PATIENCE = (1 << 6),
GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
- GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8)
+ GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
+ GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9)
};
/**
diff --git a/include/git2/status.h b/include/git2/status.h
index 3390529..a24d39f 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -83,14 +83,14 @@ typedef enum {
* the workdir files are included in the status "show" option.
* Right now, there is no option to include all files in
* directories that are ignored completely.
- * - GIT_STATUS_OPT_EXCLUDE_UNMODIFIED indicates that callback
- * do not need to be made on unmodified files.
+ * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback
+ * should be made even on unmodified files.
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories
* which appear to be submodules should just be skipped over.
*/
#define GIT_STATUS_OPT_INCLUDE_UNTRACKED (1 << 0)
#define GIT_STATUS_OPT_INCLUDE_IGNORED (1 << 1)
-#define GIT_STATUS_OPT_EXCLUDE_UNMODIFIED (1 << 2)
+#define GIT_STATUS_OPT_INCLUDE_UNMODIFIED (1 << 2)
#define GIT_STATUS_OPT_EXCLUDE_SUBMODULES (1 << 3)
/**
diff --git a/src/diff.c b/src/diff.c
index 469a6c0..3f8041a 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -132,7 +132,17 @@ static int diff_delta__from_one(
git_delta_t status,
const git_index_entry *entry)
{
- git_diff_delta *delta = diff_delta__alloc(diff, status, entry->path);
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return 0;
+
+ if (status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return 0;
+
+ delta = diff_delta__alloc(diff, status, entry->path);
GITERR_CHECK_ALLOC(delta);
/* This fn is just for single-sided diffs */
@@ -168,6 +178,10 @@ static int diff_delta__from_two(
{
git_diff_delta *delta;
+ if (status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return 0;
+
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
const git_index_entry *temp = old;
old = new;
@@ -320,26 +334,30 @@ static int maybe_modified(
git_diff_list *diff)
{
git_oid noid, *use_noid = NULL;
+ git_delta_t status = GIT_DELTA_MODIFIED;
GIT_UNUSED(old);
/* support "assume unchanged" & "skip worktree" bits */
if ((oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) != 0 ||
(oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
- return 0;
+ status = GIT_DELTA_UNMODIFIED;
- if (GIT_MODE_TYPE(oitem->mode) != GIT_MODE_TYPE(nitem->mode)) {
+ /* if basic type of file changed, then split into delete and add */
+ else if (GIT_MODE_TYPE(oitem->mode) != GIT_MODE_TYPE(nitem->mode)) {
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 (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
- oitem->mode == nitem->mode)
- return 0;
+ /* if oids and modes match, then file is unmodified */
+ else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+ oitem->mode == nitem->mode)
+ status = GIT_DELTA_UNMODIFIED;
- if (git_oid_iszero(&nitem->oid) && new->type == GIT_ITERATOR_WORKDIR) {
+ /* if we have a workdir item with an unknown oid, check deeper */
+ else if (git_oid_iszero(&nitem->oid) && new->type == GIT_ITERATOR_WORKDIR) {
/* if they files look exactly alike, then we'll assume the same */
if (oitem->file_size == nitem->file_size &&
oitem->ctime.seconds == nitem->ctime.seconds &&
@@ -348,25 +366,28 @@ static int maybe_modified(
oitem->ino == nitem->ino &&
oitem->uid == nitem->uid &&
oitem->gid == nitem->gid)
- return 0;
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* TODO? should we do anything special with submodules? */
+ else if (S_ISGITLINK(nitem->mode))
+ status = GIT_DELTA_UNMODIFIED;
/* TODO: check git attributes so we will not have to read the file
* in if it is marked binary.
*/
- if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
+ else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
return -1;
- if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+ else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
oitem->mode == nitem->mode)
- return 0;
+ status = GIT_DELTA_UNMODIFIED;
/* store calculated oid so we don't have to recalc later */
use_noid = &noid;
}
- return diff_delta__from_two(
- diff, GIT_DELTA_MODIFIED, oitem, nitem, use_noid);
+ return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
}
static int diff_from_iterators(
diff --git a/src/diff_output.c b/src/diff_output.c
index 638cabc..f4c2143 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -314,7 +314,8 @@ int git_diff_foreach(
git_blob *old_blob = NULL, *new_blob = NULL;
git_map old_data, new_data;
- if (delta->status == GIT_DELTA_UNMODIFIED)
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
continue;
if (delta->status == GIT_DELTA_IGNORED &&
@@ -377,7 +378,8 @@ int git_diff_foreach(
*/
if (git_oid_cmp(&delta->old.oid, &delta->new.oid) == 0) {
delta->status = GIT_DELTA_UNMODIFIED;
- goto cleanup;
+ if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ goto cleanup;
}
}
}
diff --git a/src/iterator.c b/src/iterator.c
index cc15b5f..001bee7 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -427,7 +427,12 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
/* detect submodules */
if (S_ISDIR(wi->entry.mode) &&
git_path_contains(&wi->path, DOT_GIT) == true)
+ {
+ size_t len = strlen(wi->entry.path);
+ assert(wi->entry.path[len - 1] == '/');
+ wi->entry.path[len - 1] = '\0';
wi->entry.mode = S_IFGITLINK;
+ }
return 0;
}
diff --git a/src/status.c b/src/status.c
index eab7c88..0c7a622 100644
--- a/src/status.c
+++ b/src/status.c
@@ -137,7 +137,13 @@ int git_status_foreach_ext(
}
memset(&diffopt, 0, sizeof(diffopt));
- diffopt.flags = GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ 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)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
+ /* TODO: support EXCLUDE_SUBMODULES flag */
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
(err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
@@ -180,9 +186,9 @@ int git_status_foreach(
{
git_status_options opts;
- opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
- GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED;
return git_status_foreach_ext(repo, &opts, callback, payload);
}