4221d3ae4c7a844627cd72c3f4772bbe94abda16
[git.git] / show-diff.c
1 /*
2  * GIT - The information manager from hell
3  *
4  * Copyright (C) Linus Torvalds, 2005
5  */
6 #include "cache.h"
7
8 static void show_differences(char *name,
9         void *old_contents, unsigned long long old_size)
10 {
11         static char cmd[1000];
12         FILE *f;
13
14         snprintf(cmd, sizeof(cmd), "diff -L %s -u -N  - %s", name, name);
15         f = popen(cmd, "w");
16         if (old_size)
17                 fwrite(old_contents, old_size, 1, f);
18         pclose(f);
19 }
20
21 static void show_diff_empty(struct cache_entry *ce)
22 {
23         char *old;
24         unsigned long int size;
25         int lines=0;
26         unsigned char type[20], *p, *end;
27
28         old = read_sha1_file(ce->sha1, type, &size);
29         if (size > 0) {
30                 int startline = 1;
31                 int c = 0;
32
33                 printf("--- %s\n", ce->name);
34                 printf("+++ /dev/null\n");
35                 p = old;
36                 end = old + size;
37                 while (p < end)
38                         if (*p++ == '\n')
39                                 lines ++;
40                 printf("@@ -1,%d +0,0 @@\n", lines);
41                 p = old;
42                 while (p < end) {
43                         c = *p++;
44                         if (startline) {
45                                 putchar('-');
46                                 startline = 0;
47                         }
48                         putchar(c);
49                         if (c == '\n')
50                                 startline = 1;
51                 }
52                 if (c!='\n')
53                         printf("\n");
54                 fflush(stdout);
55         }
56 }
57
58 static const char *show_diff_usage = "show-diff [-s] [-q] [-z] [paths...]";
59
60 static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
61 {
62         int i;
63         int namelen = ce_namelen(ce);
64         for (i = 0; i < cnt; i++) {
65                 int speclen = strlen(spec[i]);
66                 if (! strncmp(spec[i], ce->name, speclen) &&
67                     speclen <= namelen &&
68                     (ce->name[speclen] == 0 ||
69                      ce->name[speclen] == '/'))
70                         return 1;
71         }
72         return 0;
73 }
74
75 int main(int argc, char **argv)
76 {
77         int silent = 0;
78         int silent_on_nonexisting_files = 0;
79         int machine_readable = 0;
80         int entries = read_cache();
81         int i;
82
83         while (1 < argc && argv[1][0] == '-') {
84                 if (!strcmp(argv[1], "-s"))
85                         silent_on_nonexisting_files = silent = 1;
86                 else if (!strcmp(argv[1], "-q"))
87                         silent_on_nonexisting_files = 1;
88                 else if (!strcmp(argv[1], "-z")) {
89                         machine_readable = 1;
90                 }
91                 else
92                         usage(show_diff_usage);
93                 argv++; argc--;
94         }
95
96         /* At this point, if argc == 1, then we are doing everything.
97          * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
98          */
99         if (entries < 0) {
100                 perror("read_cache");
101                 exit(1);
102         }
103         for (i = 0; i < entries; i++) {
104                 struct stat st;
105                 struct cache_entry *ce = active_cache[i];
106                 int changed;
107                 unsigned long size;
108                 char type[20];
109                 void *new;
110
111                 if (1 <argc &&
112                     ! matches_pathspec(ce, argv+1, argc-1))
113                         continue;
114
115                 if (stat(ce->name, &st) < 0) {
116                         if (errno == ENOENT && silent_on_nonexisting_files)
117                                 continue;
118                         if (machine_readable)
119                                 printf("X %s%c", ce->name, 0);
120                         else {
121                                 printf("%s: %s\n", ce->name, strerror(errno));
122                                 if (errno == ENOENT)
123                                         show_diff_empty(ce);
124                         }
125                         continue;
126                 }
127                 changed = cache_match_stat(ce, &st);
128                 if (!changed)
129                         continue;
130                 if (!machine_readable)
131                         printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1));
132                 else {
133                         printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0);
134                         continue;
135                 }
136                 fflush(stdout);
137                 if (silent)
138                         continue;
139
140                 new = read_sha1_file(ce->sha1, type, &size);
141                 show_differences(ce->name, new, size);
142                 free(new);
143         }
144         return 0;
145 }