2 * libgit2 "diff" example - shows how to use the diff API
4 * Written by the libgit2 contributors
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.
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/>.
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.
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.
28 static const char *colors
[] = {
32 "\033[32m", /* green */
36 /** The 'opts' struct captures all the various parsed command line options. */
38 git_diff_options diffopts
;
39 git_diff_find_options findopts
;
42 git_diff_format_t format
;
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*);
53 int main(int argc
, char *argv
[])
55 git_repository
*repo
= NULL
;
56 git_tree
*t1
= NULL
, *t2
= NULL
;
59 GIT_DIFF_OPTIONS_INIT
, GIT_DIFF_FIND_OPTIONS_INIT
,
60 -1, 0, GIT_DIFF_FORMAT_PATCH
, NULL
, NULL
, "."
65 parse_opts(&o
, argc
, argv
);
67 check_lg2(git_repository_open_ext(&repo
, o
.dir
, 0, NULL
),
68 "Could not open repository", o
.dir
);
71 * Possible argument patterns:
73 * * <sha1> <sha2>
74 * * <sha1> --cached
79 * Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2>
80 * are not supported in this example
84 treeish_to_tree(&t1
, repo
, o
.treeish1
);
86 treeish_to_tree(&t2
, repo
, o
.treeish2
);
90 git_diff_tree_to_tree(&diff
, repo
, t1
, t2
, &o
.diffopts
),
92 else if (t1
&& o
.cached
)
94 git_diff_tree_to_index(&diff
, repo
, t1
, NULL
, &o
.diffopts
),
95 "diff tree to index", NULL
);
98 git_diff_tree_to_workdir_with_index(&diff
, repo
, t1
, &o
.diffopts
),
99 "diff tree to working directory", NULL
);
101 treeish_to_tree(&t1
, repo
, "HEAD");
103 git_diff_tree_to_index(&diff
, repo
, t1
, NULL
, &o
.diffopts
),
104 "diff tree to index", NULL
);
108 git_diff_index_to_workdir(&diff
, repo
, NULL
, &o
.diffopts
),
109 "diff index to working directory", NULL
);
111 /** Apply rename and copy detection if requested. */
113 if ((o
.findopts
.flags
& GIT_DIFF_FIND_ALL
) != 0)
115 git_diff_find_similar(diff
, &o
.findopts
),
116 "finding renames and copies", NULL
);
118 /** Generate simple output using libgit2 display helper. */
121 fputs(colors
[0], stdout
);
124 git_diff_print(diff
, o
.format
, color_printer
, &o
.color
),
125 "displaying diff", NULL
);
128 fputs(colors
[0], stdout
);
130 /** Cleanup before exiting. */
135 git_repository_free(repo
);
137 git_threads_shutdown();
142 static void usage(const char *message
, const char *arg
)
145 fprintf(stderr
, "%s: %s\n", message
, arg
);
147 fprintf(stderr
, "%s\n", message
);
148 fprintf(stderr
, "usage: diff [<tree-oid> [<tree-oid>]]\n");
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
,
159 int *last_color
= data
, color
= 0;
161 (void)delta
; (void)hunk
;
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;
174 if (color
!= *last_color
) {
175 if (*last_color
== 1 || color
== 1)
176 fputs(colors
[0], stdout
);
177 fputs(colors
[color
], stdout
);
182 return diff_output(delta
, hunk
, line
, stdout
);
185 /** Parse arguments as copied from git-diff. */
186 static void parse_opts(struct opts
*o
, int argc
, char *argv
[])
188 struct args_info args
= ARGS_INFO_INIT
;
191 for (args
.pos
= 1; args
.pos
< argc
; ++args
.pos
) {
192 const char *a
= argv
[args
.pos
];
195 if (o
->treeish1
== NULL
)
197 else if (o
->treeish2
== NULL
)
200 usage("Only one or two tree identifiers can be provided", NULL
);
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"))
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"))
215 else if (!strcmp(a
, "--no-color"))
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") ||
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") ||
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") &&
249 &o
->diffopts
.context_lines
, &args
, "--unified") &&
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
);