]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/replace.c
cocci: apply the "cache.h" part of "the_repository.pending"
[thirdparty/git.git] / builtin / replace.c
CommitLineData
54b0c1e0
CC
1/*
2 * Builtin "git replace"
3 *
4 * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
5 *
09b7e220 6 * Based on builtin/tag.c by Kristian Høgsberg <krh@redhat.com>
54b0c1e0
CC
7 * and Carlos Rica <jasampler@gmail.com> that was itself based on
8 * git-tag.sh and mktag.c by Linus Torvalds.
9 */
10
11#include "cache.h"
b2141fc1 12#include "config.h"
54b0c1e0
CC
13#include "builtin.h"
14#include "refs.h"
15#include "parse-options.h"
b892bb45 16#include "run-command.h"
60ce76d3
SB
17#include "object-store.h"
18#include "repository.h"
25a05a8c 19#include "tag.h"
54b0c1e0
CC
20
21static const char * const git_replace_usage[] = {
2477bebb 22 N_("git replace [-f] <object> <replacement>"),
ab77c309 23 N_("git replace [-f] --edit <object>"),
4228e8bc 24 N_("git replace [-f] --graft <commit> [<parent>...]"),
959d670d 25 "git replace [-f] --convert-graft-file",
2477bebb 26 N_("git replace -d <object>..."),
44f9f850 27 N_("git replace [--format=<format>] [-l [<pattern>]]"),
54b0c1e0
CC
28 NULL
29};
30
663a8566 31enum replace_format {
3cc9d877
JK
32 REPLACE_FORMAT_SHORT,
33 REPLACE_FORMAT_MEDIUM,
34 REPLACE_FORMAT_LONG
663a8566 35};
44f9f850
CC
36
37struct show_data {
38 const char *pattern;
663a8566 39 enum replace_format format;
44f9f850
CC
40};
41
212e0f7e
SB
42static int show_reference(struct repository *r, const char *refname,
43 const struct object_id *oid,
54b0c1e0
CC
44 int flag, void *cb_data)
45{
44f9f850
CC
46 struct show_data *data = cb_data;
47
55d34269 48 if (!wildmatch(data->pattern, refname, 0)) {
663a8566 49 if (data->format == REPLACE_FORMAT_SHORT)
44f9f850 50 printf("%s\n", refname);
663a8566 51 else if (data->format == REPLACE_FORMAT_MEDIUM)
d70d7a8a 52 printf("%s -> %s\n", refname, oid_to_hex(oid));
663a8566 53 else { /* data->format == REPLACE_FORMAT_LONG */
d70d7a8a 54 struct object_id object;
44f9f850 55 enum object_type obj_type, repl_type;
54b0c1e0 56
d850b7a5 57 if (repo_get_oid(the_repository, refname, &object))
225c62e0 58 return error(_("failed to resolve '%s' as a valid ref"), refname);
44f9f850 59
212e0f7e
SB
60 obj_type = oid_object_info(r, &object, NULL);
61 repl_type = oid_object_info(r, oid, NULL);
44f9f850 62
debca9d2
BW
63 printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
64 oid_to_hex(oid), type_name(repl_type));
44f9f850
CC
65 }
66 }
54b0c1e0
CC
67
68 return 0;
69}
70
44f9f850 71static int list_replace_refs(const char *pattern, const char *format)
54b0c1e0 72{
44f9f850
CC
73 struct show_data data;
74
afe8a907 75 if (!pattern)
54b0c1e0 76 pattern = "*";
44f9f850
CC
77 data.pattern = pattern;
78
79 if (format == NULL || *format == '\0' || !strcmp(format, "short"))
663a8566 80 data.format = REPLACE_FORMAT_SHORT;
44f9f850 81 else if (!strcmp(format, "medium"))
663a8566
CC
82 data.format = REPLACE_FORMAT_MEDIUM;
83 else if (!strcmp(format, "long"))
84 data.format = REPLACE_FORMAT_LONG;
5a59a230
NTND
85 /*
86 * Please update _git_replace() in git-completion.bash when
87 * you add new format
88 */
44f9f850 89 else
225c62e0
NTND
90 return error(_("invalid replace format '%s'\n"
91 "valid formats are 'short', 'medium' and 'long'"),
e24e8719 92 format);
54b0c1e0 93
60ce76d3 94 for_each_replace_ref(the_repository, show_reference, (void *)&data);
54b0c1e0
CC
95
96 return 0;
97}
98
99typedef int (*each_replace_name_fn)(const char *name, const char *ref,
cea4332e 100 const struct object_id *oid);
54b0c1e0
CC
101
102static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
103{
9dfc3684 104 const char **p, *full_hex;
7f897b6f
JK
105 struct strbuf ref = STRBUF_INIT;
106 size_t base_len;
54b0c1e0 107 int had_error = 0;
cea4332e 108 struct object_id oid;
97e61e0f 109 const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
54b0c1e0 110
7f897b6f
JK
111 strbuf_addstr(&ref, git_replace_ref_base);
112 base_len = ref.len;
113
54b0c1e0 114 for (p = argv; *p; p++) {
d850b7a5 115 if (repo_get_oid(the_repository, *p, &oid)) {
1a07e59c 116 error("failed to resolve '%s' as a valid ref", *p);
54b0c1e0
CC
117 had_error = 1;
118 continue;
119 }
7f897b6f
JK
120
121 strbuf_setlen(&ref, base_len);
122 strbuf_addstr(&ref, oid_to_hex(&oid));
123 full_hex = ref.buf + base_len;
124
34c290a6 125 if (read_ref(ref.buf, &oid)) {
225c62e0 126 error(_("replace ref '%s' not found"), full_hex);
54b0c1e0
CC
127 had_error = 1;
128 continue;
129 }
7f897b6f 130 if (fn(full_hex, ref.buf, &oid))
54b0c1e0
CC
131 had_error = 1;
132 }
372b050b 133 strbuf_release(&ref);
54b0c1e0
CC
134 return had_error;
135}
136
137static int delete_replace_ref(const char *name, const char *ref,
cea4332e 138 const struct object_id *oid)
54b0c1e0 139{
2616a5e5 140 if (delete_ref(NULL, ref, oid, 0))
54b0c1e0 141 return 1;
225c62e0 142 printf_ln(_("Deleted replace ref '%s'"), name);
54b0c1e0
CC
143 return 0;
144}
145
e24e8719 146static int check_ref_valid(struct object_id *object,
cea4332e 147 struct object_id *prev,
7f897b6f 148 struct strbuf *ref,
b6e38840
CC
149 int force)
150{
97e61e0f
DS
151 const char *git_replace_ref_base = ref_namespace[NAMESPACE_REPLACE].ref;
152
7f897b6f
JK
153 strbuf_reset(ref);
154 strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object));
155 if (check_refname_format(ref->buf, 0))
225c62e0 156 return error(_("'%s' is not a valid ref name"), ref->buf);
7f897b6f 157
34c290a6 158 if (read_ref(ref->buf, prev))
cea4332e 159 oidclr(prev);
b6e38840 160 else if (!force)
225c62e0 161 return error(_("replace ref '%s' already exists"), ref->buf);
e24e8719 162 return 0;
b6e38840
CC
163}
164
cea4332e 165static int replace_object_oid(const char *object_ref,
166 struct object_id *object,
479bd757 167 const char *replace_ref,
cea4332e 168 struct object_id *repl,
479bd757 169 int force)
bebdd271 170{
cea4332e 171 struct object_id prev;
277336a5 172 enum object_type obj_type, repl_type;
7f897b6f 173 struct strbuf ref = STRBUF_INIT;
867c2fac
RS
174 struct ref_transaction *transaction;
175 struct strbuf err = STRBUF_INIT;
e24e8719 176 int res = 0;
bebdd271 177
0df8e965
SB
178 obj_type = oid_object_info(the_repository, object, NULL);
179 repl_type = oid_object_info(the_repository, repl, NULL);
277336a5 180 if (!force && obj_type != repl_type)
225c62e0
NTND
181 return error(_("Objects must be of the same type.\n"
182 "'%s' points to a replaced object of type '%s'\n"
183 "while '%s' points to a replacement object of "
184 "type '%s'."),
e24e8719
JS
185 object_ref, type_name(obj_type),
186 replace_ref, type_name(repl_type));
187
188 if (check_ref_valid(object, &prev, &ref, force)) {
189 strbuf_release(&ref);
190 return -1;
191 }
bebdd271 192
867c2fac
RS
193 transaction = ref_transaction_begin(&err);
194 if (!transaction ||
89f3bbdd 195 ref_transaction_update(transaction, ref.buf, repl, &prev,
1d147bdf 196 0, NULL, &err) ||
db7516ab 197 ref_transaction_commit(transaction, &err))
e24e8719 198 res = error("%s", err.buf);
bebdd271 199
867c2fac 200 ref_transaction_free(transaction);
7f897b6f 201 strbuf_release(&ref);
e24e8719 202 return res;
bebdd271
CC
203}
204
479bd757
JK
205static int replace_object(const char *object_ref, const char *replace_ref, int force)
206{
cea4332e 207 struct object_id object, repl;
479bd757 208
d850b7a5 209 if (repo_get_oid(the_repository, object_ref, &object))
225c62e0 210 return error(_("failed to resolve '%s' as a valid ref"),
e24e8719 211 object_ref);
d850b7a5 212 if (repo_get_oid(the_repository, replace_ref, &repl))
225c62e0 213 return error(_("failed to resolve '%s' as a valid ref"),
e24e8719 214 replace_ref);
479bd757 215
cea4332e 216 return replace_object_oid(object_ref, &object, replace_ref, &repl, force);
479bd757
JK
217}
218
b892bb45 219/*
2deda629
JK
220 * Write the contents of the object named by "sha1" to the file "filename".
221 * If "raw" is true, then the object's raw contents are printed according to
222 * "type". Otherwise, we pretty-print the contents for human editing.
b892bb45 223 */
e24e8719 224static int export_object(const struct object_id *oid, enum object_type type,
2deda629 225 int raw, const char *filename)
b892bb45 226{
d3180279 227 struct child_process cmd = CHILD_PROCESS_INIT;
b892bb45
JK
228 int fd;
229
230 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
231 if (fd < 0)
225c62e0 232 return error_errno(_("unable to open %s for writing"), filename);
b892bb45 233
22f9b7f3
JK
234 strvec_push(&cmd.args, "--no-replace-objects");
235 strvec_push(&cmd.args, "cat-file");
2deda629 236 if (raw)
22f9b7f3 237 strvec_push(&cmd.args, type_name(type));
2deda629 238 else
22f9b7f3
JK
239 strvec_push(&cmd.args, "-p");
240 strvec_push(&cmd.args, oid_to_hex(oid));
b892bb45
JK
241 cmd.git_cmd = 1;
242 cmd.out = fd;
243
244 if (run_command(&cmd))
225c62e0 245 return error(_("cat-file reported failure"));
e24e8719 246 return 0;
b892bb45
JK
247}
248
249/*
250 * Read a previously-exported (and possibly edited) object back from "filename",
251 * interpreting it as "type", and writing the result to the object database.
252 * The sha1 of the written object is returned via sha1.
253 */
e24e8719 254static int import_object(struct object_id *oid, enum object_type type,
2deda629 255 int raw, const char *filename)
b892bb45
JK
256{
257 int fd;
258
259 fd = open(filename, O_RDONLY);
260 if (fd < 0)
225c62e0 261 return error_errno(_("unable to open %s for reading"), filename);
b892bb45 262
2deda629 263 if (!raw && type == OBJ_TREE) {
d3180279 264 struct child_process cmd = CHILD_PROCESS_INIT;
b892bb45
JK
265 struct strbuf result = STRBUF_INIT;
266
2b709893 267 strvec_push(&cmd.args, "mktree");
b892bb45
JK
268 cmd.git_cmd = 1;
269 cmd.in = fd;
270 cmd.out = -1;
271
e24e8719
JS
272 if (start_command(&cmd)) {
273 close(fd);
225c62e0 274 return error(_("unable to spawn mktree"));
e24e8719 275 }
b892bb45 276
28ba1830 277 if (strbuf_read(&result, cmd.out, the_hash_algo->hexsz + 1) < 0) {
225c62e0 278 error_errno(_("unable to read from mktree"));
e24e8719
JS
279 close(fd);
280 close(cmd.out);
281 return -1;
282 }
b892bb45
JK
283 close(cmd.out);
284
e24e8719
JS
285 if (finish_command(&cmd)) {
286 strbuf_release(&result);
225c62e0 287 return error(_("mktree reported failure"));
e24e8719
JS
288 }
289 if (get_oid_hex(result.buf, oid) < 0) {
290 strbuf_release(&result);
225c62e0 291 return error(_("mktree did not return an object name"));
e24e8719 292 }
b892bb45
JK
293
294 strbuf_release(&result);
295 } else {
296 struct stat st;
297 int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT;
298
e24e8719 299 if (fstat(fd, &st) < 0) {
225c62e0 300 error_errno(_("unable to fstat %s"), filename);
e24e8719
JS
301 close(fd);
302 return -1;
303 }
f8adbec9 304 if (index_fd(the_repository->index, oid, fd, &st, type, NULL, flags) < 0)
225c62e0 305 return error(_("unable to write object to database"));
b892bb45
JK
306 /* index_fd close()s fd for us */
307 }
308
309 /*
310 * No need to close(fd) here; both run-command and index-fd
311 * will have done it for us.
312 */
e24e8719 313 return 0;
b892bb45
JK
314}
315
2deda629 316static int edit_and_replace(const char *object_ref, int force, int raw)
b892bb45 317{
e24e8719 318 char *tmpfile;
b892bb45 319 enum object_type type;
efdfe11f 320 struct object_id old_oid, new_oid, prev;
7f897b6f 321 struct strbuf ref = STRBUF_INIT;
b892bb45 322
d850b7a5 323 if (repo_get_oid(the_repository, object_ref, &old_oid) < 0)
225c62e0 324 return error(_("not a valid object name: '%s'"), object_ref);
b892bb45 325
0df8e965 326 type = oid_object_info(the_repository, &old_oid, NULL);
b892bb45 327 if (type < 0)
225c62e0 328 return error(_("unable to get object type for %s"),
e24e8719 329 oid_to_hex(&old_oid));
b892bb45 330
e24e8719
JS
331 if (check_ref_valid(&old_oid, &prev, &ref, force)) {
332 strbuf_release(&ref);
333 return -1;
334 }
7f897b6f 335 strbuf_release(&ref);
24790835 336
e24e8719
JS
337 tmpfile = git_pathdup("REPLACE_EDITOBJ");
338 if (export_object(&old_oid, type, raw, tmpfile)) {
339 free(tmpfile);
340 return -1;
341 }
342 if (launch_editor(tmpfile, NULL, NULL) < 0) {
343 free(tmpfile);
225c62e0 344 return error(_("editing object file failed"));
e24e8719
JS
345 }
346 if (import_object(&new_oid, type, raw, tmpfile)) {
347 free(tmpfile);
348 return -1;
349 }
b892bb45
JK
350 free(tmpfile);
351
4a7e27e9 352 if (oideq(&old_oid, &new_oid))
225c62e0 353 return error(_("new object is the same as the old one: '%s'"), oid_to_hex(&old_oid));
f22166b5 354
efdfe11f 355 return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force);
b892bb45
JK
356}
357
e24e8719 358static int replace_parents(struct strbuf *buf, int argc, const char **argv)
4228e8bc
CC
359{
360 struct strbuf new_parents = STRBUF_INIT;
361 const char *parent_start, *parent_end;
362 int i;
28ba1830 363 const unsigned hexsz = the_hash_algo->hexsz;
4228e8bc
CC
364
365 /* find existing parents */
366 parent_start = buf->buf;
28ba1830 367 parent_start += hexsz + 6; /* "tree " + "hex sha1" + "\n" */
4228e8bc
CC
368 parent_end = parent_start;
369
370 while (starts_with(parent_end, "parent "))
28ba1830 371 parent_end += hexsz + 8; /* "parent " + "hex sha1" + "\n" */
4228e8bc
CC
372
373 /* prepare new parents */
374 for (i = 0; i < argc; i++) {
cea4332e 375 struct object_id oid;
f8e44a81
CC
376 struct commit *commit;
377
d850b7a5 378 if (repo_get_oid(the_repository, argv[i], &oid) < 0) {
e24e8719 379 strbuf_release(&new_parents);
1a07e59c 380 return error(_("not a valid object name: '%s'"),
e24e8719
JS
381 argv[i]);
382 }
f8e44a81
CC
383 commit = lookup_commit_reference(the_repository, &oid);
384 if (!commit) {
e24e8719 385 strbuf_release(&new_parents);
f8e44a81 386 return error(_("could not parse %s as a commit"), argv[i]);
e24e8719 387 }
f8e44a81 388 strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&commit->object.oid));
4228e8bc
CC
389 }
390
391 /* replace existing parents with new ones */
392 strbuf_splice(buf, parent_start - buf->buf, parent_end - parent_start,
393 new_parents.buf, new_parents.len);
394
395 strbuf_release(&new_parents);
e24e8719 396 return 0;
4228e8bc
CC
397}
398
25a05a8c
CC
399struct check_mergetag_data {
400 int argc;
401 const char **argv;
402};
403
fef461ea 404static int check_one_mergetag(struct commit *commit,
25a05a8c
CC
405 struct commit_extra_header *extra,
406 void *data)
407{
408 struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data;
409 const char *ref = mergetag_data->argv[0];
cea4332e 410 struct object_id tag_oid;
25a05a8c
CC
411 struct tag *tag;
412 int i;
413
2dcde20e 414 hash_object_file(the_hash_algo, extra->value, extra->len,
44439c1c 415 OBJ_TAG, &tag_oid);
ce71efb7 416 tag = lookup_tag(the_repository, &tag_oid);
25a05a8c 417 if (!tag)
e24e8719 418 return error(_("bad mergetag in commit '%s'"), ref);
0e740fed 419 if (parse_tag_buffer(the_repository, tag, extra->value, extra->len))
e24e8719 420 return error(_("malformed mergetag in commit '%s'"), ref);
25a05a8c
CC
421
422 /* iterate over new parents */
423 for (i = 1; i < mergetag_data->argc; i++) {
f2fd0760 424 struct object_id oid;
d850b7a5 425 if (repo_get_oid(the_repository, mergetag_data->argv[i], &oid) < 0)
1a07e59c 426 return error(_("not a valid object name: '%s'"),
e24e8719 427 mergetag_data->argv[i]);
c77722b3 428 if (oideq(get_tagged_oid(tag), &oid))
fef461ea 429 return 0; /* found */
25a05a8c
CC
430 }
431
e24e8719
JS
432 return error(_("original commit '%s' contains mergetag '%s' that is "
433 "discarded; use --edit instead of --graft"), ref,
434 oid_to_hex(&tag_oid));
25a05a8c
CC
435}
436
fef461ea 437static int check_mergetags(struct commit *commit, int argc, const char **argv)
25a05a8c
CC
438{
439 struct check_mergetag_data mergetag_data;
440
441 mergetag_data.argc = argc;
442 mergetag_data.argv = argv;
fef461ea 443 return for_each_mergetag(check_one_mergetag, commit, &mergetag_data);
25a05a8c
CC
444}
445
041c98e2 446static int create_graft(int argc, const char **argv, int force, int gentle)
4228e8bc 447{
efdfe11f 448 struct object_id old_oid, new_oid;
4228e8bc
CC
449 const char *old_ref = argv[0];
450 struct commit *commit;
451 struct strbuf buf = STRBUF_INIT;
452 const char *buffer;
453 unsigned long size;
454
d850b7a5 455 if (repo_get_oid(the_repository, old_ref, &old_oid) < 0)
1a07e59c 456 return error(_("not a valid object name: '%s'"), old_ref);
2122f675 457 commit = lookup_commit_reference(the_repository, &old_oid);
e24e8719
JS
458 if (!commit)
459 return error(_("could not parse %s"), old_ref);
4228e8bc
CC
460
461 buffer = get_commit_buffer(commit, &size);
462 strbuf_add(&buf, buffer, size);
463 unuse_commit_buffer(commit, buffer);
464
e24e8719
JS
465 if (replace_parents(&buf, argc - 1, &argv[1]) < 0) {
466 strbuf_release(&buf);
467 return -1;
468 }
4228e8bc 469
0b05ab6f 470 if (remove_signature(&buf)) {
1a07e59c 471 warning(_("the original commit '%s' has a gpg signature"), old_ref);
0b05ab6f
CC
472 warning(_("the signature will be removed in the replacement commit!"));
473 }
474
e24e8719
JS
475 if (check_mergetags(commit, argc, argv)) {
476 strbuf_release(&buf);
477 return -1;
478 }
25a05a8c 479
c80d226a 480 if (write_object_file(buf.buf, buf.len, OBJ_COMMIT, &new_oid)) {
e24e8719
JS
481 strbuf_release(&buf);
482 return error(_("could not write replacement commit for: '%s'"),
483 old_ref);
484 }
4228e8bc
CC
485
486 strbuf_release(&buf);
487
ee521ec4 488 if (oideq(&commit->object.oid, &new_oid)) {
041c98e2 489 if (gentle) {
ee521ec4
CC
490 warning(_("graft for '%s' unnecessary"),
491 oid_to_hex(&commit->object.oid));
041c98e2
JS
492 return 0;
493 }
ee521ec4
CC
494 return error(_("new commit is the same as the old one: '%s'"),
495 oid_to_hex(&commit->object.oid));
041c98e2 496 }
4228e8bc 497
ee521ec4
CC
498 return replace_object_oid(old_ref, &commit->object.oid,
499 "replacement", &new_oid, force);
4228e8bc
CC
500}
501
fb404291
JS
502static int convert_graft_file(int force)
503{
b16b60f7 504 const char *graft_file = get_graft_file(the_repository);
fb404291
JS
505 FILE *fp = fopen_or_warn(graft_file, "r");
506 struct strbuf buf = STRBUF_INIT, err = STRBUF_INIT;
22f9b7f3 507 struct strvec args = STRVEC_INIT;
fb404291
JS
508
509 if (!fp)
510 return -1;
511
ab628588 512 no_graft_file_deprecated_advice = 1;
fb404291
JS
513 while (strbuf_getline(&buf, fp) != EOF) {
514 if (*buf.buf == '#')
515 continue;
516
22f9b7f3 517 strvec_split(&args, buf.buf);
d70a9eb6 518 if (args.nr && create_graft(args.nr, args.v, force, 1))
fb404291 519 strbuf_addf(&err, "\n\t%s", buf.buf);
22f9b7f3 520 strvec_clear(&args);
fb404291
JS
521 }
522 fclose(fp);
523
524 strbuf_release(&buf);
525
526 if (!err.len)
527 return unlink_or_warn(graft_file);
528
529 warning(_("could not convert the following graft(s):\n%s"), err.buf);
530 strbuf_release(&err);
531
532 return -1;
533}
534
54b0c1e0
CC
535int cmd_replace(int argc, const char **argv, const char *prefix)
536{
70c7bd6d 537 int force = 0;
2deda629 538 int raw = 0;
44f9f850 539 const char *format = NULL;
70c7bd6d
JK
540 enum {
541 MODE_UNSPECIFIED = 0,
542 MODE_LIST,
543 MODE_DELETE,
b892bb45 544 MODE_EDIT,
4228e8bc 545 MODE_GRAFT,
fb404291 546 MODE_CONVERT_GRAFT_FILE,
70c7bd6d
JK
547 MODE_REPLACE
548 } cmdmode = MODE_UNSPECIFIED;
54b0c1e0 549 struct option options[] = {
70c7bd6d
JK
550 OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
551 OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
b892bb45 552 OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
4228e8bc 553 OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
fb404291 554 OPT_CMDMODE(0, "convert-graft-file", &cmdmode, N_("convert existing graft file"), MODE_CONVERT_GRAFT_FILE),
1b354755
NTND
555 OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"),
556 PARSE_OPT_NOCOMPLETE),
2deda629 557 OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
44f9f850 558 OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
54b0c1e0
CC
559 OPT_END()
560 };
561
6ebd1caf 562 read_replace_refs = 0;
36b14370 563 git_config(git_default_config, NULL);
769a4fa4 564
451bb210 565 argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
54b0c1e0 566
70c7bd6d
JK
567 if (!cmdmode)
568 cmdmode = argc ? MODE_REPLACE : MODE_LIST;
3f495f67 569
70c7bd6d 570 if (format && cmdmode != MODE_LIST)
225c62e0 571 usage_msg_opt(_("--format cannot be used when not listing"),
44f9f850
CC
572 git_replace_usage, options);
573
4228e8bc
CC
574 if (force &&
575 cmdmode != MODE_REPLACE &&
576 cmdmode != MODE_EDIT &&
fb404291
JS
577 cmdmode != MODE_GRAFT &&
578 cmdmode != MODE_CONVERT_GRAFT_FILE)
225c62e0 579 usage_msg_opt(_("-f only makes sense when writing a replacement"),
86af2caa 580 git_replace_usage, options);
bebdd271 581
2deda629 582 if (raw && cmdmode != MODE_EDIT)
225c62e0 583 usage_msg_opt(_("--raw only makes sense with --edit"),
2deda629
JK
584 git_replace_usage, options);
585
70c7bd6d
JK
586 switch (cmdmode) {
587 case MODE_DELETE:
54b0c1e0 588 if (argc < 1)
225c62e0 589 usage_msg_opt(_("-d needs at least one argument"),
86af2caa 590 git_replace_usage, options);
54b0c1e0 591 return for_each_replace_name(argv, delete_replace_ref);
54b0c1e0 592
70c7bd6d 593 case MODE_REPLACE:
bebdd271 594 if (argc != 2)
225c62e0 595 usage_msg_opt(_("bad number of arguments"),
86af2caa 596 git_replace_usage, options);
bebdd271 597 return replace_object(argv[0], argv[1], force);
bebdd271 598
b892bb45
JK
599 case MODE_EDIT:
600 if (argc != 1)
225c62e0 601 usage_msg_opt(_("-e needs exactly one argument"),
b892bb45 602 git_replace_usage, options);
2deda629 603 return edit_and_replace(argv[0], force, raw);
b892bb45 604
4228e8bc
CC
605 case MODE_GRAFT:
606 if (argc < 1)
225c62e0 607 usage_msg_opt(_("-g needs at least one argument"),
4228e8bc 608 git_replace_usage, options);
041c98e2 609 return create_graft(argc, argv, force, 0);
4228e8bc 610
fb404291
JS
611 case MODE_CONVERT_GRAFT_FILE:
612 if (argc != 0)
225c62e0 613 usage_msg_opt(_("--convert-graft-file takes no argument"),
fb404291
JS
614 git_replace_usage, options);
615 return !!convert_graft_file(force);
4228e8bc 616
70c7bd6d
JK
617 case MODE_LIST:
618 if (argc > 1)
225c62e0 619 usage_msg_opt(_("only one pattern can be given with -l"),
70c7bd6d
JK
620 git_replace_usage, options);
621 return list_replace_refs(argv[0], format);
54b0c1e0 622
70c7bd6d 623 default:
d398f2ea 624 BUG("invalid cmdmode %d", (int)cmdmode);
70c7bd6d 625 }
54b0c1e0 626}