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