Merge with http://members.cox.net/junkio/git-jc.git
[git.git] / diff-tree-helper.c
1 /*
2  * Copyright (C) 2005 Junio C Hamano
3  */
4 #include <limits.h>
5 #include "cache.h"
6 #include "strbuf.h"
7 #include "diff.h"
8
9 static int matches_pathspec(const char *name, const char **spec, int cnt)
10 {
11         int i;
12         int namelen = strlen(name);
13         for (i = 0; i < cnt; i++) {
14                 int speclen = strlen(spec[i]);
15                 if (! strncmp(spec[i], name, speclen) &&
16                     speclen <= namelen &&
17                     (name[speclen] == 0 ||
18                      name[speclen] == '/'))
19                         return 1;
20         }
21         return 0;
22 }
23
24 static int parse_oneside_change(const char *cp, struct diff_spec *one,
25                                 char *path)
26 {
27         int ch;
28
29         one->file_valid = one->sha1_valid = 1;
30         one->mode = 0;
31         while ((ch = *cp) && '0' <= ch && ch <= '7') {
32                 one->mode = (one->mode << 3) | (ch - '0');
33                 cp++;
34         }
35
36         if (strncmp(cp, "\tblob\t", 6))
37                 return -1;
38         cp += 6;
39         if (get_sha1_hex(cp, one->blob_sha1))
40                 return -1;
41         cp += 40;
42         if (*cp++ != '\t')
43                 return -1;
44         strcpy(path, cp);
45         return 0;
46 }
47
48 static int parse_diff_tree_output(const char *buf,
49                                   const char **spec, int cnt, int reverse)
50 {
51         struct diff_spec old, new;
52         char path[PATH_MAX];
53         const char *cp = buf;
54         int ch;
55
56         switch (*cp++) {
57         case 'U':
58                 if (!cnt || matches_pathspec(cp + 1, spec, cnt))
59                         diff_unmerge(cp + 1);
60                 return 0;
61         case '+':
62                 old.file_valid = 0;
63                 parse_oneside_change(cp, &new, path);
64                 break;
65         case '-':
66                 new.file_valid = 0;
67                 parse_oneside_change(cp, &old, path);
68                 break;
69         case '*':
70                 old.file_valid = old.sha1_valid =
71                         new.file_valid = new.sha1_valid = 1;
72                 old.mode = new.mode = 0;
73                 while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
74                         old.mode = (old.mode << 3) | (ch - '0');
75                         cp++;
76                 }
77                 if (strncmp(cp, "->", 2))
78                         return -1;
79                 cp += 2;
80                 while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
81                         new.mode = (new.mode << 3) | (ch - '0');
82                         cp++;
83                 }
84                 if (strncmp(cp, "\tblob\t", 6))
85                         return -1;
86                 cp += 6;
87                 if (get_sha1_hex(cp, old.blob_sha1))
88                         return -1;
89                 cp += 40;
90                 if (strncmp(cp, "->", 2))
91                         return -1;
92                 cp += 2;
93                 if (get_sha1_hex(cp, new.blob_sha1))
94                         return -1;
95                 cp += 40;
96                 if (*cp++ != '\t')
97                         return -1;
98                 strcpy(path, cp);
99                 break;
100         default:
101                 return -1;
102         }
103         if (!cnt || matches_pathspec(path, spec, cnt)) {
104                 if (reverse)
105                         run_external_diff(path, &new, &old);
106                 else
107                         run_external_diff(path, &old, &new);
108         }
109         return 0;
110 }
111
112 static const char *diff_tree_helper_usage =
113 "diff-tree-helper [-R] [-z] paths...";
114
115 int main(int ac, const char **av) {
116         struct strbuf sb;
117         int reverse = 0;
118         int line_termination = '\n';
119
120         strbuf_init(&sb);
121
122         while (1 < ac && av[1][0] == '-') {
123                 if (av[1][1] == 'R')
124                         reverse = 1;
125                 else if (av[1][1] == 'z')
126                         line_termination = 0;
127                 else
128                         usage(diff_tree_helper_usage);
129                 ac--; av++;
130         }
131         /* the remaining parameters are paths patterns */
132
133         while (1) {
134                 int status;
135                 read_line(&sb, stdin, line_termination);
136                 if (sb.eof)
137                         break;
138                 status = parse_diff_tree_output(sb.buf, av+1, ac-1, reverse);
139                 if (status)
140                         fprintf(stderr, "cannot parse %s\n", sb.buf);
141         }
142         return 0;
143 }