2 * libgit2 "blame" example - shows how to use the blame 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 how to invoke the libgit2 blame API to roughly
19 * simulate the output of `git blame` and a few of its command line arguments.
22 static void usage(const char *msg
, const char *arg
);
24 int main(int argc
, char *argv
[])
26 int i
, line
, break_on_null_hunk
;
27 const char *path
= NULL
, *a
;
28 const char *rawdata
, *commitspec
=NULL
, *bare_args
[3] = {0};
29 char spec
[1024] = {0};
30 git_repository
*repo
= NULL
;
31 git_revspec revspec
= {0};
32 git_blame_options opts
= GIT_BLAME_OPTIONS_INIT
;
33 git_blame
*blame
= NULL
;
38 if (argc
< 2) usage(NULL
, NULL
);
40 for (i
=1; i
<argc
; i
++) {
45 while (bare_args
[i
] && i
< 3) ++i
;
47 usage("Invalid argument set", NULL
);
50 else if (!strcmp(a
, "--"))
52 else if (!strcasecmp(a
, "-M"))
53 opts
.flags
|= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
;
54 else if (!strcasecmp(a
, "-C"))
55 opts
.flags
|= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
;
56 else if (!strcasecmp(a
, "-L")) {
58 if (i
>= argc
) fatal("Not enough arguments to -L", NULL
);
59 check_lg2(sscanf(a
, "%d,%d", &opts
.min_line
, &opts
.max_line
)-2, "-L format error", NULL
);
63 if (commitspec
) fatal("Only one commit spec allowed", NULL
);
68 /* Handle the bare arguments */
69 if (!bare_args
[0]) usage("Please specify a path", NULL
);
72 /* <commitspec> <path> */
74 commitspec
= bare_args
[0];
77 /* <oldcommit> <newcommit> <path> */
79 sprintf(spec
, "%s..%s", bare_args
[0], bare_args
[1]);
84 check_lg2(git_repository_open_ext(&repo
, ".", 0, NULL
), "Couldn't open repository", NULL
);
86 /* Parse the end points */
88 check_lg2(git_revparse(&revspec
, repo
, commitspec
), "Couldn't parse commit spec", NULL
);
89 if (revspec
.flags
& GIT_REVPARSE_SINGLE
) {
90 git_oid_cpy(&opts
.newest_commit
, git_object_id(revspec
.from
));
91 git_object_free(revspec
.from
);
93 git_oid_cpy(&opts
.oldest_commit
, git_object_id(revspec
.from
));
94 git_oid_cpy(&opts
.newest_commit
, git_object_id(revspec
.to
));
95 git_object_free(revspec
.from
);
96 git_object_free(revspec
.to
);
101 check_lg2(git_blame_file(&blame
, repo
, path
, &opts
), "Blame error", NULL
);
103 /* Get the raw data for output */
104 if (git_oid_iszero(&opts
.newest_commit
))
105 strcpy(spec
, "HEAD");
107 git_oid_tostr(spec
, sizeof(spec
), &opts
.newest_commit
);
113 check_lg2(git_revparse_single(&obj
, repo
, spec
), "Object lookup error", NULL
);
114 check_lg2(git_blob_lookup(&blob
, repo
, git_object_id(obj
)), "Blob lookup error", NULL
);
115 git_object_free(obj
);
117 rawdata
= git_blob_rawcontent(blob
);
119 /* Produce the output */
122 break_on_null_hunk
= 0;
123 while (i
< git_blob_rawsize(blob
)) {
124 const char *eol
= strchr(rawdata
+i
, '\n');
126 const git_blame_hunk
*hunk
= git_blame_get_hunk_byline(blame
, line
);
128 if (break_on_null_hunk
&& !hunk
) break;
131 break_on_null_hunk
= 1;
134 git_oid_tostr(oid
, 10, &hunk
->final_commit_id
);
135 snprintf(sig
, 30, "%s <%s>", hunk
->final_signature
->name
, hunk
->final_signature
->email
);
137 printf("%s ( %-30s %3d) %.*s\n",
141 (int)(eol
-rawdata
-i
),
145 i
= eol
- rawdata
+ 1;
151 git_blame_free(blame
);
152 git_repository_free(repo
);
153 git_threads_shutdown();
156 static void usage(const char *msg
, const char *arg
)
159 fprintf(stderr
, "%s: %s\n", msg
, arg
);
161 fprintf(stderr
, "%s\n", msg
);
162 fprintf(stderr
, "usage: blame [options] [<commit range>] <path>\n");
163 fprintf(stderr
, "\n");
164 fprintf(stderr
, " <commit range> example: `HEAD~10..HEAD`, or `1234abcd`\n");
165 fprintf(stderr
, " -L <n,m> process only line range n-m, counting from 1\n");
166 fprintf(stderr
, " -M find line moves within and across files\n");
167 fprintf(stderr
, " -C find line copies within and across files\n");
168 fprintf(stderr
, "\n");