|
8 | 8 | #include "diffcore.h" |
9 | 9 | #include "revision.h" |
10 | 10 | #include "cache-tree.h" |
| 11 | +#include "path-list.h" |
11 | 12 |
|
12 | 13 | /* |
13 | 14 | * diff-files |
14 | 15 | */ |
15 | 16 |
|
| 17 | +static int read_directory(const char *path, struct path_list *list) |
| 18 | +{ |
| 19 | + DIR *dir; |
| 20 | + struct dirent *e; |
| 21 | + |
| 22 | + if (!(dir = opendir(path))) |
| 23 | + return error("Could not open directory %s", path); |
| 24 | + |
| 25 | + while ((e = readdir(dir))) |
| 26 | + if (strcmp(".", e->d_name) && strcmp("..", e->d_name)) |
| 27 | + path_list_insert(xstrdup(e->d_name), list); |
| 28 | + |
| 29 | + closedir(dir); |
| 30 | + return 0; |
| 31 | +} |
| 32 | + |
| 33 | +static int queue_diff(struct diff_options *o, |
| 34 | + const char *name1, const char *name2) |
| 35 | +{ |
| 36 | + struct stat st; |
| 37 | + int mode1 = 0, mode2 = 0; |
| 38 | + |
| 39 | + if (name1) { |
| 40 | + if (stat(name1, &st)) |
| 41 | + return error("Could not access '%s'", name1); |
| 42 | + mode1 = st.st_mode; |
| 43 | + } |
| 44 | + if (name2) { |
| 45 | + if (stat(name2, &st)) |
| 46 | + return error("Could not access '%s'", name1); |
| 47 | + mode2 = st.st_mode; |
| 48 | + } |
| 49 | + |
| 50 | + if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) |
| 51 | + return error("file/directory conflict: %s, %s", name1, name2); |
| 52 | + |
| 53 | + if (S_ISDIR(mode1) || S_ISDIR(mode2)) { |
| 54 | + char buffer1[PATH_MAX], buffer2[PATH_MAX]; |
| 55 | + struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1}; |
| 56 | + int len1 = 0, len2 = 0, i1, i2, ret = 0; |
| 57 | + |
| 58 | + if (name1 && read_directory(name1, &p1)) |
| 59 | + return -1; |
| 60 | + if (name2 && read_directory(name2, &p2)) { |
| 61 | + path_list_clear(&p1, 0); |
| 62 | + return -1; |
| 63 | + } |
| 64 | + |
| 65 | + if (name1) { |
| 66 | + len1 = strlen(name1); |
| 67 | + if (len1 > 0 && name1[len1 - 1] == '/') |
| 68 | + len1--; |
| 69 | + memcpy(buffer1, name1, len1); |
| 70 | + buffer1[len1++] = '/'; |
| 71 | + } |
| 72 | + |
| 73 | + if (name2) { |
| 74 | + len2 = strlen(name2); |
| 75 | + if (len2 > 0 && name2[len2 - 1] == '/') |
| 76 | + len2--; |
| 77 | + memcpy(buffer2, name2, len2); |
| 78 | + buffer2[len2++] = '/'; |
| 79 | + } |
| 80 | + |
| 81 | + for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) { |
| 82 | + const char *n1, *n2; |
| 83 | + int comp; |
| 84 | + |
| 85 | + if (i1 == p1.nr) |
| 86 | + comp = 1; |
| 87 | + else if (i2 == p2.nr) |
| 88 | + comp = -1; |
| 89 | + else |
| 90 | + comp = strcmp(p1.items[i1].path, |
| 91 | + p2.items[i2].path); |
| 92 | + |
| 93 | + if (comp > 0) |
| 94 | + n1 = NULL; |
| 95 | + else { |
| 96 | + n1 = buffer1; |
| 97 | + strncpy(buffer1 + len1, p1.items[i1++].path, |
| 98 | + PATH_MAX - len1); |
| 99 | + } |
| 100 | + |
| 101 | + if (comp < 0) |
| 102 | + n2 = NULL; |
| 103 | + else { |
| 104 | + n2 = buffer2; |
| 105 | + strncpy(buffer2 + len2, p2.items[i2++].path, |
| 106 | + PATH_MAX - len2); |
| 107 | + } |
| 108 | + |
| 109 | + ret = queue_diff(o, n1, n2); |
| 110 | + } |
| 111 | + path_list_clear(&p1, 0); |
| 112 | + path_list_clear(&p2, 0); |
| 113 | + |
| 114 | + return ret; |
| 115 | + } else { |
| 116 | + struct diff_filespec *d1, *d2; |
| 117 | + |
| 118 | + if (o->reverse_diff) { |
| 119 | + unsigned tmp; |
| 120 | + const char *tmp_c; |
| 121 | + tmp = mode1; mode1 = mode2; mode2 = tmp; |
| 122 | + tmp_c = name1; name1 = name2; name2 = tmp_c; |
| 123 | + } |
| 124 | + |
| 125 | + if (!name1) |
| 126 | + name1 = "/dev/null"; |
| 127 | + if (!name2) |
| 128 | + name2 = "/dev/null"; |
| 129 | + d1 = alloc_filespec(name1); |
| 130 | + d2 = alloc_filespec(name2); |
| 131 | + fill_filespec(d1, null_sha1, mode1); |
| 132 | + fill_filespec(d2, null_sha1, mode2); |
| 133 | + |
| 134 | + diff_queue(&diff_queued_diff, d1, d2); |
| 135 | + return 0; |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +static int is_in_index(const char *path) |
| 140 | +{ |
| 141 | + int len = strlen(path); |
| 142 | + int pos = cache_name_pos(path, len); |
| 143 | + char c; |
| 144 | + |
| 145 | + if (pos < 0) |
| 146 | + return 0; |
| 147 | + if (strncmp(active_cache[pos]->name, path, len)) |
| 148 | + return 0; |
| 149 | + c = active_cache[pos]->name[len]; |
| 150 | + return c == '\0' || c == '/'; |
| 151 | +} |
| 152 | + |
| 153 | +static int handle_diff_files_args(struct rev_info *revs, |
| 154 | + int argc, const char **argv, int *silent) |
| 155 | +{ |
| 156 | + *silent = 0; |
| 157 | + |
| 158 | + /* revs->max_count == -2 means --no-index */ |
| 159 | + while (1 < argc && argv[1][0] == '-') { |
| 160 | + if (!strcmp(argv[1], "--base")) |
| 161 | + revs->max_count = 1; |
| 162 | + else if (!strcmp(argv[1], "--ours")) |
| 163 | + revs->max_count = 2; |
| 164 | + else if (!strcmp(argv[1], "--theirs")) |
| 165 | + revs->max_count = 3; |
| 166 | + else if (!strcmp(argv[1], "-n") || |
| 167 | + !strcmp(argv[1], "--no-index")) |
| 168 | + revs->max_count = -2; |
| 169 | + else if (!strcmp(argv[1], "-q")) |
| 170 | + *silent = 1; |
| 171 | + else |
| 172 | + return error("invalid option: %s", argv[1]); |
| 173 | + argv++; argc--; |
| 174 | + } |
| 175 | + |
| 176 | + if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) { |
| 177 | + /* |
| 178 | + * If two files are specified, and at least one is untracked, |
| 179 | + * default to no-index. |
| 180 | + */ |
| 181 | + read_cache(); |
| 182 | + if (!is_in_index(revs->diffopt.paths[0]) || |
| 183 | + !is_in_index(revs->diffopt.paths[1])) |
| 184 | + revs->max_count = -2; |
| 185 | + } |
| 186 | + |
| 187 | + /* |
| 188 | + * Make sure there are NO revision (i.e. pending object) parameter, |
| 189 | + * rev.max_count is reasonable (0 <= n <= 3), |
| 190 | + * there is no other revision filtering parameters. |
| 191 | + */ |
| 192 | + if (revs->pending.nr || revs->max_count > 3 || |
| 193 | + revs->min_age != -1 || revs->max_age != -1) |
| 194 | + return error("no revision allowed with diff-files"); |
| 195 | + |
| 196 | + if (revs->max_count == -1 && |
| 197 | + (revs->diffopt.output_format & DIFF_FORMAT_PATCH)) |
| 198 | + revs->combine_merges = revs->dense_combined_merges = 1; |
| 199 | + |
| 200 | + return 0; |
| 201 | +} |
| 202 | + |
| 203 | +int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv) |
| 204 | +{ |
| 205 | + int silent_on_removed; |
| 206 | + |
| 207 | + if (handle_diff_files_args(revs, argc, argv, &silent_on_removed)) |
| 208 | + return -1; |
| 209 | + |
| 210 | + if (revs->max_count == -2) { |
| 211 | + if (revs->diffopt.nr_paths != 2) |
| 212 | + return error("need two files/directories with --no-index"); |
| 213 | + queue_diff(&revs->diffopt, revs->diffopt.paths[0], |
| 214 | + revs->diffopt.paths[1]); |
| 215 | + diffcore_std(&revs->diffopt); |
| 216 | + diff_flush(&revs->diffopt); |
| 217 | + return 0; |
| 218 | + } |
| 219 | + |
| 220 | + return run_diff_files(revs, silent_on_removed); |
| 221 | +} |
| 222 | + |
16 | 223 | int run_diff_files(struct rev_info *revs, int silent_on_removed) |
17 | 224 | { |
18 | 225 | int entries, i; |
|
0 commit comments