2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
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.
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.
21 static const char *colors
[] = {
25 "\033[32m", /* green */
29 /** The 'opts' struct captures all the various parsed command line options. */
31 git_diff_options diffopts
;
32 git_diff_find_options findopts
;
35 git_diff_format_t format
;
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*);
46 int main(int argc
, char *argv
[])
48 git_repository
*repo
= NULL
;
49 git_tree
*t1
= NULL
, *t2
= NULL
;
52 GIT_DIFF_OPTIONS_INIT
, GIT_DIFF_FIND_OPTIONS_INIT
,
53 -1, 0, GIT_DIFF_FORMAT_PATCH
, NULL
, NULL
, "."
58 parse_opts(&o
, argc
, argv
);
60 check_lg2(git_repository_open_ext(&repo
, o
.dir
, 0, NULL
),
61 "Could not open repository", o
.dir
);
64 * Possible argument patterns:
66 * * <sha1> <sha2>
67 * * <sha1> --cached
72 * Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2>
73 * are not supported in this example
77 treeish_to_tree(&t1
, repo
, o
.treeish1
);
79 treeish_to_tree(&t2
, repo
, o
.treeish2
);
83 git_diff_tree_to_tree(&diff
, repo
, t1
, t2
, &o
.diffopts
),
85 else if (t1
&& o
.cached
)
87 git_diff_tree_to_index(&diff
, repo
, t1
, NULL
, &o
.diffopts
),
88 "diff tree to index", NULL
);
91 git_diff_tree_to_workdir_with_index(&diff
, repo
, t1
, &o
.diffopts
),
92 "diff tree to working directory", NULL
);
94 treeish_to_tree(&t1
, repo
, "HEAD");
96 git_diff_tree_to_index(&diff
, repo
, t1
, NULL
, &o
.diffopts
),
97 "diff tree to index", NULL
);
101 git_diff_index_to_workdir(&diff
, repo
, NULL
, &o
.diffopts
),
102 "diff index to working directory", NULL
);
104 /** Apply rename and copy detection if requested. */
106 if ((o
.findopts
.flags
& GIT_DIFF_FIND_ALL
) != 0)
108 git_diff_find_similar(diff
, &o
.findopts
),
109 "finding renames and copies", NULL
);
111 /** Generate simple output using libgit2 display helper. */
114 fputs(colors
[0], stdout
);
117 git_diff_print(diff
, o
.format
, color_printer
, &o
.color
),
118 "displaying diff", NULL
);
121 fputs(colors
[0], stdout
);
123 /** Cleanup before exiting. */
128 git_repository_free(repo
);
130 git_threads_shutdown();
135 static void usage(const char *message
, const char *arg
)
138 fprintf(stderr
, "%s: %s\n", message
, arg
);
140 fprintf(stderr
, "%s\n", message
);
141 fprintf(stderr
, "usage: diff [<tree-oid> [<tree-oid>]]\n");
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
,
152 int *last_color
= data
, color
= 0;
154 (void)delta
; (void)hunk
;
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;
167 if (color
!= *last_color
) {
168 if (*last_color
== 1 || color
== 1)
169 fputs(colors
[0], stdout
);
170 fputs(colors
[color
], stdout
);
175 return diff_output(delta
, hunk
, line
, stdout
);
178 /** Parse arguments as copied from git-diff. */
179 static void parse_opts(struct opts
*o
, int argc
, char *argv
[])
181 struct args_info args
= ARGS_INFO_INIT
;
184 for (args
.pos
= 1; args
.pos
< argc
; ++args
.pos
) {
185 const char *a
= argv
[args
.pos
];
188 if (o
->treeish1
== NULL
)
190 else if (o
->treeish2
== NULL
)
193 usage("Only one or two tree identifiers can be provided", NULL
);
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"))
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"))
208 else if (!strcmp(a
, "--no-color"))
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") ||
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") ||
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") &&
242 &o
->diffopts
.context_lines
, &args
, "--unified") &&
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
);