]> git.proxmox.com Git - libgit2.git/blob - examples/diff.c
Replace copyright topmatter in example files
[libgit2.git] / examples / diff.c
1 /*
2 * libgit2 "diff" example - shows how to use the diff API
3 *
4 * Written by the libgit2 contributors
5 *
6 * To the extent possible under law, the author(s) have dedicated all copyright
7 * and related and neighboring rights to this software to the public domain
8 * worldwide. This software is distributed without any warranty.
9 *
10 * You should have received a copy of the CC0 Public Domain Dedication along
11 * with this software. If not, see
12 * <http://creativecommons.org/publicdomain/zero/1.0/>.
13 */
14
15 #include "common.h"
16
17 /**
18 * This example demonstrates the use of the libgit2 diff APIs to
19 * create `git_diff` objects and display them, emulating a number of
20 * core Git `diff` command line options.
21 *
22 * This covers on a portion of the core Git diff options and doesn't
23 * have particularly good error handling, but it should show most of
24 * the core libgit2 diff APIs, including various types of diffs and
25 * how to do renaming detection and patch formatting.
26 */
27
28 static const char *colors[] = {
29 "\033[m", /* reset */
30 "\033[1m", /* bold */
31 "\033[31m", /* red */
32 "\033[32m", /* green */
33 "\033[36m" /* cyan */
34 };
35
36 /** The 'opts' struct captures all the various parsed command line options. */
37 struct opts {
38 git_diff_options diffopts;
39 git_diff_find_options findopts;
40 int color;
41 int cached;
42 git_diff_format_t format;
43 const char *treeish1;
44 const char *treeish2;
45 const char *dir;
46 };
47
48 /** These functions are implemented at the end */
49 static void parse_opts(struct opts *o, int argc, char *argv[]);
50 static int color_printer(
51 const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
52
53 int main(int argc, char *argv[])
54 {
55 git_repository *repo = NULL;
56 git_tree *t1 = NULL, *t2 = NULL;
57 git_diff *diff;
58 struct opts o = {
59 GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
60 -1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
61 };
62
63 git_threads_init();
64
65 parse_opts(&o, argc, argv);
66
67 check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
68 "Could not open repository", o.dir);
69
70 /**
71 * Possible argument patterns:
72 *
73 * * &lt;sha1&gt; &lt;sha2&gt;
74 * * &lt;sha1&gt; --cached
75 * * &lt;sha1&gt;
76 * * --cached
77 * * nothing
78 *
79 * Currently ranged arguments like &lt;sha1&gt;..&lt;sha2&gt; and &lt;sha1&gt;...&lt;sha2&gt;
80 * are not supported in this example
81 */
82
83 if (o.treeish1)
84 treeish_to_tree(&t1, repo, o.treeish1);
85 if (o.treeish2)
86 treeish_to_tree(&t2, repo, o.treeish2);
87
88 if (t1 && t2)
89 check_lg2(
90 git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
91 "diff trees", NULL);
92 else if (t1 && o.cached)
93 check_lg2(
94 git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
95 "diff tree to index", NULL);
96 else if (t1)
97 check_lg2(
98 git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
99 "diff tree to working directory", NULL);
100 else if (o.cached) {
101 treeish_to_tree(&t1, repo, "HEAD");
102 check_lg2(
103 git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
104 "diff tree to index", NULL);
105 }
106 else
107 check_lg2(
108 git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
109 "diff index to working directory", NULL);
110
111 /** Apply rename and copy detection if requested. */
112
113 if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
114 check_lg2(
115 git_diff_find_similar(diff, &o.findopts),
116 "finding renames and copies", NULL);
117
118 /** Generate simple output using libgit2 display helper. */
119
120 if (o.color >= 0)
121 fputs(colors[0], stdout);
122
123 check_lg2(
124 git_diff_print(diff, o.format, color_printer, &o.color),
125 "displaying diff", NULL);
126
127 if (o.color >= 0)
128 fputs(colors[0], stdout);
129
130 /** Cleanup before exiting. */
131
132 git_diff_free(diff);
133 git_tree_free(t1);
134 git_tree_free(t2);
135 git_repository_free(repo);
136
137 git_threads_shutdown();
138
139 return 0;
140 }
141
142 static void usage(const char *message, const char *arg)
143 {
144 if (message && arg)
145 fprintf(stderr, "%s: %s\n", message, arg);
146 else if (message)
147 fprintf(stderr, "%s\n", message);
148 fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
149 exit(1);
150 }
151
152 /** This implements very rudimentary colorized output. */
153 static int color_printer(
154 const git_diff_delta *delta,
155 const git_diff_hunk *hunk,
156 const git_diff_line *line,
157 void *data)
158 {
159 int *last_color = data, color = 0;
160
161 (void)delta; (void)hunk;
162
163 if (*last_color >= 0) {
164 switch (line->origin) {
165 case GIT_DIFF_LINE_ADDITION: color = 3; break;
166 case GIT_DIFF_LINE_DELETION: color = 2; break;
167 case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
168 case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
169 case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
170 case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
171 default: break;
172 }
173
174 if (color != *last_color) {
175 if (*last_color == 1 || color == 1)
176 fputs(colors[0], stdout);
177 fputs(colors[color], stdout);
178 *last_color = color;
179 }
180 }
181
182 return diff_output(delta, hunk, line, stdout);
183 }
184
185 /** Parse arguments as copied from git-diff. */
186 static void parse_opts(struct opts *o, int argc, char *argv[])
187 {
188 struct args_info args = ARGS_INFO_INIT;
189
190
191 for (args.pos = 1; args.pos < argc; ++args.pos) {
192 const char *a = argv[args.pos];
193
194 if (a[0] != '-') {
195 if (o->treeish1 == NULL)
196 o->treeish1 = a;
197 else if (o->treeish2 == NULL)
198 o->treeish2 = a;
199 else
200 usage("Only one or two tree identifiers can be provided", NULL);
201 }
202 else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
203 !strcmp(a, "--patch"))
204 o->format = GIT_DIFF_FORMAT_PATCH;
205 else if (!strcmp(a, "--cached"))
206 o->cached = 1;
207 else if (!strcmp(a, "--name-only"))
208 o->format = GIT_DIFF_FORMAT_NAME_ONLY;
209 else if (!strcmp(a, "--name-status"))
210 o->format = GIT_DIFF_FORMAT_NAME_STATUS;
211 else if (!strcmp(a, "--raw"))
212 o->format = GIT_DIFF_FORMAT_RAW;
213 else if (!strcmp(a, "--color"))
214 o->color = 0;
215 else if (!strcmp(a, "--no-color"))
216 o->color = -1;
217 else if (!strcmp(a, "-R"))
218 o->diffopts.flags |= GIT_DIFF_REVERSE;
219 else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
220 o->diffopts.flags |= GIT_DIFF_FORCE_TEXT;
221 else if (!strcmp(a, "--ignore-space-at-eol"))
222 o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
223 else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
224 o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
225 else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
226 o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
227 else if (!strcmp(a, "--ignored"))
228 o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
229 else if (!strcmp(a, "--untracked"))
230 o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
231 else if (match_uint16_arg(
232 &o->findopts.rename_threshold, &args, "-M") ||
233 match_uint16_arg(
234 &o->findopts.rename_threshold, &args, "--find-renames"))
235 o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
236 else if (match_uint16_arg(
237 &o->findopts.copy_threshold, &args, "-C") ||
238 match_uint16_arg(
239 &o->findopts.copy_threshold, &args, "--find-copies"))
240 o->findopts.flags |= GIT_DIFF_FIND_COPIES;
241 else if (!strcmp(a, "--find-copies-harder"))
242 o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
243 else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
244 /* TODO: parse thresholds */
245 o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
246 else if (!match_uint16_arg(
247 &o->diffopts.context_lines, &args, "-U") &&
248 !match_uint16_arg(
249 &o->diffopts.context_lines, &args, "--unified") &&
250 !match_uint16_arg(
251 &o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
252 !match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
253 !match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
254 !match_str_arg(&o->dir, &args, "--git-dir"))
255 usage("Unknown command line argument", a);
256 }
257 }