C++ 操作Git仓库
代码
#include "common.h"
#include "args.c"
#include "common.c"
enum index_mode {
INDEX_NONE,
INDEX_ADD
};
struct index_options {
int dry_run;
int verbose;
git_repository* repo;
enum index_mode mode;
int add_update;
};
/* Forward declarations for helpers */
static void parse_opts(const char** repo_path, struct index_options* opts, struct args_info* args);
int print_matched_cb(const char* path, const char* matched_pathspec, void* payload);
int lg2_add(git_repository* repo, int argc, char** argv)
{
git_index_matched_path_cb matched_cb = NULL;
git_index* index;
git_strarray array = { 0 };
struct index_options options = { 0 };
struct args_info args = ARGS_INFO_INIT;
options.mode = INDEX_ADD;
/* Parse the options & arguments. */
parse_opts(NULL, &options, &args);
strarray_from_args(&array, &args);
/* Grab the repository's index. */
check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
/* Setup a callback if the requested options need it */
if (options.verbose || options.dry_run) {
matched_cb = &print_matched_cb;
}
options.repo = repo;
/* Perform the requested action with the index and files */
if (options.add_update) {
git_index_update_all(index, &array, matched_cb, &options);
}
else {
git_index_add_all(index, &array, 0, matched_cb, &options);
}
/* Cleanup memory */
git_index_write(index);
git_index_free(index);
return 0;
}
/*
* This callback is called for each file under consideration by
* git_index_(update|add)_all above.
* It makes uses of the callback's ability to abort the action.
*/
int print_matched_cb(const char* path, const char* matched_pathspec, void* payload)
{
struct index_options* opts = (struct index_options*)(payload);
int ret;
unsigned status;
(void)matched_pathspec;
/* Get the file status */
if (git_status_file(&status, opts->repo, path) < 0)
return -1;
if ((status & GIT_STATUS_WT_MODIFIED) || (status & GIT_STATUS_WT_NEW)) {
printf("add '%s'\n", path);
ret = 0;
}
else {
ret = 1;
}
if (opts->dry_run)
ret = 1;
return ret;
}
static void parse_opts(const char** repo_path, struct index_options* opts, struct args_info* args)
{
if (args->argc <= 1)
return;
for (args->pos = 1; args->pos < args->argc; ++args->pos) {
const char* curr = args->argv[args->pos];
if (curr[0] != '-') {
if (!strcmp("add", curr)) {
opts->mode = INDEX_ADD;
continue;
}
else if (opts->mode == INDEX_NONE) {
fprintf(stderr, "missing command: %s", curr);
break;
}
else {
/* We might be looking at a filename */
break;
}
}
else if (match_bool_arg(&opts->verbose, args, "--verbose") ||
match_bool_arg(&opts->dry_run, args, "--dry-run") ||
match_str_arg(repo_path, args, "--git-dir") ||
(opts->mode == INDEX_ADD && match_bool_arg(&opts->add_update, args, "--update"))) {
continue;
}
else if (match_bool_arg(NULL, args, "--help")) {
break;
}
else if (match_arg_separator(args)) {
break;
}
else {
fprintf(stderr, "Unsupported option %s.\n", curr);
}
}
}
struct blame_opts {
char* path;
char* commitspec;
int C;
int M;
int start_line;
int end_line;
int F;
};
static void parse_opts(struct blame_opts* o, int argc, char* argv[]);
int lg2_blame(git_repository* repo, int argc, char* argv[])
{
int line, break_on_null_hunk;
git_object_size_t i, rawsize;
char spec[1024] = { 0 };
struct blame_opts o = { 0 };
const char* rawdata;
git_revspec revspec = { 0 };
git_blame_options blameopts = GIT_BLAME_OPTIONS_INIT;
git_blame* blame = NULL;
git_blob* blob;
git_object* obj;
parse_opts(&o, argc, argv);
if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT;
if (o.start_line && o.end_line) {
blameopts.min_line = o.start_line;
blameopts.max_line = o.end_line;
}
/**
* The commit range comes in "committish" form. Use the rev-parse API to
* nail down the end points.
*/
if (o.commitspec) {
check_lg2(git_revparse(&revspec, repo, o.commitspec), "Couldn't parse commit spec", NULL);
if (revspec.flags & GIT_REVSPEC_SINGLE) {
git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.from));
git_object_free(revspec.from);
}
else {
git_oid_cpy(&blameopts.oldest_commit, git_object_id(revspec.from));
git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.to));
git_object_free(revspec.from);
git_object_free(revspec.to);
}
}
/** Run the blame. */
check_lg2(git_blame_file(&blame, repo, o.path, &blameopts), "Blame error", NULL);
/**
* Get the raw data inside the blob for output. We use the
* `committish:path/to/file.txt` format to find it.
*/
if (git_oid_is_zero(&blameopts.newest_commit))
strcpy(spec, "HEAD");
else
git_oid_tostr(spec, sizeof(spec), &blameopts.newest_commit);
strcat(spec, ":");
strcat(spec, o.path);
check_lg2(git_revparse_single(&obj, repo, spec), "Object lookup error", NULL);
check_lg2(git_blob_lookup(&blob, repo, git_object_id(obj)), "Blob lookup error", NULL);
git_object_free(obj);
rawdata = (const char*)git_blob_rawcontent(blob);
rawsize = git_blob_rawsize(blob);
/** Produce the output. */
line = 1;
i = 0;
break_on_null_hunk = 0;
while (i < rawsize) {
const char* eol = (const char*)memchr(rawdata + i, '\n', (size_t)(rawsize - i));
char oid[10] = { 0 };
const git_blame_hunk* hunk = git_blame_get_hunk_byline(blame, line);
if (break_on_null_hunk && !hunk)
break;
if (hunk) {
char sig[128] = { 0 };
break_on_null_hunk = 1;
git_oid_tostr(oid, 10, &hunk->final_commit_id);
snprintf(sig, 30, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email);
printf("%s ( %-30s %3d) %.*s\n",
oid,
sig,
line,
(int)(eol - rawdata - i),
rawdata + i);
}
i = (int)(eol - rawdata + 1);
line++;
}
/** Cleanup. */
git_blob_free(blob);
git_blame_free(blame);
return 0;
}
/** Tell the user how to make this thing work. */
static void usage(const char* msg, const char* arg)
{
if (msg && arg)
fprintf(stderr, "%s: %s\n", msg, arg);
else if (msg)
fprintf(stderr, "%s\n", msg);
fprintf(stderr, "usage: blame [options] [<commit range>] <path>\n");
fprintf(stderr, "\n");
fprintf(stderr, " <commit range> example: `HEAD~10..HEAD`, or `1234abcd`\n");
fprintf(stderr, " -L <n,m> process only line range n-m, counting from 1\n");
fprintf(stderr, " -M find line moves within and across files\n");
fprintf(stderr, " -C find line copies within and across files\n");
fprintf(stderr, " -F follow only the first parent commits\n");
fprintf(stderr, "\n");
exit(1);
}
/** Parse the arguments. */
static void parse_opts(struct blame_opts* o, int argc, char* argv[])
{
int i;
char* bare_args[3] = { 0 };
if (argc < 2) usage(NULL, NULL);
for (i = 1; i < argc; i++) {
char* a = argv[i];
if (a[0] != '-') {
int i = 0;
while (bare_args[i] && i < 3) ++i;
if (i >= 3)
usage("Invalid argument set", NULL);
bare_args[i] = a;
}
else if (!strcmp(a, "--"))
continue;
else if (!strcasecmp(a, "-M"))
o->M = 1;
else if (!strcasecmp(a, "-C"))
o->C = 1;
else if (!strcasecmp(a, "-F"))
o->F = 1;
else if (!strcasecmp(a, "-L")) {
i++; a = argv[i];
if (i >= argc) fatal("Not enough arguments to -L", NULL);
check_lg2(sscanf(a, "%d,%d", &o->start_line, &o->end_line) - 2, "-L format error", NULL);
}
else {
/* commit range */
if (o->commitspec) fatal("Only one commit spec allowed", NULL);
o->commitspec = a;
}
}
/* Handle the bare arguments */
if (!bare_args[0]) usage("Please specify a path", NULL);
o->path = bare_args[0];
if (bare_args[1]) {
/* <commitspec> <path> */
o->path = bare_args[1];
o->commitspec = bare_args[0];
}
if (bare_args[2]) {
/* <oldcommit> <newcommit> <path> */
char spec[128] = { 0 };
o->path = bare_args[2];
sprintf(spec, "%s..%s", bare_args[0], bare_args[1]);
o->commitspec = spec;
}
}
// git add调用
void testAdd(git_repository* repo) {
int argc = 2;
//const char** argv = { "add", "--verbose", "--dry-run", "--git-dir", "--update", NULL };
char* argv1 = "add";
char* argv2 = "main.cpp";
char* argv[] = { argv1, argv2, NULL };
lg2_add(repo, argc, argv);
}
// git blame调用
void testBlame(git_repository* repo) {
int argc = 2;
//const char** argv = { "add", "--verbose", "--dry-run", "--git-dir", "--update", NULL };
char* argv1 = "blame ";
char* argv2 = "`HEAD~10..HEAD`";
char* argv3 = "-F";
char* argv[] = { argv1, argv2, argv3, NULL };
lg2_blame(repo, argc, argv);
}
void test() {
git_libgit2_init();
git_repository* repo;
const char* repo_path = "D:\\libgit2\\.git";
auto error = git_repository_open(&repo, repo_path);
if (error) {
printf("open git error");
return;
}
testAdd(repo);// git add调用
testBlame(repo);// git blame调用
git_repository_free(repo);
}
输出
Blame error [-3] - the path '`HEAD~10..HEAD`' does not exist in the given tree
参考
https://github.com/libgit2/libgit2
创作不易,小小的支持一下吧!
原文地址:https://blog.csdn.net/qq_30220519/article/details/140833571
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!