]> git.proxmox.com Git - libgit2.git/blobdiff - examples/checkout.c
New upstream version 1.1.0+dfsg.1
[libgit2.git] / examples / checkout.c
index 9119d65036bac168b560d5662a5303888b760ea6..204b58d88251e0138706683ac60c7f146147fbae 100644 (file)
@@ -13,7 +13,6 @@
  */
 
 #include "common.h"
-#include <assert.h>
 
 /* Define the printf format specifer to use for size_t output */
 #if defined(_MSC_VER) || defined(__MINGW32__)
@@ -66,7 +65,7 @@ static void parse_options(const char **repo_path, checkout_options *opts, struct
                const char *curr = args->argv[args->pos];
                int bool_arg;
 
-               if (strcmp(curr, "--") == 0) {
+               if (match_arg_separator(args)) {
                        break;
                } else if (!strcmp(curr, "--force")) {
                        opts->force = 1;
@@ -112,9 +111,10 @@ static void print_perf_data(const git_checkout_perfdata *perfdata, void *payload
  * 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)
+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_CHECKOUT_OPTIONS_INIT;
+       git_reference *ref = NULL, *branch = NULL;
        git_commit *target_commit = NULL;
        int err;
 
@@ -156,10 +156,25 @@ static int perform_checkout_ref(git_repository *repo, git_annotated_commit *targ
         * we might need to detach HEAD.
         */
        if (git_annotated_commit_ref(target)) {
-               err = git_repository_set_head(repo, 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;
@@ -167,14 +182,70 @@ static int perform_checkout_ref(git_repository *repo, git_annotated_commit *targ
 
 cleanup:
        git_commit_free(target_commit);
+       git_reference_free(branch);
+       git_reference_free(ref);
 
        return err;
 }
 
+/**
+ * This corresponds to `git switch --guess`: if a given ref does
+ * not exist, git will by default try to guess the reference by
+ * seeing whether any remote has a branch called <ref>. If there
+ * is a single remote only that has it, then it is assumed to be
+ * the desired reference and a local branch is created for it.
+ *
+ * The following is a simplified implementation. It will not try
+ * to check whether the ref is unique across all remotes.
+ */
+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)
+               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 = malloc(reflen + 1)) == NULL) {
+                       error = -1;
+                       goto next;
+               }
+               snprintf(refname, reflen + 1, "refs/remotes/%s/%s", remotes.strings[i], ref);
+
+               if ((error = git_reference_lookup(&remote_ref, repo, refname)) < 0)
+                       goto next;
+
+               break;
+next:
+               free(refname);
+               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;
+}
+
 /** That example's entry point */
-int main(int argc, char **argv)
+int lg2_checkout(git_repository *repo, int argc, char **argv)
 {
-       git_repository *repo = NULL;
        struct args_info args = ARGS_INFO_INIT;
        checkout_options opts;
        git_repository_state_t state;
@@ -185,15 +256,6 @@ int main(int argc, char **argv)
        /** 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) {
@@ -201,11 +263,7 @@ int main(int argc, char **argv)
                goto cleanup;
        }
 
-       if (args.pos >= args.argc) {
-               fprintf(stderr, "unhandled\n");
-               err = -1;
-               goto cleanup;
-       } else if (strcmp("--", args.argv[args.pos])) {
+       if (match_arg_separator(&args)) {
                /**
                 * Try to checkout the given path
                 */
@@ -217,19 +275,16 @@ int main(int argc, char **argv)
                /**
                 * 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) {
+               if ((err = resolve_refish(&checkout_target, repo, args.argv[args.pos])) < 0 &&
+                   (err = guess_refish(&checkout_target, repo, args.argv[args.pos])) < 0) {
                        fprintf(stderr, "failed to resolve %s: %s\n", args.argv[args.pos], git_error_last()->message);
                        goto cleanup;
                }
-               err = perform_checkout_ref(repo, checkout_target, &opts);
+               err = perform_checkout_ref(repo, checkout_target, args.argv[args.pos], &opts);
        }
 
 cleanup:
        git_annotated_commit_free(checkout_target);
 
-       git_repository_free(repo);
-       git_libgit2_shutdown();
-
        return err;
 }