5d9f2b529e018b517bc001e20057fb1af6aa9e2a
   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
  23static struct option pull_options[] = {
  24        /* Shared options */
  25        OPT__VERBOSITY(&opt_verbosity),
  26        OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
  27                N_("force progress reporting"),
  28                PARSE_OPT_NOARG),
  29
  30        OPT_END()
  31};
  32
  33/**
  34 * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level.
  35 */
  36static void argv_push_verbosity(struct argv_array *arr)
  37{
  38        int verbosity;
  39
  40        for (verbosity = opt_verbosity; verbosity > 0; verbosity--)
  41                argv_array_push(arr, "-v");
  42
  43        for (verbosity = opt_verbosity; verbosity < 0; verbosity++)
  44                argv_array_push(arr, "-q");
  45}
  46
  47/**
  48 * Parses argv into [<repo> [<refspecs>...]], returning their values in `repo`
  49 * as a string and `refspecs` as a null-terminated array of strings. If `repo`
  50 * is not provided in argv, it is set to NULL.
  51 */
  52static void parse_repo_refspecs(int argc, const char **argv, const char **repo,
  53                const char ***refspecs)
  54{
  55        if (argc > 0) {
  56                *repo = *argv++;
  57                argc--;
  58        } else
  59                *repo = NULL;
  60        *refspecs = argv;
  61}
  62
  63/**
  64 * Runs git-fetch, returning its exit status. `repo` and `refspecs` are the
  65 * repository and refspecs to fetch, or NULL if they are not provided.
  66 */
  67static int run_fetch(const char *repo, const char **refspecs)
  68{
  69        struct argv_array args = ARGV_ARRAY_INIT;
  70        int ret;
  71
  72        argv_array_pushl(&args, "fetch", "--update-head-ok", NULL);
  73
  74        /* Shared options */
  75        argv_push_verbosity(&args);
  76        if (opt_progress)
  77                argv_array_push(&args, opt_progress);
  78
  79        if (repo) {
  80                argv_array_push(&args, repo);
  81                argv_array_pushv(&args, refspecs);
  82        } else if (*refspecs)
  83                die("BUG: refspecs without repo?");
  84        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
  85        argv_array_clear(&args);
  86        return ret;
  87}
  88
  89/**
  90 * Runs git-merge, returning its exit status.
  91 */
  92static int run_merge(void)
  93{
  94        int ret;
  95        struct argv_array args = ARGV_ARRAY_INIT;
  96
  97        argv_array_pushl(&args, "merge", NULL);
  98
  99        /* Shared options */
 100        argv_push_verbosity(&args);
 101        if (opt_progress)
 102                argv_array_push(&args, opt_progress);
 103
 104        argv_array_push(&args, "FETCH_HEAD");
 105        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
 106        argv_array_clear(&args);
 107        return ret;
 108}
 109
 110int cmd_pull(int argc, const char **argv, const char *prefix)
 111{
 112        const char *repo, **refspecs;
 113
 114        if (!getenv("_GIT_USE_BUILTIN_PULL")) {
 115                const char *path = mkpath("%s/git-pull", git_exec_path());
 116
 117                if (sane_execvp(path, (char **)argv) < 0)
 118                        die_errno("could not exec %s", path);
 119        }
 120
 121        argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0);
 122
 123        parse_repo_refspecs(argc, argv, &repo, &refspecs);
 124
 125        if (run_fetch(repo, refspecs))
 126                return 1;
 127
 128        return run_merge();
 129}