Fix a few diff bugs with directory content There are a few cases where diff should leave directories in the diff list if we want to match core git, such as when the directory contains a .git dir. That feature was lost when I introduced some of the new submodule handling. This restores that and then fixes a couple of related to diff output that are triggered by having diffs with directories in them. Also, this adds a new flag that can be passed to diff if you want diff output to actually include the file content of any untracked files.
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
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 1f7f8ab..121c403 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -47,7 +47,8 @@ enum {
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
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),
};
/**
diff --git a/src/diff.c b/src/diff.c
index 7a0051a..8718e5a 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -669,7 +669,8 @@ static int diff_from_iterators(
/* check if contained in ignored parent directory */
if (git_buf_len(&ignore_prefix) &&
- ITERATOR_PREFIXCMP(*old_iter, nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
+ ITERATOR_PREFIXCMP(*old_iter, nitem->path,
+ git_buf_cstr(&ignore_prefix)) == 0)
delta_type = GIT_DELTA_IGNORED;
if (S_ISDIR(nitem->mode)) {
@@ -677,10 +678,23 @@ static int diff_from_iterators(
* it or if the user requested the contents of untracked
* directories and it is not under an ignored directory.
*/
- if ((oitem && ITERATOR_PREFIXCMP(*old_iter, oitem->path, nitem->path) == 0) ||
+ bool contains_tracked =
+ (oitem &&
+ !ITERATOR_PREFIXCMP(*old_iter, oitem->path, nitem->path));
+ bool recurse_untracked =
(delta_type == GIT_DELTA_UNTRACKED &&
- (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
- {
+ (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0);
+
+ /* do not advance into directories that contain a .git file */
+ if (!contains_tracked && recurse_untracked) {
+ git_buf *full = NULL;
+ if (git_iterator_current_workdir_path(new_iter, &full) < 0)
+ goto fail;
+ if (git_path_contains_dir(full, DOT_GIT))
+ recurse_untracked = false;
+ }
+
+ if (contains_tracked || recurse_untracked) {
/* if this directory is ignored, remember it as the
* "ignore_prefix" for processing contained items
*/
diff --git a/src/diff_output.c b/src/diff_output.c
index f5f6c38..9fee127 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -321,6 +321,9 @@ static int get_workdir_content(
if (file->mode == GIT_FILEMODE_COMMIT)
return get_workdir_sm_content(ctxt, file, map);
+ if (S_ISDIR(file->mode))
+ return 0;
+
if (git_buf_joinpath(&path, wd, file->path) < 0)
return -1;
@@ -535,6 +538,11 @@ static int diff_patch_load(
break;
case GIT_DELTA_MODIFIED:
break;
+ case GIT_DELTA_UNTRACKED:
+ delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA;
+ if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)
+ delta->new_file.flags |= GIT_DIFF_FILE_NO_DATA;
+ break;
default:
delta->new_file.flags |= GIT_DIFF_FILE_NO_DATA;
delta->old_file.flags |= GIT_DIFF_FILE_NO_DATA;
@@ -1070,6 +1078,9 @@ static int print_patch_file(
GIT_UNUSED(progress);
+ if (S_ISDIR(delta->new_file.mode))
+ return 0;
+
if (!oldpfx)
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
@@ -1134,6 +1145,9 @@ static int print_patch_hunk(
{
diff_print_info *pi = data;
+ if (S_ISDIR(d->new_file.mode))
+ return 0;
+
git_buf_clear(pi->buf);
if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
return -1;
@@ -1158,6 +1172,9 @@ static int print_patch_line(
{
diff_print_info *pi = data;
+ if (S_ISDIR(delta->new_file.mode))
+ return 0;
+
git_buf_clear(pi->buf);
if (line_origin == GIT_DIFF_LINE_ADDITION ||
diff --git a/src/iterator.c b/src/iterator.c
index e52554d..267687e 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -905,3 +905,15 @@ int git_iterator_cmp(
return ITERATOR_PREFIXCMP(*iter, entry->path, path_prefix);
}
+int git_iterator_current_workdir_path(git_iterator *iter, git_buf **path)
+{
+ workdir_iterator *wi = (workdir_iterator *)iter;
+
+ if (iter->type != GIT_ITERATOR_WORKDIR || !wi->entry.path)
+ *path = NULL;
+ else
+ *path = &wi->path;
+
+ return 0;
+}
+
diff --git a/src/iterator.h b/src/iterator.h
index 11cd218..29c8985 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -10,6 +10,7 @@
#include "common.h"
#include "git2/index.h"
#include "vector.h"
+#include "buffer.h"
#define ITERATOR_PREFIXCMP(ITER, STR, PREFIX) (((ITER).ignore_case) ? \
git__prefixcmp_icase((STR), (PREFIX)) : \
@@ -166,4 +167,11 @@ extern int git_iterator_advance_into_directory(
extern int git_iterator_cmp(
git_iterator *iter, const char *path_prefix);
+/**
+ * Get the full path of the current item from a workdir iterator.
+ * This will return NULL for a non-workdir iterator.
+ */
+extern int git_iterator_current_workdir_path(
+ git_iterator *iter, git_buf **path);
+
#endif