THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
7.2KB

  1. /*
  2. * "diff --no-index" support
  3. * Copyright (c) 2007 by Johannes Schindelin
  4. * Copyright (c) 2008 by Junio C Hamano
  5. */
  6. #include "cache.h"
  7. #include "color.h"
  8. #include "commit.h"
  9. #include "blob.h"
  10. #include "tag.h"
  11. #include "diff.h"
  12. #include "diffcore.h"
  13. #include "revision.h"
  14. #include "log-tree.h"
  15. #include "builtin.h"
  16. #include "parse-options.h"
  17. #include "string-list.h"
  18. #include "dir.h"
  19. static int read_directory_contents(const char *path, struct string_list *list)
  20. {
  21. DIR *dir;
  22. struct dirent *e;
  23. if (!(dir = opendir(path)))
  24. return error("Could not open directory %s", path);
  25. while ((e = readdir(dir)))
  26. if (!is_dot_or_dotdot(e->d_name))
  27. string_list_insert(list, e->d_name);
  28. closedir(dir);
  29. return 0;
  30. }
  31. /*
  32. * This should be "(standard input)" or something, but it will
  33. * probably expose many more breakages in the way no-index code
  34. * is bolted onto the diff callchain.
  35. */
  36. static const char file_from_standard_input[] = "-";
  37. static int get_mode(const char *path, int *mode)
  38. {
  39. struct stat st;
  40. if (!path || !strcmp(path, "/dev/null"))
  41. *mode = 0;
  42. #ifdef GIT_WINDOWS_NATIVE
  43. else if (!strcasecmp(path, "nul"))
  44. *mode = 0;
  45. #endif
  46. else if (path == file_from_standard_input)
  47. *mode = create_ce_mode(0666);
  48. else if (lstat(path, &st))
  49. return error("Could not access '%s'", path);
  50. else
  51. *mode = st.st_mode;
  52. return 0;
  53. }
  54. static int populate_from_stdin(struct diff_filespec *s)
  55. {
  56. struct strbuf buf = STRBUF_INIT;
  57. size_t size = 0;
  58. if (strbuf_read(&buf, 0, 0) < 0)
  59. return error_errno("error while reading from stdin");
  60. s->should_munmap = 0;
  61. s->data = strbuf_detach(&buf, &size);
  62. s->size = size;
  63. s->should_free = 1;
  64. s->is_stdin = 1;
  65. return 0;
  66. }
  67. static struct diff_filespec *noindex_filespec(const char *name, int mode)
  68. {
  69. struct diff_filespec *s;
  70. if (!name)
  71. name = "/dev/null";
  72. s = alloc_filespec(name);
  73. fill_filespec(s, &null_oid, 0, mode);
  74. if (name == file_from_standard_input)
  75. populate_from_stdin(s);
  76. return s;
  77. }
  78. static int queue_diff(struct diff_options *o,
  79. const char *name1, const char *name2)
  80. {
  81. int mode1 = 0, mode2 = 0;
  82. if (get_mode(name1, &mode1) || get_mode(name2, &mode2))
  83. return -1;
  84. if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2)) {
  85. struct diff_filespec *d1, *d2;
  86. if (S_ISDIR(mode1)) {
  87. /* 2 is file that is created */
  88. d1 = noindex_filespec(NULL, 0);
  89. d2 = noindex_filespec(name2, mode2);
  90. name2 = NULL;
  91. mode2 = 0;
  92. } else {
  93. /* 1 is file that is deleted */
  94. d1 = noindex_filespec(name1, mode1);
  95. d2 = noindex_filespec(NULL, 0);
  96. name1 = NULL;
  97. mode1 = 0;
  98. }
  99. /* emit that file */
  100. diff_queue(&diff_queued_diff, d1, d2);
  101. /* and then let the entire directory be created or deleted */
  102. }
  103. if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
  104. struct strbuf buffer1 = STRBUF_INIT;
  105. struct strbuf buffer2 = STRBUF_INIT;
  106. struct string_list p1 = STRING_LIST_INIT_DUP;
  107. struct string_list p2 = STRING_LIST_INIT_DUP;
  108. int i1, i2, ret = 0;
  109. size_t len1 = 0, len2 = 0;
  110. if (name1 && read_directory_contents(name1, &p1))
  111. return -1;
  112. if (name2 && read_directory_contents(name2, &p2)) {
  113. string_list_clear(&p1, 0);
  114. return -1;
  115. }
  116. if (name1) {
  117. strbuf_addstr(&buffer1, name1);
  118. strbuf_complete(&buffer1, '/');
  119. len1 = buffer1.len;
  120. }
  121. if (name2) {
  122. strbuf_addstr(&buffer2, name2);
  123. strbuf_complete(&buffer2, '/');
  124. len2 = buffer2.len;
  125. }
  126. for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
  127. const char *n1, *n2;
  128. int comp;
  129. strbuf_setlen(&buffer1, len1);
  130. strbuf_setlen(&buffer2, len2);
  131. if (i1 == p1.nr)
  132. comp = 1;
  133. else if (i2 == p2.nr)
  134. comp = -1;
  135. else
  136. comp = strcmp(p1.items[i1].string, p2.items[i2].string);
  137. if (comp > 0)
  138. n1 = NULL;
  139. else {
  140. strbuf_addstr(&buffer1, p1.items[i1++].string);
  141. n1 = buffer1.buf;
  142. }
  143. if (comp < 0)
  144. n2 = NULL;
  145. else {
  146. strbuf_addstr(&buffer2, p2.items[i2++].string);
  147. n2 = buffer2.buf;
  148. }
  149. ret = queue_diff(o, n1, n2);
  150. }
  151. string_list_clear(&p1, 0);
  152. string_list_clear(&p2, 0);
  153. strbuf_release(&buffer1);
  154. strbuf_release(&buffer2);
  155. return ret;
  156. } else {
  157. struct diff_filespec *d1, *d2;
  158. if (o->flags.reverse_diff) {
  159. SWAP(mode1, mode2);
  160. SWAP(name1, name2);
  161. }
  162. d1 = noindex_filespec(name1, mode1);
  163. d2 = noindex_filespec(name2, mode2);
  164. diff_queue(&diff_queued_diff, d1, d2);
  165. return 0;
  166. }
  167. }
  168. /* append basename of F to D */
  169. static void append_basename(struct strbuf *path, const char *dir, const char *file)
  170. {
  171. const char *tail = strrchr(file, '/');
  172. strbuf_addstr(path, dir);
  173. while (path->len && path->buf[path->len - 1] == '/')
  174. path->len--;
  175. strbuf_addch(path, '/');
  176. strbuf_addstr(path, tail ? tail + 1 : file);
  177. }
  178. /*
  179. * DWIM "diff D F" into "diff D/F F" and "diff F D" into "diff F D/F"
  180. * Note that we append the basename of F to D/, so "diff a/b/file D"
  181. * becomes "diff a/b/file D/file", not "diff a/b/file D/a/b/file".
  182. */
  183. static void fixup_paths(const char **path, struct strbuf *replacement)
  184. {
  185. unsigned int isdir0, isdir1;
  186. if (path[0] == file_from_standard_input ||
  187. path[1] == file_from_standard_input)
  188. return;
  189. isdir0 = is_directory(path[0]);
  190. isdir1 = is_directory(path[1]);
  191. if (isdir0 == isdir1)
  192. return;
  193. if (isdir0) {
  194. append_basename(replacement, path[0], path[1]);
  195. path[0] = replacement->buf;
  196. } else {
  197. append_basename(replacement, path[1], path[0]);
  198. path[1] = replacement->buf;
  199. }
  200. }
  201. static const char * const diff_no_index_usage[] = {
  202. N_("git diff --no-index [<options>] <path> <path>"),
  203. NULL
  204. };
  205. int diff_no_index(struct rev_info *revs,
  206. int implicit_no_index,
  207. int argc, const char **argv)
  208. {
  209. int i, no_index;
  210. const char *paths[2];
  211. struct strbuf replacement = STRBUF_INIT;
  212. const char *prefix = revs->prefix;
  213. struct option no_index_options[] = {
  214. OPT_BOOL_F(0, "no-index", &no_index, "",
  215. PARSE_OPT_NONEG | PARSE_OPT_HIDDEN),
  216. OPT_END(),
  217. };
  218. struct option *options;
  219. options = parse_options_concat(no_index_options,
  220. revs->diffopt.parseopts);
  221. argc = parse_options(argc, argv, revs->prefix, options,
  222. diff_no_index_usage, 0);
  223. if (argc != 2) {
  224. if (implicit_no_index)
  225. warning(_("Not a git repository. Use --no-index to "
  226. "compare two paths outside a working tree"));
  227. usage_with_options(diff_no_index_usage, options);
  228. }
  229. FREE_AND_NULL(options);
  230. for (i = 0; i < 2; i++) {
  231. const char *p = argv[argc - 2 + i];
  232. if (!strcmp(p, "-"))
  233. /*
  234. * stdin should be spelled as "-"; if you have
  235. * path that is "-", spell it as "./-".
  236. */
  237. p = file_from_standard_input;
  238. else if (prefix)
  239. p = prefix_filename(prefix, p);
  240. paths[i] = p;
  241. }
  242. fixup_paths(paths, &replacement);
  243. revs->diffopt.skip_stat_unmatch = 1;
  244. if (!revs->diffopt.output_format)
  245. revs->diffopt.output_format = DIFF_FORMAT_PATCH;
  246. revs->diffopt.flags.no_index = 1;
  247. revs->diffopt.flags.relative_name = 1;
  248. revs->diffopt.prefix = prefix;
  249. revs->max_count = -2;
  250. diff_setup_done(&revs->diffopt);
  251. setup_diff_pager(&revs->diffopt);
  252. revs->diffopt.flags.exit_with_status = 1;
  253. if (queue_diff(&revs->diffopt, paths[0], paths[1]))
  254. return 1;
  255. diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
  256. diffcore_std(&revs->diffopt);
  257. diff_flush(&revs->diffopt);
  258. strbuf_release(&replacement);
  259. /*
  260. * The return code for --no-index imitates diff(1):
  261. * 0 = no changes, 1 = changes, else error
  262. */
  263. return diff_result_code(&revs->diffopt, 0);
  264. }