added CheckoutBranch()
This commit is contained in:
parent
4101823f6e
commit
c2328b69f0
@ -33,6 +33,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (InitGitLibrary() > 0) {
|
||||
qCritical() << CloneRepository("https://git.mimbach49.de/GerhardHoffmann/customer_999.git", "C:\\tmp\\customer_999");
|
||||
qCritical() << CheckoutLocalBranch("C:\\tmp\\customer_999", "master");
|
||||
ShutdownGitLibrary();
|
||||
}
|
||||
#else
|
||||
|
@ -2,7 +2,9 @@ QT+=core
|
||||
|
||||
TEMPLATE = lib
|
||||
DEFINES += CALCULATOR_C_INTERFACE_LIBRARY
|
||||
QMAKE_CXXFLAGS += -fPIC
|
||||
|
||||
|
||||
QMAKE_CXXFLAGS += -fPIC -std=c++20
|
||||
|
||||
unix {
|
||||
LIBS += -L/usr/lib64 -lgit2
|
||||
@ -10,6 +12,7 @@ unix {
|
||||
|
||||
win32 {
|
||||
INCLUDEPATH += C:\Users\G.Hoffmann\Downloads\libgit2-1.7.2\libgit2-1.7.2\include
|
||||
|
||||
LIBS += -LC:\Users\G.Hoffmann\Downloads\libgit2-1.7.2\libgit2-1.7.2\build\Debug -lgit2
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,215 @@
|
||||
#include "calculator_c_interface_lib.h"
|
||||
|
||||
//#include <git2/common.h>
|
||||
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
|
||||
|
||||
#include <git2.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static QMap<QString, git_repository *> customerRepoMap;
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// see https://libgit2.org/libgit2/ex/HEAD/checkout.html
|
||||
|
||||
/* Define t/he printf format specifier to use for size_t output */
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
# define PRIuZ "Illu"
|
||||
# define PRIxZ "Illx"
|
||||
# define PRIdZ "Illd"
|
||||
#else
|
||||
# define PRIuZ "zu"
|
||||
# define PRIxZ "zx"
|
||||
# define PRIdZ "zd"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned int force : 1;
|
||||
unsigned int progress : 1;
|
||||
unsigned int perf : 1;
|
||||
} checkout_options;
|
||||
|
||||
static void print_checkout_progress(const char *path, size_t completed_steps, size_t total_steps, void *payload) {
|
||||
(void)payload;
|
||||
if (path == NULL) {
|
||||
printf("checkout started: %" PRIuZ " steps\n", total_steps);
|
||||
} else {
|
||||
printf("checkout: %s %" PRIuZ "/%" PRIuZ "\n", path, completed_steps, total_steps);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_perf_data(const git_checkout_perfdata *perfdata, void *payload) {
|
||||
(void)payload;
|
||||
printf("perf: stat: %" PRIuZ " mkdir: %" PRIuZ " chmod: %" PRIuZ "\n",
|
||||
perfdata->stat_calls, perfdata->mkdir_calls, perfdata->chmod_calls);
|
||||
}
|
||||
|
||||
static int resolve_refish(git_annotated_commit **commit, git_repository *repo, const char *refish)
|
||||
{
|
||||
git_reference *ref;
|
||||
git_object *obj;
|
||||
int err = 0;
|
||||
|
||||
assert(commit != NULL);
|
||||
|
||||
err = git_reference_dwim(&ref, repo, refish);
|
||||
if (err == GIT_OK) {
|
||||
git_annotated_commit_from_ref(commit, repo, ref);
|
||||
git_reference_free(ref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = git_revparse_single(&obj, repo, refish);
|
||||
if (err == GIT_OK) {
|
||||
err = git_annotated_commit_lookup(commit, repo, git_object_id(obj));
|
||||
git_object_free(obj);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perform_checkout_ref(git_repository *repo,
|
||||
git_annotated_commit *target,
|
||||
const char *target_ref, checkout_options *opts) {
|
||||
git_checkout_options checkout_opts;
|
||||
git_reference *ref = NULL, *branch = NULL;
|
||||
git_commit *target_commit = NULL;
|
||||
int err;
|
||||
|
||||
if ((err = git_checkout_options_init(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION)) < 0) {
|
||||
fprintf(stderr, "error checkout options init %s\n", git_error_last()->message);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Setup our checkout options from the parsed options
|
||||
|
||||
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
||||
|
||||
// if (opts->force)
|
||||
// checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
|
||||
|
||||
// if (opts->progress)
|
||||
// checkout_opts.progress_cb = print_checkout_progress;
|
||||
|
||||
// if (opts->perf)
|
||||
// checkout_opts.perfdata_cb = print_perf_data;
|
||||
|
||||
// Grab the commit we're interested to move to
|
||||
|
||||
err = git_commit_lookup(&target_commit, repo, git_annotated_commit_id(target));
|
||||
if (err != 0) {
|
||||
fprintf(stderr, "failed to lookup commit: %s\n", git_error_last()->message);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Perform the checkout so the workdir corresponds to what target_commit contains.
|
||||
// Note that it's okay to pass a git_commit here, because it will be peeled to a tree.
|
||||
|
||||
err = git_checkout_tree(repo, (const git_object *)target_commit, &checkout_opts);
|
||||
if (err != 0) {
|
||||
fprintf(stderr, "failed to checkout tree: %s\n", git_error_last()->message);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Now that the checkout has completed, we have to update HEAD.
|
||||
// Depending on the "origin" of target (ie. it's an OID or a branch name), we might need to detach HEAD.
|
||||
|
||||
if (git_annotated_commit_ref(target)) {
|
||||
const char *target_head;
|
||||
|
||||
if ((err = git_reference_lookup(&ref, repo, git_annotated_commit_ref(target))) < 0)
|
||||
goto error;
|
||||
|
||||
if (git_reference_is_remote(ref)) {
|
||||
if ((err = git_branch_create_from_annotated(&branch, repo, target_ref, target, 0)) < 0)
|
||||
goto error;
|
||||
target_head = git_reference_name(branch);
|
||||
} else {
|
||||
target_head = git_annotated_commit_ref(target);
|
||||
}
|
||||
|
||||
err = git_repository_set_head(repo, target_head);
|
||||
} else {
|
||||
err = git_repository_set_head_detached_from_annotated(repo, target);
|
||||
}
|
||||
|
||||
error:
|
||||
if (err != 0) {
|
||||
fprintf(stderr, "failed to update HEAD reference: %s\n", git_error_last()->message);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_commit_free(target_commit);
|
||||
git_reference_free(branch);
|
||||
git_reference_free(ref);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int guess_refish(git_annotated_commit **out, git_repository *repo, const char *ref) {
|
||||
git_strarray remotes = { NULL, 0 };
|
||||
git_reference *remote_ref = NULL;
|
||||
int error;
|
||||
size_t i;
|
||||
|
||||
if ((error = git_remote_list(&remotes, repo)) < 0) {
|
||||
fprintf(stderr, "error git_remote_list %s\n", git_error_last()->message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < remotes.count; i++) {
|
||||
char *refname = NULL;
|
||||
size_t reflen;
|
||||
|
||||
reflen = snprintf(refname, 0, "refs/remotes/%s/%s", remotes.strings[i], ref);
|
||||
if ((refname = (char *)malloc(reflen + 1)) == NULL) {
|
||||
error = -1;
|
||||
fprintf(stderr, "%s:%d malloc failed\n", __func__, __LINE__);
|
||||
goto next;
|
||||
}
|
||||
snprintf(refname, reflen + 1, "refs/remotes/%s/%s", remotes.strings[i], ref);
|
||||
|
||||
if ((error = git_reference_lookup(&remote_ref, repo, refname)) < 0) {
|
||||
fprintf(stderr, "git reference lookup %s\n", git_error_last()->message);
|
||||
goto next;
|
||||
}
|
||||
|
||||
break;
|
||||
next:
|
||||
free(refname);
|
||||
refname = NULL;
|
||||
if (error < 0 && error != GIT_ENOTFOUND) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!remote_ref) {
|
||||
error = GIT_ENOTFOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((error = git_annotated_commit_from_ref(out, repo, remote_ref)) < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
git_reference_free(remote_ref);
|
||||
git_strarray_dispose(&remotes);
|
||||
return error;
|
||||
}
|
||||
|
||||
TariffCalculatorHandle NewTariffCalculator(void) {
|
||||
return new TariffCalculator();
|
||||
}
|
||||
@ -21,14 +223,29 @@ int InitGitLibrary(void) {
|
||||
}
|
||||
|
||||
int ShutdownGitLibrary(void) {
|
||||
|
||||
for (auto key : customerRepoMap.keys()) {
|
||||
git_repository_free(customerRepoMap.value(key));
|
||||
}
|
||||
customerRepoMap.clear();
|
||||
return git_libgit2_shutdown();
|
||||
}
|
||||
|
||||
int CloneRepository(char const *url, char const *local_path) {
|
||||
|
||||
QString localRepoPath(QDir::cleanPath(QString(local_path) + QDir::separator() + ".git"));
|
||||
QDir localRepoDir(localRepoPath);
|
||||
|
||||
if (localRepoDir.exists()) {
|
||||
fprintf(stderr, "local repository path %s already exists\n",
|
||||
localRepoPath.toUtf8().constData());
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_repository *out;
|
||||
git_clone_options opts;
|
||||
|
||||
int res = 0;
|
||||
|
||||
if ((res = git_clone_options_init(&opts, GIT_CLONE_OPTIONS_VERSION)) == 0) {
|
||||
opts.checkout_branch = "master";
|
||||
|
||||
@ -37,6 +254,9 @@ int CloneRepository(char const *url, char const *local_path) {
|
||||
if ((res = git_clone(&out, url, local_path, &opts)) < 0) {
|
||||
git_error const *error = git_error_last();
|
||||
fprintf(stderr, "%s:%d error: %s\n", __func__, __LINE__, error->message);
|
||||
} else {
|
||||
QString localRepoPath = QDir::cleanPath(QString(local_path) + QDir::separator() + ".git");
|
||||
customerRepoMap.insert(localRepoPath, out);
|
||||
}
|
||||
} else {
|
||||
git_error const *error = git_error_last();
|
||||
@ -45,6 +265,96 @@ int CloneRepository(char const *url, char const *local_path) {
|
||||
return res;
|
||||
}
|
||||
|
||||
int CheckoutLocalBranch(char const *local_path, char const *branch_name) {
|
||||
int err = 0;
|
||||
checkout_options opts;
|
||||
git_repository_state_t state = GIT_REPOSITORY_STATE_NONE;
|
||||
git_annotated_commit *checkout_target = NULL;
|
||||
git_repository *repo = NULL;
|
||||
git_reference *branch_ref = NULL;
|
||||
|
||||
QString localRepoPath(QDir::cleanPath(QString(local_path) + QDir::separator() + ".git"));
|
||||
QDir localRepoDir(localRepoPath);
|
||||
|
||||
if (!localRepoDir.exists()) {
|
||||
fprintf(stderr, "local repository path %s does not exist\n",
|
||||
localRepoPath.toUtf8().constData());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!customerRepoMap.contains(localRepoPath)) {
|
||||
if ((err = git_repository_open(&repo, localRepoPath.toUtf8().constData())) != 0) {
|
||||
fprintf(stderr, "repository %s cannot be opened: %s\n",
|
||||
localRepoPath.toUtf8().constData(), git_error_last()->message);
|
||||
return -1;
|
||||
}
|
||||
customerRepoMap.insert(localRepoPath, repo);
|
||||
}
|
||||
|
||||
if (customerRepoMap.contains(localRepoPath)) {
|
||||
repo = customerRepoMap[localRepoPath];
|
||||
|
||||
// Make sure we're not about to checkout while something else is going on
|
||||
if ((state = (git_repository_state_t)git_repository_state(repo)) != GIT_REPOSITORY_STATE_NONE) {
|
||||
fprintf(stderr, "repository %s is in unexpected state %d\n",
|
||||
localRepoPath.toUtf8().constData(), state);
|
||||
err = -1;
|
||||
} else {
|
||||
fprintf(stderr, "repository %s is in state %d\n",
|
||||
localRepoPath.toUtf8().constData(), state);
|
||||
|
||||
if ((err = resolve_refish(&checkout_target, repo, branch_name)) < 0 &&
|
||||
(err = guess_refish(&checkout_target, repo, branch_name)) < 0) {
|
||||
fprintf(stderr, "failed to resolve %s: %s\n", branch_name, git_error_last()->message);
|
||||
} else {
|
||||
if ((err = perform_checkout_ref(repo, checkout_target, branch_name, &opts)) == 0) {
|
||||
fprintf(stderr, "%s:%d checkout ok\n", __func__, __LINE__);
|
||||
if ((err = git_branch_lookup(&branch_ref, repo, branch_name, GIT_BRANCH_LOCAL)) != 0) {
|
||||
if (err == GIT_ENOTFOUND) {
|
||||
fprintf(stderr, "local branch %s not found\n", branch_name);
|
||||
} else {
|
||||
fprintf(stderr, "local branch %s not found: %s\n",
|
||||
branch_name, git_error_last()->message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (branch_ref) {
|
||||
git_reference_free(branch_ref);
|
||||
}
|
||||
|
||||
if (checkout_target) {
|
||||
git_annotated_commit_free(checkout_target);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int CommitFile(char const *local_path, char const *file_name) {
|
||||
assert(local_path != NULL);
|
||||
assert(file_name != NULL);
|
||||
|
||||
QString const currentWD = QDir::currentPath();
|
||||
QString localPath(path);
|
||||
|
||||
if (!localPath.exists()) {
|
||||
fprintf(stderr, "local path %s for file %s does not exist\n",
|
||||
localRepoPath.toUtf8().constData(), file_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (QDir::setCurrent(localPath)) {
|
||||
|
||||
}
|
||||
|
||||
QDir::setCurrent(currentWD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -13,13 +13,12 @@ extern "C" {
|
||||
TariffCalculatorHandle NewTariffCalculator(void) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
void DeleteTariffCalculator(TariffCalculatorHandle handle) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
|
||||
// libgit2
|
||||
int InitGitLibrary(void) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
int ShutdownGitLibrary(void) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
|
||||
int CloneRepository(char const *url, char const *local_path) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
|
||||
|
||||
void DeleteTariffCalculator(TariffCalculatorHandle handle) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
int CheckoutLocalBranch(char const *local_path, char const *branch) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
int CommitFile(char const *local_path, char const *branch_name, char const *file_name) CALCULATOR_C_INTERFACE_LIB_EXPORT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user