summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEtienne Samson <samson.etienne@gmail.com>2018-07-07 12:52:24 +0000
committerEtienne Samson <samson.etienne@gmail.com>2018-07-17 21:43:28 +0200
commitb24202e15462802f7c5ff4c550949453dcbd0da3 (patch)
tree591be926ee415e27a713063dd1c916c8485c3c45
parent5b7ba78630d935a8222db11d5bccffe85ddbf1eb (diff)
downloadlibgit2-b24202e15462802f7c5ff4c550949453dcbd0da3.tar.gz
examples: add checkout
-rw-r--r--examples/checkout.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/examples/checkout.c b/examples/checkout.c
new file mode 100644
index 000000000..f92ad7399
--- /dev/null
+++ b/examples/checkout.c
@@ -0,0 +1,235 @@
+/*
+ * libgit2 "checkout" example - shows how to perform checkouts
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+#include <assert.h>
+
+/* Define the printf format specifer to use for size_t output */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+# define PRIuZ "Iu"
+# define PRIxZ "Ix"
+# define PRIdZ "Id"
+#else
+# define PRIuZ "zu"
+# define PRIxZ "zx"
+# define PRIdZ "zd"
+#endif
+
+/**
+ * The following example demonstrates how to do checkouts with libgit2.
+ *
+ * Recognized options are :
+ * --force: force the checkout to happen.
+ * --[no-]progress: show checkout progress, on by default.
+ * --perf: show performance data.
+ */
+
+typedef struct {
+ int force : 1;
+ int progress : 1;
+ int perf : 1;
+} checkout_options;
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: checkout [options] <branch>\n"
+ "Options are :\n"
+ " --git-dir: use the following git repository.\n"
+ " --force: force the checkout.\n"
+ " --[no-]progress: show checkout progress.\n"
+ " --perf: show performance data.\n");
+ exit(1);
+}
+
+static void parse_options(const char **repo_path, checkout_options *opts, struct args_info *args)
+{
+ if (args->argc <= 1)
+ print_usage();
+
+ memset(opts, 0, sizeof(*opts));
+
+ /* Default values */
+ opts->progress = 1;
+
+ for (args->pos = 1; args->pos < args->argc; ++args->pos) {
+ const char *curr = args->argv[args->pos];
+ int bool_arg;
+
+ if (strcmp(curr, "--") == 0) {
+ break;
+ } else if (!strcmp(curr, "--force")) {
+ opts->force = 1;
+ } else if (match_bool_arg(&bool_arg, args, "--progress")) {
+ opts->progress = bool_arg;
+ } else if (match_bool_arg(&bool_arg, args, "--perf")) {
+ opts->perf = bool_arg;
+ } else if (match_str_arg(repo_path, args, "--git-dir")) {
+ continue;
+ } else {
+ break;
+ }
+ }
+}
+
+/**
+ * This function is called to report progression, ie. it's called once with
+ * a NULL path and the number of total steps, then for each subsequent path,
+ * the current completed_step value.
+ */
+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);
+ }
+}
+
+/**
+ * This function is called when the checkout completes, and is used to report the
+ * number of syscalls performed.
+ */
+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);
+}
+
+/**
+ * This is the main "checkout <branch>" function, responsible for performing
+ * a branch-based checkout.
+ */
+static int perform_checkout_ref(git_repository *repo, git_annotated_commit *target, checkout_options *opts)
+{
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_commit *target_commit = NULL;
+ int 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", giterr_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", giterr_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)) {
+ err = git_repository_set_head(repo, git_annotated_commit_ref(target));
+ } else {
+ err = git_repository_set_head_detached_from_annotated(repo, target);
+ }
+ if (err != 0) {
+ fprintf(stderr, "failed to update HEAD reference: %s\n", giterr_last()->message);
+ goto cleanup;
+ }
+
+cleanup:
+ git_commit_free(target_commit);
+
+ return err;
+}
+
+/** That example's entry point */
+int main(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ struct args_info args = ARGS_INFO_INIT;
+ checkout_options opts;
+ git_repository_state_t state;
+ git_annotated_commit *checkout_target = NULL;
+ int err = 0;
+ const char *path = ".";
+
+ /** Parse our command line options */
+ parse_options(&path, &opts, &args);
+
+ /** Initialize the library */
+ err = git_libgit2_init();
+ if (!err)
+ check_lg2(err, "Failed to initialize libgit2", NULL);
+
+ /** Open the repository corresponding to the options */
+ check_lg2(git_repository_open_ext(&repo, path, 0, NULL),
+ "Could not open repository", NULL);
+
+ /** Make sure we're not about to checkout while something else is going on */
+ state = git_repository_state(repo);
+ if (state != GIT_REPOSITORY_STATE_NONE) {
+ fprintf(stderr, "repository is in unexpected state %d\n", state);
+ goto cleanup;
+ }
+
+ if (args.pos >= args.argc) {
+ fprintf(stderr, "unhandled\n");
+ err = -1;
+ goto cleanup;
+ } else if (strcmp("--", args.argv[args.pos])) {
+ /**
+ * Try to checkout the given path
+ */
+
+ fprintf(stderr, "unhandled path-based checkout\n");
+ err = 1;
+ goto cleanup;
+ } else {
+ /**
+ * Try to resolve a "refish" argument to a target libgit2 can use
+ */
+ err = resolve_refish(&checkout_target, repo, args.argv[args.pos]);
+ if (err != 0) {
+ fprintf(stderr, "failed to resolve %s: %s\n", args.argv[args.pos], giterr_last()->message);
+ goto cleanup;
+ }
+ err = perform_checkout_ref(repo, checkout_target, &opts);
+ }
+
+cleanup:
+ git_annotated_commit_free(checkout_target);
+
+ git_repository_free(repo);
+ git_libgit2_shutdown();
+
+ return err;
+}