Skip to content

Commit 4228e8b

Browse files
chriscoolgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1c2828c commit 4228e8b

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

builtin/replace.c

+73-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
static const char * const git_replace_usage[] = {
1818
N_("git replace [-f] <object> <replacement>"),
1919
N_("git replace [-f] --edit <object>"),
20+
N_("git replace [-f] --graft <commit> [<parent>...]"),
2021
N_("git replace -d <object>..."),
2122
N_("git replace [--format=<format>] [-l [<pattern>]]"),
2223
NULL
@@ -294,6 +295,66 @@ static int edit_and_replace(const char *object_ref, int force)
294295
return replace_object_sha1(object_ref, old, "replacement", new, force);
295296
}
296297

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+
297358
int cmd_replace(int argc, const char **argv, const char *prefix)
298359
{
299360
int force = 0;
@@ -303,12 +364,14 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
303364
MODE_LIST,
304365
MODE_DELETE,
305366
MODE_EDIT,
367+
MODE_GRAFT,
306368
MODE_REPLACE
307369
} cmdmode = MODE_UNSPECIFIED;
308370
struct option options[] = {
309371
OPT_CMDMODE('l', "list", &cmdmode, N_("list replace refs"), MODE_LIST),
310372
OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
311373
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),
312375
OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
313376
OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
314377
OPT_END()
@@ -325,7 +388,10 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
325388
usage_msg_opt("--format cannot be used when not listing",
326389
git_replace_usage, options);
327390

328-
if (force && cmdmode != MODE_REPLACE && cmdmode != MODE_EDIT)
391+
if (force &&
392+
cmdmode != MODE_REPLACE &&
393+
cmdmode != MODE_EDIT &&
394+
cmdmode != MODE_GRAFT)
329395
usage_msg_opt("-f only makes sense when writing a replacement",
330396
git_replace_usage, options);
331397

@@ -348,6 +414,12 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
348414
git_replace_usage, options);
349415
return edit_and_replace(argv[0], force);
350416

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+
351423
case MODE_LIST:
352424
if (argc > 1)
353425
usage_msg_opt("only one pattern can be given with -l",

0 commit comments

Comments
 (0)