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