This allows us to checkout a PR a'la:
$ gcli pulls -i 42 checkout
Note that on GitLab, if a MR has been merged you cannot check it
out anymore.
Signed-off-by: Nico Sonack <nsonack@herrhotzenplotz.de>
---
Changelog.md | 3 ++
Makefile.in | 3 ++
docs/gcli-pulls.1.in | 8 ++++
include/gcli/cmd/cmdconfig.h | 3 +-
include/gcli/cmd/gitconfig.h | 7 ++-
include/gcli/forges.h | 8 +++-
include/gcli/github/checkout.h | 35 ++++++++++++++
include/gcli/gitlab/checkout.h | 35 ++++++++++++++
include/gcli/pulls.h | 4 +-
include/gcli/waitproc.h | 35 ++++++++++++++
src/cmd/cmdconfig.c | 22 ++++++++-
src/cmd/gitconfig.c | 18 ++++++-
src/cmd/pulls.c | 22 ++++++++-
src/forges.c | 6 ++-
src/github/checkout.c | 86 +++++++++++++++++++++++++++++++++
src/gitlab/checkout.c | 87 ++++++++++++++++++++++++++++++++++
src/pulls.c | 9 +++-
src/waitproc.c | 61 ++++++++++++++++++++++++
18 files changed, 442 insertions(+), 10 deletions(-)
create mode 100644 include/gcli/github/checkout.h
create mode 100644 include/gcli/gitlab/checkout.h
create mode 100644 include/gcli/waitproc.h
create mode 100644 src/github/checkout.c
create mode 100644 src/gitlab/checkout.c
create mode 100644 src/waitproc.c
diff --git a/Changelog.md b/Changelog.md
index e91c46f..f50db50 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -6,6 +6,9 @@ This changelog does not follow semantic versioning.
### Added
+- Added a `checkout` action to the `pulls` subcommand that allows
+ quickly checking out the target branch of a pull request.
+
### Fixed
### Changed
diff --git a/Makefile.in b/Makefile.in
index 9647e46..e8857b8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -131,6 +131,7 @@ LIBGCLI_SRCS = \
src/gitea/status.c \
src/github/api.c \
src/github/checks.c \
+ src/github/checkout.c \
src/github/comments.c \
src/github/config.c \
src/github/forks.c \
@@ -144,6 +145,7 @@ LIBGCLI_SRCS = \
src/github/sshkeys.c \
src/github/status.c \
src/gitlab/api.c \
+ src/gitlab/checkout.c \
src/gitlab/comments.c \
src/gitlab/config.c \
src/gitlab/forks.c \
@@ -178,6 +180,7 @@ LIBGCLI_SRCS = \
src/repos.c \
src/sshkeys.c \
src/status.c \
+ src/waitproc.c \
thirdparty/pdjson/pdjson.c \
thirdparty/sn/sn.c
diff --git a/docs/gcli-pulls.1.in b/docs/gcli-pulls.1.in
index 8517718..0a598a0 100644
--- a/docs/gcli-pulls.1.in
+++ b/docs/gcli-pulls.1.in
@@ -139,6 +139,14 @@ implied:
.Cm op ,
.Cm commits and
.Cm ci .
+.It Cm checkout
+Do a git checkout of the head branch associated with this pull request.
+This requires that
+.Xr git 1
+is available in the
+.Ev PATH
+and that the current working directory resides within a clone of the
+target repository.
.It Cm commits
Print the list of commits associated with the Pull Requests.
.It Cm comments
diff --git a/include/gcli/cmd/cmdconfig.h b/include/gcli/cmd/cmdconfig.h
index a1df785..3356055 100644
--- a/include/gcli/cmd/cmdconfig.h
+++ b/include/gcli/cmd/cmdconfig.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -66,6 +66,7 @@ gcli_forge_type gcli_config_get_forge_type(struct gcli_ctx *ctx);
sn_sv gcli_config_get_override_default_account(struct gcli_ctx *ctx);
bool gcli_config_pr_inhibit_delete_source_branch(struct gcli_ctx *ctx);
int gcli_config_get_repo(struct gcli_ctx *ctx, char const **, char const **);
+int gcli_config_get_remote(struct gcli_ctx *ctx, char **remote);
int gcli_config_have_colours(struct gcli_ctx *ctx);
int gcli_config_display_progress_spinner(struct gcli_ctx *ctx);
bool gcli_config_enable_experimental(struct gcli_ctx *ctx);
diff --git a/include/gcli/cmd/gitconfig.h b/include/gcli/cmd/gitconfig.h
index 0a4edeb..7b4f388 100644
--- a/include/gcli/cmd/gitconfig.h
+++ b/include/gcli/cmd/gitconfig.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -41,7 +41,7 @@ struct gcli_gitremote {
sn_sv owner;
sn_sv repo;
sn_sv url;
- int forge_type;
+ gcli_forge_type forge_type;
};
sn_sv gcli_gitconfig_get_current_branch(void);
@@ -54,4 +54,7 @@ int gcli_gitconfig_repo_by_remote(struct gcli_ctx *ctx, char const *const remote
char const **const owner, char const **const repo,
int *const forge);
+int gcli_gitconfig_get_remote(struct gcli_ctx *ctx, gcli_forge_type type,
+ char **remote);
+
#endif /* GCLI_CMD_GITCONFIG_H */
diff --git a/include/gcli/forges.h b/include/gcli/forges.h
index 420f562..3a8b6be 100644
--- a/include/gcli/forges.h
+++ b/include/gcli/forges.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -444,6 +444,12 @@ struct gcli_forge_descriptor {
struct gcli_ctx *ctx,
struct gcli_pull_create_review_details const *details);
+ /** Checkout this PR */
+ int (*pull_checkout)(
+ struct gcli_ctx *ctx,
+ char const *remote,
+ gcli_id pull);
+
/**
* Get a list of releases in the given repo */
int (*get_releases)(
diff --git a/include/gcli/github/checkout.h b/include/gcli/github/checkout.h
new file mode 100644
index 0000000..69dc985
--- /dev/null
+++ b/include/gcli/github/checkout.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Nico Sonack <nsonack@herrhotzenplotz.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GITHUB_CHECKOUT_H
+#define GITHUB_CHECKOUT_H
+
+int github_pull_checkout(struct gcli_ctx *ctx, char const *remote, gcli_id pull);
+
+#endif /* GITHUB_CHECKOUT_H */
diff --git a/include/gcli/gitlab/checkout.h b/include/gcli/gitlab/checkout.h
new file mode 100644
index 0000000..ddaec4c
--- /dev/null
+++ b/include/gcli/gitlab/checkout.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Nico Sonack <nsonack@herrhotzenplotz.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GITLAB_CHECKOUT_H
+#define GITLAB_CHECKOUT_H
+
+int gitlab_mr_checkout(struct gcli_ctx *ctx, char const *remote, gcli_id pull);
+
+#endif /* GITLAB_CHECKOUT_H */
diff --git a/include/gcli/pulls.h b/include/gcli/pulls.h
index 9b01b4c..fa40944 100644
--- a/include/gcli/pulls.h
+++ b/include/gcli/pulls.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -224,4 +224,6 @@ int gcli_pull_get_patch(struct gcli_ctx *ctx, FILE *out, char const *owner,
char const *gcli_pull_get_meta_by_key(struct gcli_pull_create_review_details const *,
char const *key);
+int gcli_pull_checkout(struct gcli_ctx *ctx, char const *remote, gcli_id pull);
+
#endif /* PULLS_H */
diff --git a/include/gcli/waitproc.h b/include/gcli/waitproc.h
new file mode 100644
index 0000000..979f631
--- /dev/null
+++ b/include/gcli/waitproc.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Nico Sonack <nsonack@herrhotzenplotz.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef GCLI_WAITPROC_H
+#define GCLI_WAITPROC_H
+
+int gcli_wait_proc_ok(struct gcli_ctx *ctx, pid_t pid);
+
+#endif /* GCLI_WAITPROC_H */
diff --git a/src/cmd/cmdconfig.c b/src/cmd/cmdconfig.c
index 12cb2a5..b1b0cf4 100644
--- a/src/cmd/cmdconfig.c
+++ b/src/cmd/cmdconfig.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -930,6 +930,26 @@ gcli_config_get_forge_type(struct gcli_ctx *ctx)
return result;
}
+int
+gcli_config_get_remote(struct gcli_ctx *ctx, char **remote)
+{
+ struct gcli_config *cfg;
+ gcli_forge_type type;
+ int rc;
+
+ cfg = ensure_config(ctx);
+
+ if (cfg->override_remote) {
+ *remote = strdup(cfg->override_remote);
+ return 0;
+ }
+
+ type = gcli_config_get_forge_type(ctx);
+ rc = gcli_gitconfig_get_remote(ctx, type, remote);
+
+ return rc;
+}
+
int
gcli_config_get_repo(struct gcli_ctx *ctx, char const **const owner,
char const **const repo)
diff --git a/src/cmd/gitconfig.c b/src/cmd/gitconfig.c
index 6066221..a24eb98 100644
--- a/src/cmd/gitconfig.c
+++ b/src/cmd/gitconfig.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -562,3 +562,19 @@ gcli_gitconfig_repo_by_remote(struct gcli_ctx *ctx, char const *const remote,
return 0;
}
+
+int
+gcli_gitconfig_get_remote(struct gcli_ctx *ctx, gcli_forge_type const type,
+ char **remote)
+{
+ gcli_gitconfig_read_gitconfig();
+
+ for (size_t i = 0; i < remotes_size; ++i) {
+ if (remotes[i].forge_type == type) {
+ *remote = sn_sv_to_cstr(remotes[i].url);
+ return 0;
+ }
+ }
+
+ return gcli_error(ctx, "no suitable remote for forge type");
+}
diff --git a/src/cmd/pulls.c b/src/cmd/pulls.c
index a3ff300..1bc4104 100644
--- a/src/cmd/pulls.c
+++ b/src/cmd/pulls.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2022-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -98,6 +98,7 @@ usage(void)
fprintf(stderr, " patch Display changes as patch series\n");
fprintf(stderr, " title <new-title> Change the title of the pull request\n");
fprintf(stderr, " request-review <user> Add <user> as a reviewer of the PR\n");
+ fprintf(stderr, " checkout Do a git-checkout of this PR (GitHub- and GitLab only)\n");
if (gcli_config_enable_experimental(g_clictx))
fprintf(stderr, " review Start a review of this PR\n");
@@ -1091,6 +1092,24 @@ action_review(struct action_ctx *ctx)
do_review_session(ctx->owner, ctx->repo, ctx->pr);
}
+static void
+action_checkout(struct action_ctx *ctx)
+{
+ char *remote;
+ int rc = 0;
+
+ rc = gcli_config_get_remote(g_clictx, &remote);
+ if (rc < 0)
+ errx(1, "gcli: error: %s", gcli_get_error(g_clictx));
+
+ if (gcli_pull_checkout(g_clictx, remote, ctx->pr) < 0) {
+ errx(1, "gcli: error: failed to checkout pull: %s",
+ gcli_get_error(g_clictx));
+ }
+
+ free(remote);
+}
+
static struct action {
char const *name;
void (*fn)(struct action_ctx *ctx);
@@ -1112,6 +1131,7 @@ static struct action {
{ .name = "request-review", .fn = action_request_review },
{ .name = "title", .fn = action_title },
{ .name = "review", .fn = action_review },
+ { .name = "checkout", .fn = action_checkout },
};
static size_t const actions_size = ARRAY_SIZE(actions);
diff --git a/src/forges.c b/src/forges.c
index 31ffd11..b9573d6 100644
--- a/src/forges.c
+++ b/src/forges.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2021, 2022, 2023 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,6 +32,7 @@
#include <gcli/forges.h>
#include <gcli/github/api.h>
+#include <gcli/github/checkout.h>
#include <gcli/github/comments.h>
#include <gcli/github/config.h>
#include <gcli/github/forks.h>
@@ -45,6 +46,7 @@
#include <gcli/github/status.h>
#include <gcli/gitlab/api.h>
+#include <gcli/gitlab/checkout.h>
#include <gcli/gitlab/comments.h>
#include <gcli/gitlab/config.h>
#include <gcli/gitlab/forks.h>
@@ -125,6 +127,7 @@ github_forge_descriptor =
.pull_merge = github_pull_merge,
.pull_reopen = github_pull_reopen,
.pull_set_title = github_pull_set_title,
+ .pull_checkout = github_pull_checkout,
/* HACK: Here we can use the same functions as with issues because
* PRs are the same as issues on Github and the functions have the
@@ -230,6 +233,7 @@ gitlab_forge_descriptor =
.pull_reopen = gitlab_mr_reopen,
.pull_set_milestone = gitlab_mr_set_milestone,
.pull_set_title = gitlab_mr_set_title,
+ .pull_checkout = gitlab_mr_checkout,
/* Releases */
.create_release = gitlab_create_release,
diff --git a/src/github/checkout.c b/src/github/checkout.c
new file mode 100644
index 0000000..5f6e4cb
--- /dev/null
+++ b/src/github/checkout.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 Nico Sonack <nsonack@herrhotzenplotz.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gcli/gcli.h>
+#include <gcli/waitproc.h>
+
+#include <sn/sn.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+github_pull_checkout(struct gcli_ctx *ctx, char const *const remote, gcli_id const pr_id)
+{
+ /* FIXME: this is more than not ideal! */
+ char *remote_ref, *local_ref, *refspec;
+ int rc;
+ pid_t pid;
+
+ remote_ref = sn_asprintf("refs/pull/%"PRIid"/head", pr_id);
+ local_ref = sn_asprintf("github/pr/%"PRIid, pr_id);
+ refspec = sn_asprintf("%s:%s", remote_ref, local_ref);
+
+ pid = fork();
+ if (pid < 0)
+ return gcli_error(ctx, "could not fork");
+
+ if (pid == 0) {
+ rc = execlp("git", "git", "fetch", remote, refspec, NULL);
+ if (rc < 0)
+ exit(EXIT_FAILURE);
+
+ /* NOTREACHED */
+ }
+
+ rc = gcli_wait_proc_ok(ctx, pid);
+ if (rc < 0)
+ return rc;
+
+ free(remote_ref); remote_ref = NULL;
+ free(refspec); refspec = NULL;
+
+ pid = fork();
+ if (pid < 0)
+ return gcli_error(ctx, "could not fork");
+
+ if (pid == 0) {
+ rc = execlp("git", "git", "checkout", "--track", local_ref, NULL);
+ if (rc < 0)
+ exit(EXIT_FAILURE);
+
+ /* NOTREACHED */
+ }
+
+ rc = gcli_wait_proc_ok(ctx, pid);
+
+ free(local_ref); local_ref = NULL;
+
+ return rc;
+}
diff --git a/src/gitlab/checkout.c b/src/gitlab/checkout.c
new file mode 100644
index 0000000..41d5b2f
--- /dev/null
+++ b/src/gitlab/checkout.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 Nico Sonack <nsonack@herrhotzenplotz.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gcli/gcli.h>
+#include <gcli/gitlab/checkout.h>
+#include <gcli/waitproc.h>
+
+#include <sn/sn.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+gitlab_mr_checkout(struct gcli_ctx *ctx, char const *const remote, gcli_id const pr_id)
+{
+ /* FIXME: this is more than not ideal! */
+ char *remote_ref, *local_ref, *refspec;
+ int rc;
+ pid_t pid;
+
+ remote_ref = sn_asprintf("merge-requests/%"PRIid"/head", pr_id);
+ local_ref = sn_asprintf("gitlab/mr/%"PRIid, pr_id);
+ refspec = sn_asprintf("%s:%s", remote_ref, local_ref);
+
+ pid = fork();
+ if (pid < 0)
+ return gcli_error(ctx, "could not fork");
+
+ if (pid == 0) {
+ rc = execlp("git", "git", "fetch", remote, refspec, NULL);
+ if (rc < 0)
+ exit(EXIT_FAILURE);
+
+ /* NOTREACHED */
+ }
+
+ rc = gcli_wait_proc_ok(ctx, pid);
+ if (rc < 0)
+ return rc;
+
+ free(remote_ref); remote_ref = NULL;
+ free(refspec); refspec = NULL;
+
+ pid = fork();
+ if (pid < 0)
+ return gcli_error(ctx, "could not fork");
+
+ if (pid == 0) {
+ rc = execlp("git", "git", "checkout", "--track", local_ref, NULL);
+ if (rc < 0)
+ exit(EXIT_FAILURE);
+
+ /* NOTREACHED */
+ }
+
+ rc = gcli_wait_proc_ok(ctx, pid);
+
+ free(local_ref); local_ref = NULL;
+
+ return rc;
+}
diff --git a/src/pulls.c b/src/pulls.c
index f792d93..84b1fd1 100644
--- a/src/pulls.c
+++ b/src/pulls.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2021,2022 Nico Sonack <nsonack@herrhotzenplotz.de>
+ * Copyright 2021-2024 Nico Sonack <nsonack@herrhotzenplotz.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -255,3 +255,10 @@ gcli_pull_get_meta_by_key(struct gcli_pull_create_review_details const *details,
return NULL;
}
+
+int
+gcli_pull_checkout(struct gcli_ctx *ctx, char const *const remote,
+ gcli_id const pull)
+{
+ gcli_null_check_call(pull_checkout, ctx, remote, pull);
+}
diff --git a/src/waitproc.c b/src/waitproc.c
new file mode 100644
index 0000000..aa4b962
--- /dev/null
+++ b/src/waitproc.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 Nico Sonack <nsonack@herrhotzenplotz.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <gcli/gcli.h>
+
+#include <errno.h>
+#include <string.h>
+#include <sys/wait.h>
+
+int
+gcli_wait_proc_ok(struct gcli_ctx *ctx, pid_t pid)
+{
+ int status;
+
+ if (waitpid(pid, &status, WEXITED) == -1) {
+ return gcli_error(ctx, "failed to wait for child process: %s",
+ strerror(errno));
+ }
+
+ if (WIFEXITED(status)) {
+ int exit_code = WEXITSTATUS(status);
+ if (exit_code) {
+ return gcli_error(ctx, "child exited with error code %d",
+ WEXITSTATUS(status));
+ }
+ return 0;
+ }
+
+ if (WIFSIGNALED(status)) {
+ return gcli_error(ctx, "child exited due to signal %d",
+ WTERMSIG(status));
+ }
+
+ return gcli_error(ctx, "unknown child status");
+}
--
2.45.2