]>
Commit | Line | Data |
---|---|---|
8bc9a0c7 LT |
1 | /* |
2 | * GIT - The information manager from hell | |
3 | * | |
4 | * Copyright (C) Linus Torvalds, 2005 | |
5 | */ | |
e83c5163 LT |
6 | #include "cache.h" |
7 | ||
ff69ab9a JH |
8 | static char *diff_cmd = "diff -L 'a/%s' -L 'b/%s' "; |
9 | static char *diff_opts = "-p -u"; | |
4789576e JH |
10 | static char *diff_arg_forward = " - '%s'"; |
11 | static char *diff_arg_reverse = " '%s' -"; | |
ff69ab9a JH |
12 | |
13 | static void prepare_diff_cmd(void) | |
14 | { | |
15 | /* | |
16 | * Default values above are meant to match the | |
17 | * Linux kernel development style. Examples of | |
18 | * alternative styles you can specify via environment | |
19 | * variables are: | |
20 | * | |
21 | * GIT_DIFF_CMD="diff -L '%s' -L '%s'" | |
22 | * GIT_DIFF_OPTS="-c"; | |
23 | */ | |
24 | diff_cmd = getenv("GIT_DIFF_CMD") ? : diff_cmd; | |
25 | diff_opts = getenv("GIT_DIFF_OPTS") ? : diff_opts; | |
26 | } | |
9bd94caf JH |
27 | |
28 | /* Help to copy the thing properly quoted for the shell safety. | |
29 | * any single quote is replaced with '\'', and the caller is | |
30 | * expected to enclose the result within a single quote pair. | |
31 | * | |
32 | * E.g. | |
33 | * original sq_expand result | |
34 | * name ==> name ==> 'name' | |
35 | * a b ==> a b ==> 'a b' | |
36 | * a'b ==> a'\''b ==> 'a'\''b' | |
9bd94caf JH |
37 | */ |
38 | static char *sq_expand(char *src) | |
39 | { | |
40 | static char *buf = NULL; | |
9bd94caf JH |
41 | int cnt, c; |
42 | char *cp; | |
43 | ||
64982f75 JH |
44 | /* count bytes needed to store the quoted string. */ |
45 | for (cnt = 1, cp = src; *cp; cnt++, cp++) | |
9bd94caf JH |
46 | if (*cp == '\'') |
47 | cnt += 3; | |
48 | ||
c157d376 JH |
49 | if (! (buf = malloc(cnt))) |
50 | return buf; | |
9bd94caf JH |
51 | cp = buf; |
52 | while ((c = *src++)) { | |
53 | if (c != '\'') | |
54 | *cp++ = c; | |
55 | else { | |
56 | cp = strcpy(cp, "'\\''"); | |
57 | cp += 4; | |
58 | } | |
59 | } | |
60 | *cp = 0; | |
61 | return buf; | |
62 | } | |
63 | ||
8f3671dc | 64 | static void show_differences(char *name, char *label, void *old_contents, |
4789576e | 65 | unsigned long long old_size, int reverse) |
e83c5163 | 66 | { |
e83c5163 | 67 | FILE *f; |
9bd94caf | 68 | char *name_sq = sq_expand(name); |
8f3671dc | 69 | char *label_sq = (name != label) ? sq_expand(label) : name_sq; |
4789576e | 70 | char *diff_arg = reverse ? diff_arg_reverse : diff_arg_forward; |
ff69ab9a JH |
71 | int cmd_size = strlen(name_sq) + strlen(label_sq) * 2 + |
72 | strlen(diff_cmd) + strlen(diff_opts) + strlen(diff_arg); | |
c157d376 | 73 | char *cmd = malloc(cmd_size); |
ff69ab9a | 74 | int next_at; |
e83c5163 | 75 | |
8f3671dc | 76 | fflush(stdout); |
ff69ab9a JH |
77 | next_at = snprintf(cmd, cmd_size, diff_cmd, label_sq, label_sq); |
78 | next_at += snprintf(cmd+next_at, cmd_size-next_at, "%s", diff_opts); | |
79 | next_at += snprintf(cmd+next_at, cmd_size-next_at, diff_arg, name_sq); | |
e83c5163 | 80 | f = popen(cmd, "w"); |
c0fb976a CL |
81 | if (old_size) |
82 | fwrite(old_contents, old_size, 1, f); | |
e83c5163 | 83 | pclose(f); |
8f3671dc JH |
84 | if (label_sq != name_sq) |
85 | free(label_sq); | |
c157d376 JH |
86 | free(name_sq); |
87 | free(cmd); | |
e83c5163 LT |
88 | } |
89 | ||
4789576e | 90 | static void show_diff_empty(struct cache_entry *ce, int reverse) |
c0fb976a CL |
91 | { |
92 | char *old; | |
93 | unsigned long int size; | |
8f3671dc | 94 | unsigned char type[20]; |
c0fb976a CL |
95 | |
96 | old = read_sha1_file(ce->sha1, type, &size); | |
d0db4663 JH |
97 | if (! old) { |
98 | error("unable to read blob object for %s (%s)", ce->name, | |
99 | sha1_to_hex(ce->sha1)); | |
100 | return; | |
101 | } | |
4789576e | 102 | show_differences("/dev/null", ce->name, old, size, reverse); |
c0fb976a CL |
103 | } |
104 | ||
d2522a65 | 105 | static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]"; |
b8f80925 JH |
106 | |
107 | static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt) | |
108 | { | |
109 | int i; | |
110 | int namelen = ce_namelen(ce); | |
111 | for (i = 0; i < cnt; i++) { | |
112 | int speclen = strlen(spec[i]); | |
113 | if (! strncmp(spec[i], ce->name, speclen) && | |
114 | speclen <= namelen && | |
115 | (ce->name[speclen] == 0 || | |
116 | ce->name[speclen] == '/')) | |
117 | return 1; | |
118 | } | |
119 | return 0; | |
120 | } | |
121 | ||
e83c5163 LT |
122 | int main(int argc, char **argv) |
123 | { | |
e2e5e98a | 124 | int silent = 0; |
ca2a0798 | 125 | int silent_on_nonexisting_files = 0; |
d94c6128 | 126 | int machine_readable = 0; |
4789576e | 127 | int reverse = 0; |
e83c5163 LT |
128 | int entries = read_cache(); |
129 | int i; | |
130 | ||
b8f80925 | 131 | while (1 < argc && argv[1][0] == '-') { |
4789576e JH |
132 | if (!strcmp(argv[1], "-R")) |
133 | reverse = 1; | |
134 | else if (!strcmp(argv[1], "-s")) | |
ca2a0798 | 135 | silent_on_nonexisting_files = silent = 1; |
b8f80925 | 136 | else if (!strcmp(argv[1], "-q")) |
ca2a0798 | 137 | silent_on_nonexisting_files = 1; |
d2522a65 | 138 | else if (!strcmp(argv[1], "-z")) |
d94c6128 | 139 | machine_readable = 1; |
b8f80925 JH |
140 | else |
141 | usage(show_diff_usage); | |
142 | argv++; argc--; | |
e2e5e98a PB |
143 | } |
144 | ||
b8f80925 JH |
145 | /* At this point, if argc == 1, then we are doing everything. |
146 | * Otherwise argv[1] .. argv[argc-1] have the explicit paths. | |
147 | */ | |
e83c5163 LT |
148 | if (entries < 0) { |
149 | perror("read_cache"); | |
150 | exit(1); | |
151 | } | |
ff69ab9a | 152 | prepare_diff_cmd(); |
e83c5163 LT |
153 | for (i = 0; i < entries; i++) { |
154 | struct stat st; | |
155 | struct cache_entry *ce = active_cache[i]; | |
d94c6128 | 156 | int changed; |
e83c5163 LT |
157 | unsigned long size; |
158 | char type[20]; | |
b70c8942 | 159 | void *old; |
e83c5163 | 160 | |
d2522a65 | 161 | if (1 < argc && |
b8f80925 JH |
162 | ! matches_pathspec(ce, argv+1, argc-1)) |
163 | continue; | |
164 | ||
9fec8b26 JH |
165 | if (ce_stage(ce)) { |
166 | if (machine_readable) | |
167 | printf("U %s%c", ce->name, 0); | |
168 | else | |
169 | printf("%s: Unmerged\n", | |
170 | ce->name); | |
171 | while (i < entries && | |
172 | !strcmp(ce->name, active_cache[i]->name)) | |
173 | i++; | |
174 | i--; /* compensate for loop control increments */ | |
175 | continue; | |
176 | } | |
177 | ||
e83c5163 | 178 | if (stat(ce->name, &st) < 0) { |
ca2a0798 JH |
179 | if (errno == ENOENT && silent_on_nonexisting_files) |
180 | continue; | |
d94c6128 JH |
181 | if (machine_readable) |
182 | printf("X %s%c", ce->name, 0); | |
183 | else { | |
184 | printf("%s: %s\n", ce->name, strerror(errno)); | |
185 | if (errno == ENOENT) | |
4789576e | 186 | show_diff_empty(ce, reverse); |
d94c6128 | 187 | } |
e83c5163 LT |
188 | continue; |
189 | } | |
734aab75 | 190 | changed = cache_match_stat(ce, &st); |
5e76011c | 191 | if (!changed) |
e83c5163 | 192 | continue; |
d94c6128 JH |
193 | if (!machine_readable) |
194 | printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1)); | |
195 | else { | |
196 | printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0); | |
197 | continue; | |
198 | } | |
e2e5e98a PB |
199 | if (silent) |
200 | continue; | |
201 | ||
b70c8942 | 202 | old = read_sha1_file(ce->sha1, type, &size); |
d0db4663 JH |
203 | if (! old) |
204 | error("unable to read blob object for %s (%s)", | |
205 | ce->name, sha1_to_hex(ce->sha1)); | |
206 | else | |
4789576e JH |
207 | show_differences(ce->name, ce->name, old, size, |
208 | reverse); | |
b70c8942 | 209 | free(old); |
e83c5163 LT |
210 | } |
211 | return 0; | |
212 | } |