17
17
static const char * const git_replace_usage [] = {
18
18
N_ ("git replace [-f] <object> <replacement>" ),
19
19
N_ ("git replace [-f] --edit <object>" ),
20
+ N_ ("git replace [-f] --graft <commit> [<parent>...]" ),
20
21
N_ ("git replace -d <object>..." ),
21
22
N_ ("git replace [--format=<format>] [-l [<pattern>]]" ),
22
23
NULL
@@ -294,6 +295,66 @@ static int edit_and_replace(const char *object_ref, int force)
294
295
return replace_object_sha1 (object_ref , old , "replacement" , new , force );
295
296
}
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
+
297
358
int cmd_replace (int argc , const char * * argv , const char * prefix )
298
359
{
299
360
int force = 0 ;
@@ -303,12 +364,14 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
303
364
MODE_LIST ,
304
365
MODE_DELETE ,
305
366
MODE_EDIT ,
367
+ MODE_GRAFT ,
306
368
MODE_REPLACE
307
369
} cmdmode = MODE_UNSPECIFIED ;
308
370
struct option options [] = {
309
371
OPT_CMDMODE ('l' , "list" , & cmdmode , N_ ("list replace refs" ), MODE_LIST ),
310
372
OPT_CMDMODE ('d' , "delete" , & cmdmode , N_ ("delete replace refs" ), MODE_DELETE ),
311
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 ),
312
375
OPT_BOOL ('f' , "force" , & force , N_ ("replace the ref if it exists" )),
313
376
OPT_STRING (0 , "format" , & format , N_ ("format" ), N_ ("use this format" )),
314
377
OPT_END ()
@@ -325,7 +388,10 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
325
388
usage_msg_opt ("--format cannot be used when not listing" ,
326
389
git_replace_usage , options );
327
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 )
329
395
usage_msg_opt ("-f only makes sense when writing a replacement" ,
330
396
git_replace_usage , options );
331
397
@@ -348,6 +414,12 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
348
414
git_replace_usage , options );
349
415
return edit_and_replace (argv [0 ], force );
350
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
+
351
423
case MODE_LIST :
352
424
if (argc > 1 )
353
425
usage_msg_opt ("only one pattern can be given with -l" ,
0 commit comments