replace: add --graft option
The usage string for this option is: git replace [-f] --graft <commit> [<parent>...] First we create a new commit that is the same as <commit> except that its parents are [<parents>...] Then we create a replace ref that replace <commit> with the commit we just created. With this new option, it should be straightforward to convert grafts to replace refs. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Christian Couder committed
Jul 19, 2014 at 17:01 UTC
4228e8bc989c6cac1c47faece720faa06531281f
1 file changed
+73
-1
builtin/replace.c
+73
-1
@@ -17,6 +17,7 @@
17
static const char * const git_replace_usage[] = {
18
N_("git replace [-f] <object> <replacement>"),
19
N_("git replace [-f] --edit <object>"),
20
+ N_("git replace [-f] --graft <commit> [<parent>...]"),
21
N_("git replace -d <object>..."),
22
N_("git replace [--format=<format>] [-l [<pattern>]]"),
23
NULL
@@ -294,6 +295,66 @@ static int edit_and_replace(const char *object_ref, int force)
295
return replace_object_sha1(object_ref, old, "replacement", new, force);
296
}
297
298
+static void replace_parents(struct strbuf *buf, int argc, const char **argv)
299
+{
300
+ struct strbuf new_parents = STRBUF_INIT;
301
+ const char *parent_start, *parent_end;
302
+ int i;
303
+
304
+ /* find existing parents */
305
+ parent_start = buf->buf;
306
+ parent_start += 46; /* "tree " + "hex sha1" + "\n" */
307
+ parent_end = parent_start;
308
+
309
+ while (starts_with(parent_end, "parent "))
310
+ parent_end += 48; /* "parent " + "hex sha1" + "\n" */
311
+
312
+ /* prepare new parents */
313
+ for (i = 0; i < argc; i++) {
314
+ unsigned char sha1[20];
315
+ if (get_sha1(argv[i], sha1) < 0)
316
+ die(_("Not a valid object name: '%s'"), argv[i]);
317
+ lookup_commit_or_die(sha1, argv[i]);
318
+ strbuf_addf(&new_parents, "parent %s\n", sha1_to_hex(sha1));
319
+ }
320
+
321
+ /* replace existing parents with new ones */
322
+ strbuf_splice(buf, parent_start - buf->buf, parent_end - parent_start,
323
+ new_parents.buf, new_parents.len);
324
+
325
+ strbuf_release(&new_parents);
326
+}
327
+
328
+static int create_graft(int argc, const char **argv, int force)
329
+{
330
+ unsigned char old[20], new[20];
331
+ const char *old_ref = argv[0];
332
+ struct commit *commit;
333
+ struct strbuf buf = STRBUF_INIT;
334
+ const char *buffer;
335
+ unsigned long size;
336
+
337
+ if (get_sha1(old_ref, old) < 0)
338
+ die(_("Not a valid object name: '%s'"), old_ref);
339
+ commit = lookup_commit_or_die(old, old_ref);
340
+
341
+ buffer = get_commit_buffer(commit, &size);
342
+ strbuf_add(&buf, buffer, size);
343
+ unuse_commit_buffer(commit, buffer);
344
+
345
+ replace_parents(&buf, argc - 1, &argv[1]);
346
+
347
+ if (write_sha1_file(buf.buf, buf.len, commit_type, new))
348
+ die(_("could not write replacement commit for: '%s'"), old_ref);
349
+
350
+ strbuf_release(&buf);
351
+
352
+ if (!hashcmp(old, new))
353
+ return error("new commit is the same as the old one: '%s'", sha1_to_hex(old));
354
+
355
+ return replace_object_sha1(old_ref, old, "replacement", new, force);
356
+}
357
+
358
int cmd_replace(int argc, const char **argv, const char *prefix)
359
{
360
int force = 0;
@@ -303,12 +364,14 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
364
MODE_LIST,
365
MODE_DELETE,
366
MODE_EDIT,
367
+ MODE_GRAFT,
368
MODE_REPLACE
369
} cmdmode = MODE_UNSPECIFIED;
370
struct option options[] = {
371
OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
372
OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
373
OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
374
+ OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
375
OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
376
OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
377
OPT_END()
@@ -325,7 +388,10 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
388
usage_msg_opt("--format cannot be used when not listing",
389
git_replace_usage, options);
390
328
- if (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT)
391
+ if (force &&
392
+ cmdmode != MODE_REPLACE &&
393
+ cmdmode != MODE_EDIT &&
394
+ cmdmode != MODE_GRAFT)
395
usage_msg_opt("-f only makes sense when writing a replacement",
396
git_replace_usage, options);
397
@@ -348,6 +414,12 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
414
git_replace_usage, options);
415
return edit_and_replace(argv[0], force);
416
417
+ case MODE_GRAFT:
418
+ if (argc < 1)
419
+ usage_msg_opt("-g needs at least one argument",
420
+ git_replace_usage, options);
421
+ return create_graft(argc, argv, force);
422
+
423
case MODE_LIST:
424
if (argc > 1)
425
usage_msg_opt("only one pattern can be given with -l",