builtin / pull.con commit pull: pass git-merge's options to git-merge (11b6d17)
   1/*
   2 * Builtin "git pull"
   3 *
   4 * Based on git-pull.sh by Junio C Hamano
   5 *
   6 * Fetch one or more remote refs and merge it/them into the current HEAD.
   7 */
   8#include "cache.h"
   9#include "builtin.h"
  10#include "parse-options.h"
  11#include "exec_cmd.h"
  12#include "run-command.h"
  13
  14static const char * const pull_usage[] = {
  15        N_("git pull [options] [<repository> [<refspec>...]]"),
  16        NULL
  17};
  18
  19/* Shared options */
  20static int opt_verbosity;
  21static char *opt_progress;
  22
  23/* Options passed to git-merge */
  24static char *opt_diffstat;
  25static char *opt_log;
  26static char *opt_squash;
  27static char *opt_commit;
  28static char *opt_edit;
  29static char *opt_ff;
  30static char *opt_verify_signatures;
  31static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
  32static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
  33static char *opt_gpg_sign;
  34
  35static struct option pull_options[] = {
  36        /* Shared options */
  37        OPT__VERBOSITY(&opt_verbosity),
  38        OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
  39                N_("force progress reporting"),
  40                PARSE_OPT_NOARG),
  41
  42        /* Options passed to git-merge */
  43        OPT_GROUP(N_("Options related to merging")),
  44        OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
  45                N_("do not show a diffstat at the end of the merge"),
  46                PARSE_OPT_NOARG | PARSE_OPT_NONEG),
  47        OPT_PASSTHRU(0, "stat", &opt_diffstat, NULL,
  48                N_("show a diffstat at the end of the merge"),
  49                PARSE_OPT_NOARG),
  50        OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL,
  51                N_("(synonym to --stat)"),
  52                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN),
  53        OPT_PASSTHRU(0, "log", &opt_log, N_("n"),
  54                N_("add (at most <n>) entries from shortlog to merge commit message"),
  55                PARSE_OPT_OPTARG),
  56        OPT_PASSTHRU(0, "squash", &opt_squash, NULL,
  57                N_("create a single commit instead of doing a merge"),
  58                PARSE_OPT_NOARG),
  59        OPT_PASSTHRU(0, "commit", &opt_commit, NULL,
  60                N_("perform a commit if the merge succeeds (default)"),
  61                PARSE_OPT_NOARG),
  62        OPT_PASSTHRU(0, "edit", &opt_edit, NULL,
  63                N_("edit message before committing"),
  64                PARSE_OPT_NOARG),
  65        OPT_PASSTHRU(0, "ff", &opt_ff, NULL,
  66                N_("allow fast-forward"),
  67                PARSE_OPT_NOARG),
  68        OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
  69                N_("abort if fast-forward is not possible"),
  70                PARSE_OPT_NOARG | PARSE_OPT_NONEG),
  71        OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
  72                N_("verify that the named commit has a valid GPG signature"),
  73                PARSE_OPT_NOARG),
  74        OPT_PASSTHRU_ARGV('s', "strategy", &opt_strategies, N_("strategy"),
  75                N_("merge strategy to use"),
  76                0),
  77        OPT_PASSTHRU_ARGV('X', "strategy-option", &opt_strategy_opts,
  78                N_("option=value"),
  79                N_("option for selected merge strategy"),
  80                0),
  81        OPT_PASSTHRU('S', "gpg-sign", &opt_gpg_sign, N_("key-id"),
  82                N_("GPG sign commit"),
  83                PARSE_OPT_OPTARG),
  84
  85        OPT_END()
  86};
  87
  88/**
  89 * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
  90 */
  91static void argv_push_verbosity(struct argv_array *arr)
  92{
  93        int verbosity;
  94
  95        for (verbosity = opt_verbosity; verbosity > 0; verbosity--)
  96                argv_array_push(arr, "-v");
  97
  98        for (verbosity = opt_verbosity; verbosity < 0; verbosity++)
  99                argv_array_push(arr, "-q");
 100}
 101
 102/**
 103 * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`
 104 * as a string and `refspecs` as a null-terminated array of strings. If `repo`
 105 * is not provided in argv, it is set to NULL.
 106 */
 107static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
 108                const char ***refspecs)
 109{
 110        if (argc > 0) {
 111                *repo = *argv++;
 112                argc--;
 113        } else
 114                *repo = NULL;
 115        *refspecs = argv;
 116}
 117
 118/**
 119 * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the
 120 * repository and refspecs to fetch, or NULL if they are not provided.
 121 */
 122static int run_fetch(const char *repo, const char **refspecs)
 123{
 124        struct argv_array args = ARGV_ARRAY_INIT;
 125        int ret;
 126
 127        argv_array_pushl(&args, "fetch", "--update-head-ok", NULL);
 128
 129        /* Shared options */
 130        argv_push_verbosity(&args);
 131        if (opt_progress)
 132                argv_array_push(&args, opt_progress);
 133
 134        if (repo) {
 135                argv_array_push(&args, repo);
 136                argv_array_pushv(&args, refspecs);
 137        } else if (*refspecs)
 138                die("BUG: refspecs without repo?");
 139        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
 140        argv_array_clear(&args);
 141        return ret;
 142}
 143
 144/**
 145 * Runs git-merge, returning its exit status.
 146 */
 147static int run_merge(void)
 148{
 149        int ret;
 150        struct argv_array args = ARGV_ARRAY_INIT;
 151
 152        argv_array_pushl(&args, "merge", NULL);
 153
 154        /* Shared options */
 155        argv_push_verbosity(&args);
 156        if (opt_progress)
 157                argv_array_push(&args, opt_progress);
 158
 159        /* Options passed to git-merge */
 160        if (opt_diffstat)
 161                argv_array_push(&args, opt_diffstat);
 162        if (opt_log)
 163                argv_array_push(&args, opt_log);
 164        if (opt_squash)
 165                argv_array_push(&args, opt_squash);
 166        if (opt_commit)
 167                argv_array_push(&args, opt_commit);
 168        if (opt_edit)
 169                argv_array_push(&args, opt_edit);
 170        if (opt_ff)
 171                argv_array_push(&args, opt_ff);
 172        if (opt_verify_signatures)
 173                argv_array_push(&args, opt_verify_signatures);
 174        argv_array_pushv(&args, opt_strategies.argv);
 175        argv_array_pushv(&args, opt_strategy_opts.argv);
 176        if (opt_gpg_sign)
 177                argv_array_push(&args, opt_gpg_sign);
 178
 179        argv_array_push(&args, "FETCH_HEAD");
 180        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
 181        argv_array_clear(&args);
 182        return ret;
 183}
 184
 185int cmd_pull(int argc, const char **argv, const char *prefix)
 186{
 187        const char *repo, **refspecs;
 188
 189        if (!getenv("_GIT_USE_BUILTIN_PULL")) {
 190                const char *path = mkpath("%s/git-pull", git_exec_path());
 191
 192                if (sane_execvp(path, (char **)argv) < 0)
 193                        die_errno("could not exec %s", path);
 194        }
 195
 196        argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 197
 198        parse_repo_refspecs(argc, argv, &repo, &refspecs);
 199
 200        if (run_fetch(repo, refspecs))
 201                return 1;
 202
 203        return run_merge();
 204}