☰
C++ 操作Git仓库
2026/11/2 10:42:46 网站建设 项目流程
代码
#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

Libraries / libqgit2 · GitLab


创作不易,小小的支持一下吧!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询