1717static 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+
297358int 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