*/
#include "common.h"
-#include <assert.h>
/* Define the printf format specifer to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
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;
* 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;
* 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;
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;
/** 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) {
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
*/
/**
* 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;
}