Nico Sonack: 1 pulls: add a checkout action with implementations for GitHub and GitLab 18 files changed, 442 insertions(+), 10 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~herrhotzenplotz/gcli-devel/patches/54953/mbox | git am -3Learn more about email & git
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