]> git.proxmox.com Git - libgit2.git/blob - examples/diff.c
Merge pull request #1131 from libgit2/correct-ahead-behind
[libgit2.git] / examples / diff.c
1 #include <stdio.h>
2 #include <git2.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 static void check(int error, const char *message)
7 {
8 if (error) {
9 fprintf(stderr, "%s (%d)\n", message, error);
10 exit(1);
11 }
12 }
13
14 static int resolve_to_tree(
15 git_repository *repo, const char *identifier, git_tree **tree)
16 {
17 int err = 0;
18 size_t len = strlen(identifier);
19 git_oid oid;
20 git_object *obj = NULL;
21
22 /* try to resolve as OID */
23 if (git_oid_fromstrn(&oid, identifier, len) == 0)
24 git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
25
26 /* try to resolve as reference */
27 if (obj == NULL) {
28 git_reference *ref, *resolved;
29 if (git_reference_lookup(&ref, repo, identifier) == 0) {
30 git_reference_resolve(&resolved, ref);
31 git_reference_free(ref);
32 if (resolved) {
33 git_object_lookup(&obj, repo, git_reference_target(resolved), GIT_OBJ_ANY);
34 git_reference_free(resolved);
35 }
36 }
37 }
38
39 if (obj == NULL)
40 return GIT_ENOTFOUND;
41
42 switch (git_object_type(obj)) {
43 case GIT_OBJ_TREE:
44 *tree = (git_tree *)obj;
45 break;
46 case GIT_OBJ_COMMIT:
47 err = git_commit_tree(tree, (git_commit *)obj);
48 git_object_free(obj);
49 break;
50 default:
51 err = GIT_ENOTFOUND;
52 }
53
54 return err;
55 }
56
57 char *colors[] = {
58 "\033[m", /* reset */
59 "\033[1m", /* bold */
60 "\033[31m", /* red */
61 "\033[32m", /* green */
62 "\033[36m" /* cyan */
63 };
64
65 static int printer(
66 const git_diff_delta *delta,
67 const git_diff_range *range,
68 char usage,
69 const char *line,
70 size_t line_len,
71 void *data)
72 {
73 int *last_color = data, color = 0;
74
75 (void)delta; (void)range; (void)line_len;
76
77 if (*last_color >= 0) {
78 switch (usage) {
79 case GIT_DIFF_LINE_ADDITION: color = 3; break;
80 case GIT_DIFF_LINE_DELETION: color = 2; break;
81 case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
82 case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
83 case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
84 case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
85 default: color = 0;
86 }
87 if (color != *last_color) {
88 if (*last_color == 1 || color == 1)
89 fputs(colors[0], stdout);
90 fputs(colors[color], stdout);
91 *last_color = color;
92 }
93 }
94
95 fputs(line, stdout);
96 return 0;
97 }
98
99 static int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
100 {
101 size_t len = strlen(pattern);
102 uint16_t strval;
103 char *endptr = NULL;
104 if (strncmp(arg, pattern, len))
105 return 0;
106 strval = strtoul(arg + len, &endptr, 0);
107 if (endptr == arg)
108 return 0;
109 *val = strval;
110 return 1;
111 }
112
113 static int check_str_param(const char *arg, const char *pattern, const char **val)
114 {
115 size_t len = strlen(pattern);
116 if (strncmp(arg, pattern, len))
117 return 0;
118 *val = (const char *)(arg + len);
119 return 1;
120 }
121
122 static void usage(const char *message, const char *arg)
123 {
124 if (message && arg)
125 fprintf(stderr, "%s: %s\n", message, arg);
126 else if (message)
127 fprintf(stderr, "%s\n", message);
128 fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
129 exit(1);
130 }
131
132 int main(int argc, char *argv[])
133 {
134 git_repository *repo = NULL;
135 git_tree *t1 = NULL, *t2 = NULL;
136 git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
137 git_diff_list *diff;
138 int i, color = -1, compact = 0, cached = 0;
139 char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
140
141 memset(&opts, 0, sizeof(opts));
142
143 /* parse arguments as copied from git-diff */
144
145 for (i = 1; i < argc; ++i) {
146 a = argv[i];
147
148 if (a[0] != '-') {
149 if (treeish1 == NULL)
150 treeish1 = a;
151 else if (treeish2 == NULL)
152 treeish2 = a;
153 else
154 usage("Only one or two tree identifiers can be provided", NULL);
155 }
156 else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
157 !strcmp(a, "--patch"))
158 compact = 0;
159 else if (!strcmp(a, "--cached"))
160 cached = 1;
161 else if (!strcmp(a, "--name-status"))
162 compact = 1;
163 else if (!strcmp(a, "--color"))
164 color = 0;
165 else if (!strcmp(a, "--no-color"))
166 color = -1;
167 else if (!strcmp(a, "-R"))
168 opts.flags |= GIT_DIFF_REVERSE;
169 else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
170 opts.flags |= GIT_DIFF_FORCE_TEXT;
171 else if (!strcmp(a, "--ignore-space-at-eol"))
172 opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
173 else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
174 opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
175 else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
176 opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
177 else if (!strcmp(a, "--ignored"))
178 opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
179 else if (!strcmp(a, "--untracked"))
180 opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
181 else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
182 !check_uint16_param(a, "--unified=", &opts.context_lines) &&
183 !check_uint16_param(a, "--inter-hunk-context=",
184 &opts.interhunk_lines) &&
185 !check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
186 !check_str_param(a, "--dst-prefix=", &opts.new_prefix))
187 usage("Unknown arg", a);
188 }
189
190 /* open repo */
191
192 check(git_repository_open_ext(&repo, dir, 0, NULL),
193 "Could not open repository");
194
195 if (treeish1)
196 check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
197 if (treeish2)
198 check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
199
200 /* <sha1> <sha2> */
201 /* <sha1> --cached */
202 /* <sha1> */
203 /* --cached */
204 /* nothing */
205
206 if (t1 && t2)
207 check(git_diff_tree_to_tree(&diff, repo, t1, t2, &opts), "Diff");
208 else if (t1 && cached)
209 check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
210 else if (t1) {
211 git_diff_list *diff2;
212 check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
213 check(git_diff_index_to_workdir(&diff2, repo, NULL, &opts), "Diff");
214 check(git_diff_merge(diff, diff2), "Merge diffs");
215 git_diff_list_free(diff2);
216 }
217 else if (cached) {
218 check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
219 check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
220 }
221 else
222 check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff");
223
224 if (color >= 0)
225 fputs(colors[0], stdout);
226
227 if (compact)
228 check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
229 else
230 check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
231
232 if (color >= 0)
233 fputs(colors[0], stdout);
234
235 git_diff_list_free(diff);
236 git_tree_free(t1);
237 git_tree_free(t2);
238 git_repository_free(repo);
239
240 return 0;
241 }
242