patch-id.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #include "cache.h"
  2. #include "builtin.h"
  3. #include "config.h"
  4. #include "diff.h"
  5. static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
  6. {
  7. if (patchlen)
  8. printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
  9. }
  10. static int remove_space(char *line)
  11. {
  12. char *src = line;
  13. char *dst = line;
  14. unsigned char c;
  15. while ((c = *src++) != '\0') {
  16. if (!isspace(c))
  17. *dst++ = c;
  18. }
  19. return dst - line;
  20. }
  21. static int scan_hunk_header(const char *p, int *p_before, int *p_after)
  22. {
  23. static const char digits[] = "0123456789";
  24. const char *q, *r;
  25. int n;
  26. q = p + 4;
  27. n = strspn(q, digits);
  28. if (q[n] == ',') {
  29. q += n + 1;
  30. n = strspn(q, digits);
  31. }
  32. if (n == 0 || q[n] != ' ' || q[n+1] != '+')
  33. return 0;
  34. r = q + n + 2;
  35. n = strspn(r, digits);
  36. if (r[n] == ',') {
  37. r += n + 1;
  38. n = strspn(r, digits);
  39. }
  40. if (n == 0)
  41. return 0;
  42. *p_before = atoi(q);
  43. *p_after = atoi(r);
  44. return 1;
  45. }
  46. static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
  47. struct strbuf *line_buf, int stable)
  48. {
  49. int patchlen = 0, found_next = 0;
  50. int before = -1, after = -1;
  51. git_hash_ctx ctx;
  52. the_hash_algo->init_fn(&ctx);
  53. oidclr(result);
  54. while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
  55. char *line = line_buf->buf;
  56. const char *p = line;
  57. int len;
  58. if (!skip_prefix(line, "diff-tree ", &p) &&
  59. !skip_prefix(line, "commit ", &p) &&
  60. !skip_prefix(line, "From ", &p) &&
  61. starts_with(line, "\\ ") && 12 < strlen(line))
  62. continue;
  63. if (!get_oid_hex(p, next_oid)) {
  64. found_next = 1;
  65. break;
  66. }
  67. /* Ignore commit comments */
  68. if (!patchlen && !starts_with(line, "diff "))
  69. continue;
  70. /* Parsing diff header? */
  71. if (before == -1) {
  72. if (starts_with(line, "index "))
  73. continue;
  74. else if (starts_with(line, "--- "))
  75. before = after = 1;
  76. else if (!isalpha(line[0]))
  77. break;
  78. }
  79. /* Looking for a valid hunk header? */
  80. if (before == 0 && after == 0) {
  81. if (starts_with(line, "@@ -")) {
  82. /* Parse next hunk, but ignore line numbers. */
  83. scan_hunk_header(line, &before, &after);
  84. continue;
  85. }
  86. /* Split at the end of the patch. */
  87. if (!starts_with(line, "diff "))
  88. break;
  89. /* Else we're parsing another header. */
  90. if (stable)
  91. flush_one_hunk(result, &ctx);
  92. before = after = -1;
  93. }
  94. /* If we get here, we're inside a hunk. */
  95. if (line[0] == '-' || line[0] == ' ')
  96. before--;
  97. if (line[0] == '+' || line[0] == ' ')
  98. after--;
  99. /* Compute the sha without whitespace */
  100. len = remove_space(line);
  101. patchlen += len;
  102. the_hash_algo->update_fn(&ctx, line, len);
  103. }
  104. if (!found_next)
  105. oidclr(next_oid);
  106. flush_one_hunk(result, &ctx);
  107. return patchlen;
  108. }
  109. static void generate_id_list(int stable)
  110. {
  111. struct object_id oid, n, result;
  112. int patchlen;
  113. struct strbuf line_buf = STRBUF_INIT;
  114. oidclr(&oid);
  115. while (!feof(stdin)) {
  116. patchlen = get_one_patchid(&n, &result, &line_buf, stable);
  117. flush_current_id(patchlen, &oid, &result);
  118. oidcpy(&oid, &n);
  119. }
  120. strbuf_release(&line_buf);
  121. }
  122. static const char patch_id_usage[] = "git patch-id [--stable | --unstable]";
  123. static int git_patch_id_config(const char *var, const char *value, void *cb)
  124. {
  125. int *stable = cb;
  126. if (!strcmp(var, "patchid.stable")) {
  127. *stable = git_config_bool(var, value);
  128. return 0;
  129. }
  130. return git_default_config(var, value, cb);
  131. }
  132. int cmd_patch_id(int argc, const char **argv, const char *prefix)
  133. {
  134. int stable = -1;
  135. git_config(git_patch_id_config, &stable);
  136. /* If nothing is set, default to unstable. */
  137. if (stable < 0)
  138. stable = 0;
  139. if (argc == 2 && !strcmp(argv[1], "--stable"))
  140. stable = 1;
  141. else if (argc == 2 && !strcmp(argv[1], "--unstable"))
  142. stable = 0;
  143. else if (argc != 1)
  144. usage(patch_id_usage);
  145. generate_id_list(stable);
  146. return 0;
  147. }