]>
Commit | Line | Data |
---|---|---|
1e1ea69f PT |
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" | |
f2c5baa1 | 12 | #include "run-command.h" |
1e1ea69f PT |
13 | |
14 | static const char * const pull_usage[] = { | |
f2c5baa1 | 15 | N_("git pull [options] [<repository> [<refspec>...]]"), |
1e1ea69f PT |
16 | NULL |
17 | }; | |
18 | ||
2a747902 PT |
19 | /* Shared options */ |
20 | static int opt_verbosity; | |
21 | static char *opt_progress; | |
22 | ||
11b6d178 PT |
23 | /* Options passed to git-merge */ |
24 | static char *opt_diffstat; | |
25 | static char *opt_log; | |
26 | static char *opt_squash; | |
27 | static char *opt_commit; | |
28 | static char *opt_edit; | |
29 | static char *opt_ff; | |
30 | static char *opt_verify_signatures; | |
31 | static struct argv_array opt_strategies = ARGV_ARRAY_INIT; | |
32 | static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT; | |
33 | static char *opt_gpg_sign; | |
34 | ||
1e1ea69f | 35 | static struct option pull_options[] = { |
2a747902 PT |
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 | ||
11b6d178 PT |
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 | ||
1e1ea69f PT |
85 | OPT_END() |
86 | }; | |
87 | ||
2a747902 PT |
88 | /** |
89 | * Pushes "-q" or "-v" switches into arr to match the opt_verbosity level. | |
90 | */ | |
91 | static 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 | ||
f2c5baa1 PT |
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 | */ | |
107 | static 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 | */ | |
122 | static 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); | |
2a747902 PT |
128 | |
129 | /* Shared options */ | |
130 | argv_push_verbosity(&args); | |
131 | if (opt_progress) | |
132 | argv_array_push(&args, opt_progress); | |
133 | ||
f2c5baa1 PT |
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 | */ | |
147 | static int run_merge(void) | |
148 | { | |
149 | int ret; | |
150 | struct argv_array args = ARGV_ARRAY_INIT; | |
151 | ||
152 | argv_array_pushl(&args, "merge", NULL); | |
2a747902 PT |
153 | |
154 | /* Shared options */ | |
155 | argv_push_verbosity(&args); | |
156 | if (opt_progress) | |
157 | argv_array_push(&args, opt_progress); | |
158 | ||
11b6d178 PT |
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 | ||
f2c5baa1 PT |
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 | ||
1e1ea69f PT |
185 | int cmd_pull(int argc, const char **argv, const char *prefix) |
186 | { | |
f2c5baa1 PT |
187 | const char *repo, **refspecs; |
188 | ||
1e1ea69f PT |
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 | ||
f2c5baa1 PT |
198 | parse_repo_refspecs(argc, argv, &repo, &refspecs); |
199 | ||
200 | if (run_fetch(repo, refspecs)) | |
201 | return 1; | |
202 | ||
203 | return run_merge(); | |
1e1ea69f | 204 | } |