1/* 2 * Builtin "git am" 3 * 4 * Based on git-am.sh by Junio C Hamano. 5 */ 6#include"cache.h" 7#include"builtin.h" 8#include"exec_cmd.h" 9#include"parse-options.h" 10#include"dir.h" 11#include"run-command.h" 12#include"quote.h" 13#include"tempfile.h" 14#include"lockfile.h" 15#include"cache-tree.h" 16#include"refs.h" 17#include"commit.h" 18#include"diff.h" 19#include"diffcore.h" 20#include"unpack-trees.h" 21#include"branch.h" 22#include"sequencer.h" 23#include"revision.h" 24#include"merge-recursive.h" 25#include"revision.h" 26#include"log-tree.h" 27#include"notes-utils.h" 28#include"rerere.h" 29#include"prompt.h" 30#include"mailinfo.h" 31#include"string-list.h" 32 33/** 34 * Returns 1 if the file is empty or does not exist, 0 otherwise. 35 */ 36static intis_empty_file(const char*filename) 37{ 38struct stat st; 39 40if(stat(filename, &st) <0) { 41if(errno == ENOENT) 42return1; 43die_errno(_("could not stat%s"), filename); 44} 45 46return!st.st_size; 47} 48 49/** 50 * Returns the length of the first line of msg. 51 */ 52static intlinelen(const char*msg) 53{ 54returnstrchrnul(msg,'\n') - msg; 55} 56 57/** 58 * Returns true if `str` consists of only whitespace, false otherwise. 59 */ 60static intstr_isspace(const char*str) 61{ 62for(; *str; str++) 63if(!isspace(*str)) 64return0; 65 66return1; 67} 68 69enum patch_format { 70 PATCH_FORMAT_UNKNOWN =0, 71 PATCH_FORMAT_MBOX, 72 PATCH_FORMAT_STGIT, 73 PATCH_FORMAT_STGIT_SERIES, 74 PATCH_FORMAT_HG, 75 PATCH_FORMAT_MBOXRD 76}; 77 78enum keep_type { 79 KEEP_FALSE =0, 80 KEEP_TRUE,/* pass -k flag to git-mailinfo */ 81 KEEP_NON_PATCH /* pass -b flag to git-mailinfo */ 82}; 83 84enum scissors_type { 85 SCISSORS_UNSET = -1, 86 SCISSORS_FALSE =0,/* pass --no-scissors to git-mailinfo */ 87 SCISSORS_TRUE /* pass --scissors to git-mailinfo */ 88}; 89 90enum signoff_type { 91 SIGNOFF_FALSE =0, 92 SIGNOFF_TRUE =1, 93 SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ 94}; 95 96struct am_state { 97/* state directory path */ 98char*dir; 99 100/* current and last patch numbers, 1-indexed */ 101int cur; 102int last; 103 104/* commit metadata and message */ 105char*author_name; 106char*author_email; 107char*author_date; 108char*msg; 109size_t msg_len; 110 111/* when --rebasing, records the original commit the patch came from */ 112unsigned char orig_commit[GIT_SHA1_RAWSZ]; 113 114/* number of digits in patch filename */ 115int prec; 116 117/* various operating modes and command line options */ 118int interactive; 119int threeway; 120int quiet; 121int signoff;/* enum signoff_type */ 122int utf8; 123int keep;/* enum keep_type */ 124int message_id; 125int scissors;/* enum scissors_type */ 126struct argv_array git_apply_opts; 127const char*resolvemsg; 128int committer_date_is_author_date; 129int ignore_date; 130int allow_rerere_autoupdate; 131const char*sign_commit; 132int rebasing; 133}; 134 135/** 136 * Initializes am_state with the default values. The state directory is set to 137 * dir. 138 */ 139static voidam_state_init(struct am_state *state,const char*dir) 140{ 141int gpgsign; 142 143memset(state,0,sizeof(*state)); 144 145assert(dir); 146 state->dir =xstrdup(dir); 147 148 state->prec =4; 149 150git_config_get_bool("am.threeway", &state->threeway); 151 152 state->utf8 =1; 153 154git_config_get_bool("am.messageid", &state->message_id); 155 156 state->scissors = SCISSORS_UNSET; 157 158argv_array_init(&state->git_apply_opts); 159 160if(!git_config_get_bool("commit.gpgsign", &gpgsign)) 161 state->sign_commit = gpgsign ?"": NULL; 162} 163 164/** 165 * Releases memory allocated by an am_state. 166 */ 167static voidam_state_release(struct am_state *state) 168{ 169free(state->dir); 170free(state->author_name); 171free(state->author_email); 172free(state->author_date); 173free(state->msg); 174argv_array_clear(&state->git_apply_opts); 175} 176 177/** 178 * Returns path relative to the am_state directory. 179 */ 180staticinlineconst char*am_path(const struct am_state *state,const char*path) 181{ 182returnmkpath("%s/%s", state->dir, path); 183} 184 185/** 186 * For convenience to call write_file() 187 */ 188static voidwrite_state_text(const struct am_state *state, 189const char*name,const char*string) 190{ 191write_file(am_path(state, name),"%s", string); 192} 193 194static voidwrite_state_count(const struct am_state *state, 195const char*name,int value) 196{ 197write_file(am_path(state, name),"%d", value); 198} 199 200static voidwrite_state_bool(const struct am_state *state, 201const char*name,int value) 202{ 203write_state_text(state, name, value ?"t":"f"); 204} 205 206/** 207 * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline 208 * at the end. 209 */ 210static voidsay(const struct am_state *state,FILE*fp,const char*fmt, ...) 211{ 212va_list ap; 213 214va_start(ap, fmt); 215if(!state->quiet) { 216vfprintf(fp, fmt, ap); 217putc('\n', fp); 218} 219va_end(ap); 220} 221 222/** 223 * Returns 1 if there is an am session in progress, 0 otherwise. 224 */ 225static intam_in_progress(const struct am_state *state) 226{ 227struct stat st; 228 229if(lstat(state->dir, &st) <0|| !S_ISDIR(st.st_mode)) 230return0; 231if(lstat(am_path(state,"last"), &st) || !S_ISREG(st.st_mode)) 232return0; 233if(lstat(am_path(state,"next"), &st) || !S_ISREG(st.st_mode)) 234return0; 235return1; 236} 237 238/** 239 * Reads the contents of `file` in the `state` directory into `sb`. Returns the 240 * number of bytes read on success, -1 if the file does not exist. If `trim` is 241 * set, trailing whitespace will be removed. 242 */ 243static intread_state_file(struct strbuf *sb,const struct am_state *state, 244const char*file,int trim) 245{ 246strbuf_reset(sb); 247 248if(strbuf_read_file(sb,am_path(state, file),0) >=0) { 249if(trim) 250strbuf_trim(sb); 251 252return sb->len; 253} 254 255if(errno == ENOENT) 256return-1; 257 258die_errno(_("could not read '%s'"),am_path(state, file)); 259} 260 261/** 262 * Take a series of KEY='VALUE' lines where VALUE part is 263 * sq-quoted, and append <KEY, VALUE> at the end of the string list 264 */ 265static intparse_key_value_squoted(char*buf,struct string_list *list) 266{ 267while(*buf) { 268struct string_list_item *item; 269char*np; 270char*cp =strchr(buf,'='); 271if(!cp) 272return-1; 273 np =strchrnul(cp,'\n'); 274*cp++ ='\0'; 275 item =string_list_append(list, buf); 276 277 buf = np + (*np =='\n'); 278*np ='\0'; 279 cp =sq_dequote(cp); 280if(!cp) 281return-1; 282 item->util =xstrdup(cp); 283} 284return0; 285} 286 287/** 288 * Reads and parses the state directory's "author-script" file, and sets 289 * state->author_name, state->author_email and state->author_date accordingly. 290 * Returns 0 on success, -1 if the file could not be parsed. 291 * 292 * The author script is of the format: 293 * 294 * GIT_AUTHOR_NAME='$author_name' 295 * GIT_AUTHOR_EMAIL='$author_email' 296 * GIT_AUTHOR_DATE='$author_date' 297 * 298 * where $author_name, $author_email and $author_date are quoted. We are strict 299 * with our parsing, as the file was meant to be eval'd in the old git-am.sh 300 * script, and thus if the file differs from what this function expects, it is 301 * better to bail out than to do something that the user does not expect. 302 */ 303static intread_author_script(struct am_state *state) 304{ 305const char*filename =am_path(state,"author-script"); 306struct strbuf buf = STRBUF_INIT; 307struct string_list kv = STRING_LIST_INIT_DUP; 308int retval = -1;/* assume failure */ 309int fd; 310 311assert(!state->author_name); 312assert(!state->author_email); 313assert(!state->author_date); 314 315 fd =open(filename, O_RDONLY); 316if(fd <0) { 317if(errno == ENOENT) 318return0; 319die_errno(_("could not open '%s' for reading"), filename); 320} 321strbuf_read(&buf, fd,0); 322close(fd); 323if(parse_key_value_squoted(buf.buf, &kv)) 324goto finish; 325 326if(kv.nr !=3|| 327strcmp(kv.items[0].string,"GIT_AUTHOR_NAME") || 328strcmp(kv.items[1].string,"GIT_AUTHOR_EMAIL") || 329strcmp(kv.items[2].string,"GIT_AUTHOR_DATE")) 330goto finish; 331 state->author_name = kv.items[0].util; 332 state->author_email = kv.items[1].util; 333 state->author_date = kv.items[2].util; 334 retval =0; 335finish: 336string_list_clear(&kv, !!retval); 337strbuf_release(&buf); 338return retval; 339} 340 341/** 342 * Saves state->author_name, state->author_email and state->author_date in the 343 * state directory's "author-script" file. 344 */ 345static voidwrite_author_script(const struct am_state *state) 346{ 347struct strbuf sb = STRBUF_INIT; 348 349strbuf_addstr(&sb,"GIT_AUTHOR_NAME="); 350sq_quote_buf(&sb, state->author_name); 351strbuf_addch(&sb,'\n'); 352 353strbuf_addstr(&sb,"GIT_AUTHOR_EMAIL="); 354sq_quote_buf(&sb, state->author_email); 355strbuf_addch(&sb,'\n'); 356 357strbuf_addstr(&sb,"GIT_AUTHOR_DATE="); 358sq_quote_buf(&sb, state->author_date); 359strbuf_addch(&sb,'\n'); 360 361write_state_text(state,"author-script", sb.buf); 362 363strbuf_release(&sb); 364} 365 366/** 367 * Reads the commit message from the state directory's "final-commit" file, 368 * setting state->msg to its contents and state->msg_len to the length of its 369 * contents in bytes. 370 * 371 * Returns 0 on success, -1 if the file does not exist. 372 */ 373static intread_commit_msg(struct am_state *state) 374{ 375struct strbuf sb = STRBUF_INIT; 376 377assert(!state->msg); 378 379if(read_state_file(&sb, state,"final-commit",0) <0) { 380strbuf_release(&sb); 381return-1; 382} 383 384 state->msg =strbuf_detach(&sb, &state->msg_len); 385return0; 386} 387 388/** 389 * Saves state->msg in the state directory's "final-commit" file. 390 */ 391static voidwrite_commit_msg(const struct am_state *state) 392{ 393const char*filename =am_path(state,"final-commit"); 394write_file_buf(filename, state->msg, state->msg_len); 395} 396 397/** 398 * Loads state from disk. 399 */ 400static voidam_load(struct am_state *state) 401{ 402struct strbuf sb = STRBUF_INIT; 403 404if(read_state_file(&sb, state,"next",1) <0) 405die("BUG: state file 'next' does not exist"); 406 state->cur =strtol(sb.buf, NULL,10); 407 408if(read_state_file(&sb, state,"last",1) <0) 409die("BUG: state file 'last' does not exist"); 410 state->last =strtol(sb.buf, NULL,10); 411 412if(read_author_script(state) <0) 413die(_("could not parse author script")); 414 415read_commit_msg(state); 416 417if(read_state_file(&sb, state,"original-commit",1) <0) 418hashclr(state->orig_commit); 419else if(get_sha1_hex(sb.buf, state->orig_commit) <0) 420die(_("could not parse%s"),am_path(state,"original-commit")); 421 422read_state_file(&sb, state,"threeway",1); 423 state->threeway = !strcmp(sb.buf,"t"); 424 425read_state_file(&sb, state,"quiet",1); 426 state->quiet = !strcmp(sb.buf,"t"); 427 428read_state_file(&sb, state,"sign",1); 429 state->signoff = !strcmp(sb.buf,"t"); 430 431read_state_file(&sb, state,"utf8",1); 432 state->utf8 = !strcmp(sb.buf,"t"); 433 434read_state_file(&sb, state,"keep",1); 435if(!strcmp(sb.buf,"t")) 436 state->keep = KEEP_TRUE; 437else if(!strcmp(sb.buf,"b")) 438 state->keep = KEEP_NON_PATCH; 439else 440 state->keep = KEEP_FALSE; 441 442read_state_file(&sb, state,"messageid",1); 443 state->message_id = !strcmp(sb.buf,"t"); 444 445read_state_file(&sb, state,"scissors",1); 446if(!strcmp(sb.buf,"t")) 447 state->scissors = SCISSORS_TRUE; 448else if(!strcmp(sb.buf,"f")) 449 state->scissors = SCISSORS_FALSE; 450else 451 state->scissors = SCISSORS_UNSET; 452 453read_state_file(&sb, state,"apply-opt",1); 454argv_array_clear(&state->git_apply_opts); 455if(sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) <0) 456die(_("could not parse%s"),am_path(state,"apply-opt")); 457 458 state->rebasing = !!file_exists(am_path(state,"rebasing")); 459 460strbuf_release(&sb); 461} 462 463/** 464 * Removes the am_state directory, forcefully terminating the current am 465 * session. 466 */ 467static voidam_destroy(const struct am_state *state) 468{ 469struct strbuf sb = STRBUF_INIT; 470 471strbuf_addstr(&sb, state->dir); 472remove_dir_recursively(&sb,0); 473strbuf_release(&sb); 474} 475 476/** 477 * Runs applypatch-msg hook. Returns its exit code. 478 */ 479static intrun_applypatch_msg_hook(struct am_state *state) 480{ 481int ret; 482 483assert(state->msg); 484 ret =run_hook_le(NULL,"applypatch-msg",am_path(state,"final-commit"), NULL); 485 486if(!ret) { 487free(state->msg); 488 state->msg = NULL; 489if(read_commit_msg(state) <0) 490die(_("'%s' was deleted by the applypatch-msg hook"), 491am_path(state,"final-commit")); 492} 493 494return ret; 495} 496 497/** 498 * Runs post-rewrite hook. Returns it exit code. 499 */ 500static intrun_post_rewrite_hook(const struct am_state *state) 501{ 502struct child_process cp = CHILD_PROCESS_INIT; 503const char*hook =find_hook("post-rewrite"); 504int ret; 505 506if(!hook) 507return0; 508 509argv_array_push(&cp.args, hook); 510argv_array_push(&cp.args,"rebase"); 511 512 cp.in =xopen(am_path(state,"rewritten"), O_RDONLY); 513 cp.stdout_to_stderr =1; 514 515 ret =run_command(&cp); 516 517close(cp.in); 518return ret; 519} 520 521/** 522 * Reads the state directory's "rewritten" file, and copies notes from the old 523 * commits listed in the file to their rewritten commits. 524 * 525 * Returns 0 on success, -1 on failure. 526 */ 527static intcopy_notes_for_rebase(const struct am_state *state) 528{ 529struct notes_rewrite_cfg *c; 530struct strbuf sb = STRBUF_INIT; 531const char*invalid_line =_("Malformed input line: '%s'."); 532const char*msg ="Notes added by 'git rebase'"; 533FILE*fp; 534int ret =0; 535 536assert(state->rebasing); 537 538 c =init_copy_notes_for_rewrite("rebase"); 539if(!c) 540return0; 541 542 fp =xfopen(am_path(state,"rewritten"),"r"); 543 544while(!strbuf_getline_lf(&sb, fp)) { 545unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ]; 546 547if(sb.len != GIT_SHA1_HEXSZ *2+1) { 548 ret =error(invalid_line, sb.buf); 549goto finish; 550} 551 552if(get_sha1_hex(sb.buf, from_obj)) { 553 ret =error(invalid_line, sb.buf); 554goto finish; 555} 556 557if(sb.buf[GIT_SHA1_HEXSZ] !=' ') { 558 ret =error(invalid_line, sb.buf); 559goto finish; 560} 561 562if(get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ +1, to_obj)) { 563 ret =error(invalid_line, sb.buf); 564goto finish; 565} 566 567if(copy_note_for_rewrite(c, from_obj, to_obj)) 568 ret =error(_("Failed to copy notes from '%s' to '%s'"), 569sha1_to_hex(from_obj),sha1_to_hex(to_obj)); 570} 571 572finish: 573finish_copy_notes_for_rewrite(c, msg); 574fclose(fp); 575strbuf_release(&sb); 576return ret; 577} 578 579/** 580 * Determines if the file looks like a piece of RFC2822 mail by grabbing all 581 * non-indented lines and checking if they look like they begin with valid 582 * header field names. 583 * 584 * Returns 1 if the file looks like a piece of mail, 0 otherwise. 585 */ 586static intis_mail(FILE*fp) 587{ 588const char*header_regex ="^[!-9;-~]+:"; 589struct strbuf sb = STRBUF_INIT; 590 regex_t regex; 591int ret =1; 592 593if(fseek(fp,0L, SEEK_SET)) 594die_errno(_("fseek failed")); 595 596if(regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED)) 597die("invalid pattern:%s", header_regex); 598 599while(!strbuf_getline(&sb, fp)) { 600if(!sb.len) 601break;/* End of header */ 602 603/* Ignore indented folded lines */ 604if(*sb.buf =='\t'|| *sb.buf ==' ') 605continue; 606 607/* It's a header if it matches header_regex */ 608if(regexec(®ex, sb.buf,0, NULL,0)) { 609 ret =0; 610goto done; 611} 612} 613 614done: 615regfree(®ex); 616strbuf_release(&sb); 617return ret; 618} 619 620/** 621 * Attempts to detect the patch_format of the patches contained in `paths`, 622 * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if 623 * detection fails. 624 */ 625static intdetect_patch_format(const char**paths) 626{ 627enum patch_format ret = PATCH_FORMAT_UNKNOWN; 628struct strbuf l1 = STRBUF_INIT; 629struct strbuf l2 = STRBUF_INIT; 630struct strbuf l3 = STRBUF_INIT; 631FILE*fp; 632 633/* 634 * We default to mbox format if input is from stdin and for directories 635 */ 636if(!*paths || !strcmp(*paths,"-") ||is_directory(*paths)) 637return PATCH_FORMAT_MBOX; 638 639/* 640 * Otherwise, check the first few lines of the first patch, starting 641 * from the first non-blank line, to try to detect its format. 642 */ 643 644 fp =xfopen(*paths,"r"); 645 646while(!strbuf_getline(&l1, fp)) { 647if(l1.len) 648break; 649} 650 651if(starts_with(l1.buf,"From ") ||starts_with(l1.buf,"From: ")) { 652 ret = PATCH_FORMAT_MBOX; 653goto done; 654} 655 656if(starts_with(l1.buf,"# This series applies on GIT commit")) { 657 ret = PATCH_FORMAT_STGIT_SERIES; 658goto done; 659} 660 661if(!strcmp(l1.buf,"# HG changeset patch")) { 662 ret = PATCH_FORMAT_HG; 663goto done; 664} 665 666strbuf_reset(&l2); 667strbuf_getline(&l2, fp); 668strbuf_reset(&l3); 669strbuf_getline(&l3, fp); 670 671/* 672 * If the second line is empty and the third is a From, Author or Date 673 * entry, this is likely an StGit patch. 674 */ 675if(l1.len && !l2.len && 676(starts_with(l3.buf,"From:") || 677starts_with(l3.buf,"Author:") || 678starts_with(l3.buf,"Date:"))) { 679 ret = PATCH_FORMAT_STGIT; 680goto done; 681} 682 683if(l1.len &&is_mail(fp)) { 684 ret = PATCH_FORMAT_MBOX; 685goto done; 686} 687 688done: 689fclose(fp); 690strbuf_release(&l1); 691return ret; 692} 693 694/** 695 * Splits out individual email patches from `paths`, where each path is either 696 * a mbox file or a Maildir. Returns 0 on success, -1 on failure. 697 */ 698static intsplit_mail_mbox(struct am_state *state,const char**paths, 699int keep_cr,int mboxrd) 700{ 701struct child_process cp = CHILD_PROCESS_INIT; 702struct strbuf last = STRBUF_INIT; 703 704 cp.git_cmd =1; 705argv_array_push(&cp.args,"mailsplit"); 706argv_array_pushf(&cp.args,"-d%d", state->prec); 707argv_array_pushf(&cp.args,"-o%s", state->dir); 708argv_array_push(&cp.args,"-b"); 709if(keep_cr) 710argv_array_push(&cp.args,"--keep-cr"); 711if(mboxrd) 712argv_array_push(&cp.args,"--mboxrd"); 713argv_array_push(&cp.args,"--"); 714argv_array_pushv(&cp.args, paths); 715 716if(capture_command(&cp, &last,8)) 717return-1; 718 719 state->cur =1; 720 state->last =strtol(last.buf, NULL,10); 721 722return0; 723} 724 725/** 726 * Callback signature for split_mail_conv(). The foreign patch should be 727 * read from `in`, and the converted patch (in RFC2822 mail format) should be 728 * written to `out`. Return 0 on success, or -1 on failure. 729 */ 730typedefint(*mail_conv_fn)(FILE*out,FILE*in,int keep_cr); 731 732/** 733 * Calls `fn` for each file in `paths` to convert the foreign patch to the 734 * RFC2822 mail format suitable for parsing with git-mailinfo. 735 * 736 * Returns 0 on success, -1 on failure. 737 */ 738static intsplit_mail_conv(mail_conv_fn fn,struct am_state *state, 739const char**paths,int keep_cr) 740{ 741static const char*stdin_only[] = {"-", NULL}; 742int i; 743 744if(!*paths) 745 paths = stdin_only; 746 747for(i =0; *paths; paths++, i++) { 748FILE*in, *out; 749const char*mail; 750int ret; 751 752if(!strcmp(*paths,"-")) 753 in = stdin; 754else 755 in =fopen(*paths,"r"); 756 757if(!in) 758returnerror_errno(_("could not open '%s' for reading"), 759*paths); 760 761 mail =mkpath("%s/%0*d", state->dir, state->prec, i +1); 762 763 out =fopen(mail,"w"); 764if(!out) 765returnerror_errno(_("could not open '%s' for writing"), 766 mail); 767 768 ret =fn(out, in, keep_cr); 769 770fclose(out); 771fclose(in); 772 773if(ret) 774returnerror(_("could not parse patch '%s'"), *paths); 775} 776 777 state->cur =1; 778 state->last = i; 779return0; 780} 781 782/** 783 * A split_mail_conv() callback that converts an StGit patch to an RFC2822 784 * message suitable for parsing with git-mailinfo. 785 */ 786static intstgit_patch_to_mail(FILE*out,FILE*in,int keep_cr) 787{ 788struct strbuf sb = STRBUF_INIT; 789int subject_printed =0; 790 791while(!strbuf_getline_lf(&sb, in)) { 792const char*str; 793 794if(str_isspace(sb.buf)) 795continue; 796else if(skip_prefix(sb.buf,"Author:", &str)) 797fprintf(out,"From:%s\n", str); 798else if(starts_with(sb.buf,"From") ||starts_with(sb.buf,"Date")) 799fprintf(out,"%s\n", sb.buf); 800else if(!subject_printed) { 801fprintf(out,"Subject:%s\n", sb.buf); 802 subject_printed =1; 803}else{ 804fprintf(out,"\n%s\n", sb.buf); 805break; 806} 807} 808 809strbuf_reset(&sb); 810while(strbuf_fread(&sb,8192, in) >0) { 811fwrite(sb.buf,1, sb.len, out); 812strbuf_reset(&sb); 813} 814 815strbuf_release(&sb); 816return0; 817} 818 819/** 820 * This function only supports a single StGit series file in `paths`. 821 * 822 * Given an StGit series file, converts the StGit patches in the series into 823 * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in 824 * the state directory. 825 * 826 * Returns 0 on success, -1 on failure. 827 */ 828static intsplit_mail_stgit_series(struct am_state *state,const char**paths, 829int keep_cr) 830{ 831const char*series_dir; 832char*series_dir_buf; 833FILE*fp; 834struct argv_array patches = ARGV_ARRAY_INIT; 835struct strbuf sb = STRBUF_INIT; 836int ret; 837 838if(!paths[0] || paths[1]) 839returnerror(_("Only one StGIT patch series can be applied at once")); 840 841 series_dir_buf =xstrdup(*paths); 842 series_dir =dirname(series_dir_buf); 843 844 fp =fopen(*paths,"r"); 845if(!fp) 846returnerror_errno(_("could not open '%s' for reading"), *paths); 847 848while(!strbuf_getline_lf(&sb, fp)) { 849if(*sb.buf =='#') 850continue;/* skip comment lines */ 851 852argv_array_push(&patches,mkpath("%s/%s", series_dir, sb.buf)); 853} 854 855fclose(fp); 856strbuf_release(&sb); 857free(series_dir_buf); 858 859 ret =split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr); 860 861argv_array_clear(&patches); 862return ret; 863} 864 865/** 866 * A split_patches_conv() callback that converts a mercurial patch to a RFC2822 867 * message suitable for parsing with git-mailinfo. 868 */ 869static inthg_patch_to_mail(FILE*out,FILE*in,int keep_cr) 870{ 871struct strbuf sb = STRBUF_INIT; 872 873while(!strbuf_getline_lf(&sb, in)) { 874const char*str; 875 876if(skip_prefix(sb.buf,"# User ", &str)) 877fprintf(out,"From:%s\n", str); 878else if(skip_prefix(sb.buf,"# Date ", &str)) { 879unsigned long timestamp; 880long tz, tz2; 881char*end; 882 883 errno =0; 884 timestamp =strtoul(str, &end,10); 885if(errno) 886returnerror(_("invalid timestamp")); 887 888if(!skip_prefix(end," ", &str)) 889returnerror(_("invalid Date line")); 890 891 errno =0; 892 tz =strtol(str, &end,10); 893if(errno) 894returnerror(_("invalid timezone offset")); 895 896if(*end) 897returnerror(_("invalid Date line")); 898 899/* 900 * mercurial's timezone is in seconds west of UTC, 901 * however git's timezone is in hours + minutes east of 902 * UTC. Convert it. 903 */ 904 tz2 =labs(tz) /3600*100+labs(tz) %3600/60; 905if(tz >0) 906 tz2 = -tz2; 907 908fprintf(out,"Date:%s\n",show_date(timestamp, tz2,DATE_MODE(RFC2822))); 909}else if(starts_with(sb.buf,"# ")) { 910continue; 911}else{ 912fprintf(out,"\n%s\n", sb.buf); 913break; 914} 915} 916 917strbuf_reset(&sb); 918while(strbuf_fread(&sb,8192, in) >0) { 919fwrite(sb.buf,1, sb.len, out); 920strbuf_reset(&sb); 921} 922 923strbuf_release(&sb); 924return0; 925} 926 927/** 928 * Splits a list of files/directories into individual email patches. Each path 929 * in `paths` must be a file/directory that is formatted according to 930 * `patch_format`. 931 * 932 * Once split out, the individual email patches will be stored in the state 933 * directory, with each patch's filename being its index, padded to state->prec 934 * digits. 935 * 936 * state->cur will be set to the index of the first mail, and state->last will 937 * be set to the index of the last mail. 938 * 939 * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1 940 * to disable this behavior, -1 to use the default configured setting. 941 * 942 * Returns 0 on success, -1 on failure. 943 */ 944static intsplit_mail(struct am_state *state,enum patch_format patch_format, 945const char**paths,int keep_cr) 946{ 947if(keep_cr <0) { 948 keep_cr =0; 949git_config_get_bool("am.keepcr", &keep_cr); 950} 951 952switch(patch_format) { 953case PATCH_FORMAT_MBOX: 954returnsplit_mail_mbox(state, paths, keep_cr,0); 955case PATCH_FORMAT_STGIT: 956returnsplit_mail_conv(stgit_patch_to_mail, state, paths, keep_cr); 957case PATCH_FORMAT_STGIT_SERIES: 958returnsplit_mail_stgit_series(state, paths, keep_cr); 959case PATCH_FORMAT_HG: 960returnsplit_mail_conv(hg_patch_to_mail, state, paths, keep_cr); 961case PATCH_FORMAT_MBOXRD: 962returnsplit_mail_mbox(state, paths, keep_cr,1); 963default: 964die("BUG: invalid patch_format"); 965} 966return-1; 967} 968 969/** 970 * Setup a new am session for applying patches 971 */ 972static voidam_setup(struct am_state *state,enum patch_format patch_format, 973const char**paths,int keep_cr) 974{ 975unsigned char curr_head[GIT_SHA1_RAWSZ]; 976const char*str; 977struct strbuf sb = STRBUF_INIT; 978 979if(!patch_format) 980 patch_format =detect_patch_format(paths); 981 982if(!patch_format) { 983fprintf_ln(stderr,_("Patch format detection failed.")); 984exit(128); 985} 986 987if(mkdir(state->dir,0777) <0&& errno != EEXIST) 988die_errno(_("failed to create directory '%s'"), state->dir); 989 990if(split_mail(state, patch_format, paths, keep_cr) <0) { 991am_destroy(state); 992die(_("Failed to split patches.")); 993} 994 995if(state->rebasing) 996 state->threeway =1; 997 998write_state_bool(state,"threeway", state->threeway); 999write_state_bool(state,"quiet", state->quiet);1000write_state_bool(state,"sign", state->signoff);1001write_state_bool(state,"utf8", state->utf8);10021003switch(state->keep) {1004case KEEP_FALSE:1005 str ="f";1006break;1007case KEEP_TRUE:1008 str ="t";1009break;1010case KEEP_NON_PATCH:1011 str ="b";1012break;1013default:1014die("BUG: invalid value for state->keep");1015}10161017write_state_text(state,"keep", str);1018write_state_bool(state,"messageid", state->message_id);10191020switch(state->scissors) {1021case SCISSORS_UNSET:1022 str ="";1023break;1024case SCISSORS_FALSE:1025 str ="f";1026break;1027case SCISSORS_TRUE:1028 str ="t";1029break;1030default:1031die("BUG: invalid value for state->scissors");1032}1033write_state_text(state,"scissors", str);10341035sq_quote_argv(&sb, state->git_apply_opts.argv,0);1036write_state_text(state,"apply-opt", sb.buf);10371038if(state->rebasing)1039write_state_text(state,"rebasing","");1040else1041write_state_text(state,"applying","");10421043if(!get_sha1("HEAD", curr_head)) {1044write_state_text(state,"abort-safety",sha1_to_hex(curr_head));1045if(!state->rebasing)1046update_ref("am","ORIG_HEAD", curr_head, NULL,0,1047 UPDATE_REFS_DIE_ON_ERR);1048}else{1049write_state_text(state,"abort-safety","");1050if(!state->rebasing)1051delete_ref("ORIG_HEAD", NULL,0);1052}10531054/*1055 * NOTE: Since the "next" and "last" files determine if an am_state1056 * session is in progress, they should be written last.1057 */10581059write_state_count(state,"next", state->cur);1060write_state_count(state,"last", state->last);10611062strbuf_release(&sb);1063}10641065/**1066 * Increments the patch pointer, and cleans am_state for the application of the1067 * next patch.1068 */1069static voidam_next(struct am_state *state)1070{1071unsigned char head[GIT_SHA1_RAWSZ];10721073free(state->author_name);1074 state->author_name = NULL;10751076free(state->author_email);1077 state->author_email = NULL;10781079free(state->author_date);1080 state->author_date = NULL;10811082free(state->msg);1083 state->msg = NULL;1084 state->msg_len =0;10851086unlink(am_path(state,"author-script"));1087unlink(am_path(state,"final-commit"));10881089hashclr(state->orig_commit);1090unlink(am_path(state,"original-commit"));10911092if(!get_sha1("HEAD", head))1093write_state_text(state,"abort-safety",sha1_to_hex(head));1094else1095write_state_text(state,"abort-safety","");10961097 state->cur++;1098write_state_count(state,"next", state->cur);1099}11001101/**1102 * Returns the filename of the current patch email.1103 */1104static const char*msgnum(const struct am_state *state)1105{1106static struct strbuf sb = STRBUF_INIT;11071108strbuf_reset(&sb);1109strbuf_addf(&sb,"%0*d", state->prec, state->cur);11101111return sb.buf;1112}11131114/**1115 * Refresh and write index.1116 */1117static voidrefresh_and_write_cache(void)1118{1119struct lock_file *lock_file =xcalloc(1,sizeof(struct lock_file));11201121hold_locked_index(lock_file,1);1122refresh_cache(REFRESH_QUIET);1123if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))1124die(_("unable to write index file"));1125}11261127/**1128 * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn1129 * branch, returns 1 if there are entries in the index, 0 otherwise. If an1130 * strbuf is provided, the space-separated list of files that differ will be1131 * appended to it.1132 */1133static intindex_has_changes(struct strbuf *sb)1134{1135unsigned char head[GIT_SHA1_RAWSZ];1136int i;11371138if(!get_sha1_tree("HEAD", head)) {1139struct diff_options opt;11401141diff_setup(&opt);1142DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);1143if(!sb)1144DIFF_OPT_SET(&opt, QUICK);1145do_diff_cache(head, &opt);1146diffcore_std(&opt);1147for(i =0; sb && i < diff_queued_diff.nr; i++) {1148if(i)1149strbuf_addch(sb,' ');1150strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);1151}1152diff_flush(&opt);1153returnDIFF_OPT_TST(&opt, HAS_CHANGES) !=0;1154}else{1155for(i =0; sb && i < active_nr; i++) {1156if(i)1157strbuf_addch(sb,' ');1158strbuf_addstr(sb, active_cache[i]->name);1159}1160return!!active_nr;1161}1162}11631164/**1165 * Dies with a user-friendly message on how to proceed after resolving the1166 * problem. This message can be overridden with state->resolvemsg.1167 */1168static void NORETURN die_user_resolve(const struct am_state *state)1169{1170if(state->resolvemsg) {1171printf_ln("%s", state->resolvemsg);1172}else{1173const char*cmdline = state->interactive ?"git am -i":"git am";11741175printf_ln(_("When you have resolved this problem, run\"%s--continue\"."), cmdline);1176printf_ln(_("If you prefer to skip this patch, run\"%s--skip\"instead."), cmdline);1177printf_ln(_("To restore the original branch and stop patching, run\"%s--abort\"."), cmdline);1178}11791180exit(128);1181}11821183static voidam_signoff(struct strbuf *sb)1184{1185char*cp;1186struct strbuf mine = STRBUF_INIT;11871188/* Does it end with our own sign-off? */1189strbuf_addf(&mine,"\n%s%s\n",1190 sign_off_header,1191fmt_name(getenv("GIT_COMMITTER_NAME"),1192getenv("GIT_COMMITTER_EMAIL")));1193if(mine.len < sb->len &&1194!strcmp(mine.buf, sb->buf + sb->len - mine.len))1195goto exit;/* no need to duplicate */11961197/* Does it have any Signed-off-by: in the text */1198for(cp = sb->buf;1199 cp && *cp && (cp =strstr(cp, sign_off_header)) != NULL;1200 cp =strchr(cp,'\n')) {1201if(sb->buf == cp || cp[-1] =='\n')1202break;1203}12041205strbuf_addstr(sb, mine.buf + !!cp);1206exit:1207strbuf_release(&mine);1208}12091210/**1211 * Appends signoff to the "msg" field of the am_state.1212 */1213static voidam_append_signoff(struct am_state *state)1214{1215struct strbuf sb = STRBUF_INIT;12161217strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);1218am_signoff(&sb);1219 state->msg =strbuf_detach(&sb, &state->msg_len);1220}12211222/**1223 * Parses `mail` using git-mailinfo, extracting its patch and authorship info.1224 * state->msg will be set to the patch message. state->author_name,1225 * state->author_email and state->author_date will be set to the patch author's1226 * name, email and date respectively. The patch body will be written to the1227 * state directory's "patch" file.1228 *1229 * Returns 1 if the patch should be skipped, 0 otherwise.1230 */1231static intparse_mail(struct am_state *state,const char*mail)1232{1233FILE*fp;1234struct strbuf sb = STRBUF_INIT;1235struct strbuf msg = STRBUF_INIT;1236struct strbuf author_name = STRBUF_INIT;1237struct strbuf author_date = STRBUF_INIT;1238struct strbuf author_email = STRBUF_INIT;1239int ret =0;1240struct mailinfo mi;12411242setup_mailinfo(&mi);12431244if(state->utf8)1245 mi.metainfo_charset =get_commit_output_encoding();1246else1247 mi.metainfo_charset = NULL;12481249switch(state->keep) {1250case KEEP_FALSE:1251break;1252case KEEP_TRUE:1253 mi.keep_subject =1;1254break;1255case KEEP_NON_PATCH:1256 mi.keep_non_patch_brackets_in_subject =1;1257break;1258default:1259die("BUG: invalid value for state->keep");1260}12611262if(state->message_id)1263 mi.add_message_id =1;12641265switch(state->scissors) {1266case SCISSORS_UNSET:1267break;1268case SCISSORS_FALSE:1269 mi.use_scissors =0;1270break;1271case SCISSORS_TRUE:1272 mi.use_scissors =1;1273break;1274default:1275die("BUG: invalid value for state->scissors");1276}12771278 mi.input =fopen(mail,"r");1279if(!mi.input)1280die("could not open input");1281 mi.output =fopen(am_path(state,"info"),"w");1282if(!mi.output)1283die("could not open output 'info'");1284if(mailinfo(&mi,am_path(state,"msg"),am_path(state,"patch")))1285die("could not parse patch");12861287fclose(mi.input);1288fclose(mi.output);12891290/* Extract message and author information */1291 fp =xfopen(am_path(state,"info"),"r");1292while(!strbuf_getline_lf(&sb, fp)) {1293const char*x;12941295if(skip_prefix(sb.buf,"Subject: ", &x)) {1296if(msg.len)1297strbuf_addch(&msg,'\n');1298strbuf_addstr(&msg, x);1299}else if(skip_prefix(sb.buf,"Author: ", &x))1300strbuf_addstr(&author_name, x);1301else if(skip_prefix(sb.buf,"Email: ", &x))1302strbuf_addstr(&author_email, x);1303else if(skip_prefix(sb.buf,"Date: ", &x))1304strbuf_addstr(&author_date, x);1305}1306fclose(fp);13071308/* Skip pine's internal folder data */1309if(!strcmp(author_name.buf,"Mail System Internal Data")) {1310 ret =1;1311goto finish;1312}13131314if(is_empty_file(am_path(state,"patch"))) {1315printf_ln(_("Patch is empty. Was it split wrong?"));1316die_user_resolve(state);1317}13181319strbuf_addstr(&msg,"\n\n");1320strbuf_addbuf(&msg, &mi.log_message);1321strbuf_stripspace(&msg,0);13221323if(state->signoff)1324am_signoff(&msg);13251326assert(!state->author_name);1327 state->author_name =strbuf_detach(&author_name, NULL);13281329assert(!state->author_email);1330 state->author_email =strbuf_detach(&author_email, NULL);13311332assert(!state->author_date);1333 state->author_date =strbuf_detach(&author_date, NULL);13341335assert(!state->msg);1336 state->msg =strbuf_detach(&msg, &state->msg_len);13371338finish:1339strbuf_release(&msg);1340strbuf_release(&author_date);1341strbuf_release(&author_email);1342strbuf_release(&author_name);1343strbuf_release(&sb);1344clear_mailinfo(&mi);1345return ret;1346}13471348/**1349 * Sets commit_id to the commit hash where the mail was generated from.1350 * Returns 0 on success, -1 on failure.1351 */1352static intget_mail_commit_sha1(unsigned char*commit_id,const char*mail)1353{1354struct strbuf sb = STRBUF_INIT;1355FILE*fp =xfopen(mail,"r");1356const char*x;13571358if(strbuf_getline_lf(&sb, fp))1359return-1;13601361if(!skip_prefix(sb.buf,"From ", &x))1362return-1;13631364if(get_sha1_hex(x, commit_id) <0)1365return-1;13661367strbuf_release(&sb);1368fclose(fp);1369return0;1370}13711372/**1373 * Sets state->msg, state->author_name, state->author_email, state->author_date1374 * to the commit's respective info.1375 */1376static voidget_commit_info(struct am_state *state,struct commit *commit)1377{1378const char*buffer, *ident_line, *author_date, *msg;1379size_t ident_len;1380struct ident_split ident_split;1381struct strbuf sb = STRBUF_INIT;13821383 buffer =logmsg_reencode(commit, NULL,get_commit_output_encoding());13841385 ident_line =find_commit_header(buffer,"author", &ident_len);13861387if(split_ident_line(&ident_split, ident_line, ident_len) <0) {1388strbuf_add(&sb, ident_line, ident_len);1389die(_("invalid ident line:%s"), sb.buf);1390}13911392assert(!state->author_name);1393if(ident_split.name_begin) {1394strbuf_add(&sb, ident_split.name_begin,1395 ident_split.name_end - ident_split.name_begin);1396 state->author_name =strbuf_detach(&sb, NULL);1397}else1398 state->author_name =xstrdup("");13991400assert(!state->author_email);1401if(ident_split.mail_begin) {1402strbuf_add(&sb, ident_split.mail_begin,1403 ident_split.mail_end - ident_split.mail_begin);1404 state->author_email =strbuf_detach(&sb, NULL);1405}else1406 state->author_email =xstrdup("");14071408 author_date =show_ident_date(&ident_split,DATE_MODE(NORMAL));1409strbuf_addstr(&sb, author_date);1410assert(!state->author_date);1411 state->author_date =strbuf_detach(&sb, NULL);14121413assert(!state->msg);1414 msg =strstr(buffer,"\n\n");1415if(!msg)1416die(_("unable to parse commit%s"),oid_to_hex(&commit->object.oid));1417 state->msg =xstrdup(msg +2);1418 state->msg_len =strlen(state->msg);1419}14201421/**1422 * Writes `commit` as a patch to the state directory's "patch" file.1423 */1424static voidwrite_commit_patch(const struct am_state *state,struct commit *commit)1425{1426struct rev_info rev_info;1427FILE*fp;14281429 fp =xfopen(am_path(state,"patch"),"w");1430init_revisions(&rev_info, NULL);1431 rev_info.diff =1;1432 rev_info.abbrev =0;1433 rev_info.disable_stdin =1;1434 rev_info.show_root_diff =1;1435 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;1436 rev_info.no_commit_id =1;1437DIFF_OPT_SET(&rev_info.diffopt, BINARY);1438DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);1439 rev_info.diffopt.use_color =0;1440 rev_info.diffopt.file = fp;1441 rev_info.diffopt.close_file =1;1442add_pending_object(&rev_info, &commit->object,"");1443diff_setup_done(&rev_info.diffopt);1444log_tree_commit(&rev_info, commit);1445}14461447/**1448 * Writes the diff of the index against HEAD as a patch to the state1449 * directory's "patch" file.1450 */1451static voidwrite_index_patch(const struct am_state *state)1452{1453struct tree *tree;1454unsigned char head[GIT_SHA1_RAWSZ];1455struct rev_info rev_info;1456FILE*fp;14571458if(!get_sha1_tree("HEAD", head))1459 tree =lookup_tree(head);1460else1461 tree =lookup_tree(EMPTY_TREE_SHA1_BIN);14621463 fp =xfopen(am_path(state,"patch"),"w");1464init_revisions(&rev_info, NULL);1465 rev_info.diff =1;1466 rev_info.disable_stdin =1;1467 rev_info.no_commit_id =1;1468 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;1469 rev_info.diffopt.use_color =0;1470 rev_info.diffopt.file = fp;1471 rev_info.diffopt.close_file =1;1472add_pending_object(&rev_info, &tree->object,"");1473diff_setup_done(&rev_info.diffopt);1474run_diff_index(&rev_info,1);1475}14761477/**1478 * Like parse_mail(), but parses the mail by looking up its commit ID1479 * directly. This is used in --rebasing mode to bypass git-mailinfo's munging1480 * of patches.1481 *1482 * state->orig_commit will be set to the original commit ID.1483 *1484 * Will always return 0 as the patch should never be skipped.1485 */1486static intparse_mail_rebase(struct am_state *state,const char*mail)1487{1488struct commit *commit;1489unsigned char commit_sha1[GIT_SHA1_RAWSZ];14901491if(get_mail_commit_sha1(commit_sha1, mail) <0)1492die(_("could not parse%s"), mail);14931494 commit =lookup_commit_or_die(commit_sha1, mail);14951496get_commit_info(state, commit);14971498write_commit_patch(state, commit);14991500hashcpy(state->orig_commit, commit_sha1);1501write_state_text(state,"original-commit",sha1_to_hex(commit_sha1));15021503return0;1504}15051506/**1507 * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If1508 * `index_file` is not NULL, the patch will be applied to that index.1509 */1510static intrun_apply(const struct am_state *state,const char*index_file)1511{1512struct child_process cp = CHILD_PROCESS_INIT;15131514 cp.git_cmd =1;15151516if(index_file)1517argv_array_pushf(&cp.env_array,"GIT_INDEX_FILE=%s", index_file);15181519/*1520 * If we are allowed to fall back on 3-way merge, don't give false1521 * errors during the initial attempt.1522 */1523if(state->threeway && !index_file) {1524 cp.no_stdout =1;1525 cp.no_stderr =1;1526}15271528argv_array_push(&cp.args,"apply");15291530argv_array_pushv(&cp.args, state->git_apply_opts.argv);15311532if(index_file)1533argv_array_push(&cp.args,"--cached");1534else1535argv_array_push(&cp.args,"--index");15361537argv_array_push(&cp.args,am_path(state,"patch"));15381539if(run_command(&cp))1540return-1;15411542/* Reload index as git-apply will have modified it. */1543discard_cache();1544read_cache_from(index_file ? index_file :get_index_file());15451546return0;1547}15481549/**1550 * Builds an index that contains just the blobs needed for a 3way merge.1551 */1552static intbuild_fake_ancestor(const struct am_state *state,const char*index_file)1553{1554struct child_process cp = CHILD_PROCESS_INIT;15551556 cp.git_cmd =1;1557argv_array_push(&cp.args,"apply");1558argv_array_pushv(&cp.args, state->git_apply_opts.argv);1559argv_array_pushf(&cp.args,"--build-fake-ancestor=%s", index_file);1560argv_array_push(&cp.args,am_path(state,"patch"));15611562if(run_command(&cp))1563return-1;15641565return0;1566}15671568/**1569 * Attempt a threeway merge, using index_path as the temporary index.1570 */1571static intfall_back_threeway(const struct am_state *state,const char*index_path)1572{1573struct object_id orig_tree, their_tree, our_tree;1574const struct object_id *bases[1] = { &orig_tree };1575struct merge_options o;1576struct commit *result;1577char*their_tree_name;15781579if(get_oid("HEAD", &our_tree) <0)1580hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);15811582if(build_fake_ancestor(state, index_path))1583returnerror("could not build fake ancestor");15841585discard_cache();1586read_cache_from(index_path);15871588if(write_index_as_tree(orig_tree.hash, &the_index, index_path,0, NULL))1589returnerror(_("Repository lacks necessary blobs to fall back on 3-way merge."));15901591say(state, stdout,_("Using index info to reconstruct a base tree..."));15921593if(!state->quiet) {1594/*1595 * List paths that needed 3-way fallback, so that the user can1596 * review them with extra care to spot mismerges.1597 */1598struct rev_info rev_info;1599const char*diff_filter_str ="--diff-filter=AM";16001601init_revisions(&rev_info, NULL);1602 rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;1603diff_opt_parse(&rev_info.diffopt, &diff_filter_str,1, rev_info.prefix);1604add_pending_sha1(&rev_info,"HEAD", our_tree.hash,0);1605diff_setup_done(&rev_info.diffopt);1606run_diff_index(&rev_info,1);1607}16081609if(run_apply(state, index_path))1610returnerror(_("Did you hand edit your patch?\n"1611"It does not apply to blobs recorded in its index."));16121613if(write_index_as_tree(their_tree.hash, &the_index, index_path,0, NULL))1614returnerror("could not write tree");16151616say(state, stdout,_("Falling back to patching base and 3-way merge..."));16171618discard_cache();1619read_cache();16201621/*1622 * This is not so wrong. Depending on which base we picked, orig_tree1623 * may be wildly different from ours, but their_tree has the same set of1624 * wildly different changes in parts the patch did not touch, so1625 * recursive ends up canceling them, saying that we reverted all those1626 * changes.1627 */16281629init_merge_options(&o);16301631 o.branch1 ="HEAD";1632 their_tree_name =xstrfmt("%.*s",linelen(state->msg), state->msg);1633 o.branch2 = their_tree_name;16341635if(state->quiet)1636 o.verbosity =0;16371638if(merge_recursive_generic(&o, &our_tree, &their_tree,1, bases, &result)) {1639rerere(state->allow_rerere_autoupdate);1640free(their_tree_name);1641returnerror(_("Failed to merge in the changes."));1642}16431644free(their_tree_name);1645return0;1646}16471648/**1649 * Commits the current index with state->msg as the commit message and1650 * state->author_name, state->author_email and state->author_date as the author1651 * information.1652 */1653static voiddo_commit(const struct am_state *state)1654{1655unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],1656 commit[GIT_SHA1_RAWSZ];1657unsigned char*ptr;1658struct commit_list *parents = NULL;1659const char*reflog_msg, *author;1660struct strbuf sb = STRBUF_INIT;16611662if(run_hook_le(NULL,"pre-applypatch", NULL))1663exit(1);16641665if(write_cache_as_tree(tree,0, NULL))1666die(_("git write-tree failed to write a tree"));16671668if(!get_sha1_commit("HEAD", parent)) {1669 ptr = parent;1670commit_list_insert(lookup_commit(parent), &parents);1671}else{1672 ptr = NULL;1673say(state, stderr,_("applying to an empty history"));1674}16751676 author =fmt_ident(state->author_name, state->author_email,1677 state->ignore_date ? NULL : state->author_date,1678 IDENT_STRICT);16791680if(state->committer_date_is_author_date)1681setenv("GIT_COMMITTER_DATE",1682 state->ignore_date ?"": state->author_date,1);16831684if(commit_tree(state->msg, state->msg_len, tree, parents, commit,1685 author, state->sign_commit))1686die(_("failed to write commit object"));16871688 reflog_msg =getenv("GIT_REFLOG_ACTION");1689if(!reflog_msg)1690 reflog_msg ="am";16911692strbuf_addf(&sb,"%s: %.*s", reflog_msg,linelen(state->msg),1693 state->msg);16941695update_ref(sb.buf,"HEAD", commit, ptr,0, UPDATE_REFS_DIE_ON_ERR);16961697if(state->rebasing) {1698FILE*fp =xfopen(am_path(state,"rewritten"),"a");16991700assert(!is_null_sha1(state->orig_commit));1701fprintf(fp,"%s",sha1_to_hex(state->orig_commit));1702fprintf(fp,"%s\n",sha1_to_hex(commit));1703fclose(fp);1704}17051706run_hook_le(NULL,"post-applypatch", NULL);17071708strbuf_release(&sb);1709}17101711/**1712 * Validates the am_state for resuming -- the "msg" and authorship fields must1713 * be filled up.1714 */1715static voidvalidate_resume_state(const struct am_state *state)1716{1717if(!state->msg)1718die(_("cannot resume:%sdoes not exist."),1719am_path(state,"final-commit"));17201721if(!state->author_name || !state->author_email || !state->author_date)1722die(_("cannot resume:%sdoes not exist."),1723am_path(state,"author-script"));1724}17251726/**1727 * Interactively prompt the user on whether the current patch should be1728 * applied.1729 *1730 * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to1731 * skip it.1732 */1733static intdo_interactive(struct am_state *state)1734{1735assert(state->msg);17361737if(!isatty(0))1738die(_("cannot be interactive without stdin connected to a terminal."));17391740for(;;) {1741const char*reply;17421743puts(_("Commit Body is:"));1744puts("--------------------------");1745printf("%s", state->msg);1746puts("--------------------------");17471748/*1749 * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]1750 * in your translation. The program will only accept English1751 * input at this point.1752 */1753 reply =git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);17541755if(!reply) {1756continue;1757}else if(*reply =='y'|| *reply =='Y') {1758return0;1759}else if(*reply =='a'|| *reply =='A') {1760 state->interactive =0;1761return0;1762}else if(*reply =='n'|| *reply =='N') {1763return1;1764}else if(*reply =='e'|| *reply =='E') {1765struct strbuf msg = STRBUF_INIT;17661767if(!launch_editor(am_path(state,"final-commit"), &msg, NULL)) {1768free(state->msg);1769 state->msg =strbuf_detach(&msg, &state->msg_len);1770}1771strbuf_release(&msg);1772}else if(*reply =='v'|| *reply =='V') {1773const char*pager =git_pager(1);1774struct child_process cp = CHILD_PROCESS_INIT;17751776if(!pager)1777 pager ="cat";1778prepare_pager_args(&cp, pager);1779argv_array_push(&cp.args,am_path(state,"patch"));1780run_command(&cp);1781}1782}1783}17841785/**1786 * Applies all queued mail.1787 *1788 * If `resume` is true, we are "resuming". The "msg" and authorship fields, as1789 * well as the state directory's "patch" file is used as-is for applying the1790 * patch and committing it.1791 */1792static voidam_run(struct am_state *state,int resume)1793{1794const char*argv_gc_auto[] = {"gc","--auto", NULL};1795struct strbuf sb = STRBUF_INIT;17961797unlink(am_path(state,"dirtyindex"));17981799refresh_and_write_cache();18001801if(index_has_changes(&sb)) {1802write_state_bool(state,"dirtyindex",1);1803die(_("Dirty index: cannot apply patches (dirty:%s)"), sb.buf);1804}18051806strbuf_release(&sb);18071808while(state->cur <= state->last) {1809const char*mail =am_path(state,msgnum(state));1810int apply_status;18111812reset_ident_date();18131814if(!file_exists(mail))1815goto next;18161817if(resume) {1818validate_resume_state(state);1819}else{1820int skip;18211822if(state->rebasing)1823 skip =parse_mail_rebase(state, mail);1824else1825 skip =parse_mail(state, mail);18261827if(skip)1828goto next;/* mail should be skipped */18291830write_author_script(state);1831write_commit_msg(state);1832}18331834if(state->interactive &&do_interactive(state))1835goto next;18361837if(run_applypatch_msg_hook(state))1838exit(1);18391840say(state, stdout,_("Applying: %.*s"),linelen(state->msg), state->msg);18411842 apply_status =run_apply(state, NULL);18431844if(apply_status && state->threeway) {1845struct strbuf sb = STRBUF_INIT;18461847strbuf_addstr(&sb,am_path(state,"patch-merge-index"));1848 apply_status =fall_back_threeway(state, sb.buf);1849strbuf_release(&sb);18501851/*1852 * Applying the patch to an earlier tree and merging1853 * the result may have produced the same tree as ours.1854 */1855if(!apply_status && !index_has_changes(NULL)) {1856say(state, stdout,_("No changes -- Patch already applied."));1857goto next;1858}1859}18601861if(apply_status) {1862int advice_amworkdir =1;18631864printf_ln(_("Patch failed at%s%.*s"),msgnum(state),1865linelen(state->msg), state->msg);18661867git_config_get_bool("advice.amworkdir", &advice_amworkdir);18681869if(advice_amworkdir)1870printf_ln(_("The copy of the patch that failed is found in:%s"),1871am_path(state,"patch"));18721873die_user_resolve(state);1874}18751876do_commit(state);18771878next:1879am_next(state);18801881if(resume)1882am_load(state);1883 resume =0;1884}18851886if(!is_empty_file(am_path(state,"rewritten"))) {1887assert(state->rebasing);1888copy_notes_for_rebase(state);1889run_post_rewrite_hook(state);1890}18911892/*1893 * In rebasing mode, it's up to the caller to take care of1894 * housekeeping.1895 */1896if(!state->rebasing) {1897am_destroy(state);1898close_all_packs();1899run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);1900}1901}19021903/**1904 * Resume the current am session after patch application failure. The user did1905 * all the hard work, and we do not have to do any patch application. Just1906 * trust and commit what the user has in the index and working tree.1907 */1908static voidam_resolve(struct am_state *state)1909{1910validate_resume_state(state);19111912say(state, stdout,_("Applying: %.*s"),linelen(state->msg), state->msg);19131914if(!index_has_changes(NULL)) {1915printf_ln(_("No changes - did you forget to use 'git add'?\n"1916"If there is nothing left to stage, chances are that something else\n"1917"already introduced the same changes; you might want to skip this patch."));1918die_user_resolve(state);1919}19201921if(unmerged_cache()) {1922printf_ln(_("You still have unmerged paths in your index.\n"1923"Did you forget to use 'git add'?"));1924die_user_resolve(state);1925}19261927if(state->interactive) {1928write_index_patch(state);1929if(do_interactive(state))1930goto next;1931}19321933rerere(0);19341935do_commit(state);19361937next:1938am_next(state);1939am_load(state);1940am_run(state,0);1941}19421943/**1944 * Performs a checkout fast-forward from `head` to `remote`. If `reset` is1945 * true, any unmerged entries will be discarded. Returns 0 on success, -1 on1946 * failure.1947 */1948static intfast_forward_to(struct tree *head,struct tree *remote,int reset)1949{1950struct lock_file *lock_file;1951struct unpack_trees_options opts;1952struct tree_desc t[2];19531954if(parse_tree(head) ||parse_tree(remote))1955return-1;19561957 lock_file =xcalloc(1,sizeof(struct lock_file));1958hold_locked_index(lock_file,1);19591960refresh_cache(REFRESH_QUIET);19611962memset(&opts,0,sizeof(opts));1963 opts.head_idx =1;1964 opts.src_index = &the_index;1965 opts.dst_index = &the_index;1966 opts.update =1;1967 opts.merge =1;1968 opts.reset = reset;1969 opts.fn = twoway_merge;1970init_tree_desc(&t[0], head->buffer, head->size);1971init_tree_desc(&t[1], remote->buffer, remote->size);19721973if(unpack_trees(2, t, &opts)) {1974rollback_lock_file(lock_file);1975return-1;1976}19771978if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))1979die(_("unable to write new index file"));19801981return0;1982}19831984/**1985 * Merges a tree into the index. The index's stat info will take precedence1986 * over the merged tree's. Returns 0 on success, -1 on failure.1987 */1988static intmerge_tree(struct tree *tree)1989{1990struct lock_file *lock_file;1991struct unpack_trees_options opts;1992struct tree_desc t[1];19931994if(parse_tree(tree))1995return-1;19961997 lock_file =xcalloc(1,sizeof(struct lock_file));1998hold_locked_index(lock_file,1);19992000memset(&opts,0,sizeof(opts));2001 opts.head_idx =1;2002 opts.src_index = &the_index;2003 opts.dst_index = &the_index;2004 opts.merge =1;2005 opts.fn = oneway_merge;2006init_tree_desc(&t[0], tree->buffer, tree->size);20072008if(unpack_trees(1, t, &opts)) {2009rollback_lock_file(lock_file);2010return-1;2011}20122013if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))2014die(_("unable to write new index file"));20152016return0;2017}20182019/**2020 * Clean the index without touching entries that are not modified between2021 * `head` and `remote`.2022 */2023static intclean_index(const unsigned char*head,const unsigned char*remote)2024{2025struct tree *head_tree, *remote_tree, *index_tree;2026unsigned char index[GIT_SHA1_RAWSZ];20272028 head_tree =parse_tree_indirect(head);2029if(!head_tree)2030returnerror(_("Could not parse object '%s'."),sha1_to_hex(head));20312032 remote_tree =parse_tree_indirect(remote);2033if(!remote_tree)2034returnerror(_("Could not parse object '%s'."),sha1_to_hex(remote));20352036read_cache_unmerged();20372038if(fast_forward_to(head_tree, head_tree,1))2039return-1;20402041if(write_cache_as_tree(index,0, NULL))2042return-1;20432044 index_tree =parse_tree_indirect(index);2045if(!index_tree)2046returnerror(_("Could not parse object '%s'."),sha1_to_hex(index));20472048if(fast_forward_to(index_tree, remote_tree,0))2049return-1;20502051if(merge_tree(remote_tree))2052return-1;20532054remove_branch_state();20552056return0;2057}20582059/**2060 * Resets rerere's merge resolution metadata.2061 */2062static voidam_rerere_clear(void)2063{2064struct string_list merge_rr = STRING_LIST_INIT_DUP;2065rerere_clear(&merge_rr);2066string_list_clear(&merge_rr,1);2067}20682069/**2070 * Resume the current am session by skipping the current patch.2071 */2072static voidam_skip(struct am_state *state)2073{2074unsigned char head[GIT_SHA1_RAWSZ];20752076am_rerere_clear();20772078if(get_sha1("HEAD", head))2079hashcpy(head, EMPTY_TREE_SHA1_BIN);20802081if(clean_index(head, head))2082die(_("failed to clean index"));20832084am_next(state);2085am_load(state);2086am_run(state,0);2087}20882089/**2090 * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.2091 *2092 * It is not safe to reset HEAD when:2093 * 1. git-am previously failed because the index was dirty.2094 * 2. HEAD has moved since git-am previously failed.2095 */2096static intsafe_to_abort(const struct am_state *state)2097{2098struct strbuf sb = STRBUF_INIT;2099unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];21002101if(file_exists(am_path(state,"dirtyindex")))2102return0;21032104if(read_state_file(&sb, state,"abort-safety",1) >0) {2105if(get_sha1_hex(sb.buf, abort_safety))2106die(_("could not parse%s"),am_path(state,"abort_safety"));2107}else2108hashclr(abort_safety);21092110if(get_sha1("HEAD", head))2111hashclr(head);21122113if(!hashcmp(head, abort_safety))2114return1;21152116error(_("You seem to have moved HEAD since the last 'am' failure.\n"2117"Not rewinding to ORIG_HEAD"));21182119return0;2120}21212122/**2123 * Aborts the current am session if it is safe to do so.2124 */2125static voidam_abort(struct am_state *state)2126{2127unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];2128int has_curr_head, has_orig_head;2129char*curr_branch;21302131if(!safe_to_abort(state)) {2132am_destroy(state);2133return;2134}21352136am_rerere_clear();21372138 curr_branch =resolve_refdup("HEAD",0, curr_head, NULL);2139 has_curr_head = !is_null_sha1(curr_head);2140if(!has_curr_head)2141hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);21422143 has_orig_head = !get_sha1("ORIG_HEAD", orig_head);2144if(!has_orig_head)2145hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);21462147clean_index(curr_head, orig_head);21482149if(has_orig_head)2150update_ref("am --abort","HEAD", orig_head,2151 has_curr_head ? curr_head : NULL,0,2152 UPDATE_REFS_DIE_ON_ERR);2153else if(curr_branch)2154delete_ref(curr_branch, NULL, REF_NODEREF);21552156free(curr_branch);2157am_destroy(state);2158}21592160/**2161 * parse_options() callback that validates and sets opt->value to the2162 * PATCH_FORMAT_* enum value corresponding to `arg`.2163 */2164static intparse_opt_patchformat(const struct option *opt,const char*arg,int unset)2165{2166int*opt_value = opt->value;21672168if(!strcmp(arg,"mbox"))2169*opt_value = PATCH_FORMAT_MBOX;2170else if(!strcmp(arg,"stgit"))2171*opt_value = PATCH_FORMAT_STGIT;2172else if(!strcmp(arg,"stgit-series"))2173*opt_value = PATCH_FORMAT_STGIT_SERIES;2174else if(!strcmp(arg,"hg"))2175*opt_value = PATCH_FORMAT_HG;2176else if(!strcmp(arg,"mboxrd"))2177*opt_value = PATCH_FORMAT_MBOXRD;2178else2179returnerror(_("Invalid value for --patch-format:%s"), arg);2180return0;2181}21822183enum resume_mode {2184 RESUME_FALSE =0,2185 RESUME_APPLY,2186 RESUME_RESOLVED,2187 RESUME_SKIP,2188 RESUME_ABORT2189};21902191static intgit_am_config(const char*k,const char*v,void*cb)2192{2193int status;21942195 status =git_gpg_config(k, v, NULL);2196if(status)2197return status;21982199returngit_default_config(k, v, NULL);2200}22012202intcmd_am(int argc,const char**argv,const char*prefix)2203{2204struct am_state state;2205int binary = -1;2206int keep_cr = -1;2207int patch_format = PATCH_FORMAT_UNKNOWN;2208enum resume_mode resume = RESUME_FALSE;2209int in_progress;22102211const char*const usage[] = {2212N_("git am [<options>] [(<mbox>|<Maildir>)...]"),2213N_("git am [<options>] (--continue | --skip | --abort)"),2214 NULL2215};22162217struct option options[] = {2218OPT_BOOL('i',"interactive", &state.interactive,2219N_("run interactively")),2220OPT_HIDDEN_BOOL('b',"binary", &binary,2221N_("historical option -- no-op")),2222OPT_BOOL('3',"3way", &state.threeway,2223N_("allow fall back on 3way merging if needed")),2224OPT__QUIET(&state.quiet,N_("be quiet")),2225OPT_SET_INT('s',"signoff", &state.signoff,2226N_("add a Signed-off-by line to the commit message"),2227 SIGNOFF_EXPLICIT),2228OPT_BOOL('u',"utf8", &state.utf8,2229N_("recode into utf8 (default)")),2230OPT_SET_INT('k',"keep", &state.keep,2231N_("pass -k flag to git-mailinfo"), KEEP_TRUE),2232OPT_SET_INT(0,"keep-non-patch", &state.keep,2233N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),2234OPT_BOOL('m',"message-id", &state.message_id,2235N_("pass -m flag to git-mailinfo")),2236{ OPTION_SET_INT,0,"keep-cr", &keep_cr, NULL,2237N_("pass --keep-cr flag to git-mailsplit for mbox format"),2238 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL,1},2239{ OPTION_SET_INT,0,"no-keep-cr", &keep_cr, NULL,2240N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),2241 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL,0},2242OPT_BOOL('c',"scissors", &state.scissors,2243N_("strip everything before a scissors line")),2244OPT_PASSTHRU_ARGV(0,"whitespace", &state.git_apply_opts,N_("action"),2245N_("pass it through git-apply"),22460),2247OPT_PASSTHRU_ARGV(0,"ignore-space-change", &state.git_apply_opts, NULL,2248N_("pass it through git-apply"),2249 PARSE_OPT_NOARG),2250OPT_PASSTHRU_ARGV(0,"ignore-whitespace", &state.git_apply_opts, NULL,2251N_("pass it through git-apply"),2252 PARSE_OPT_NOARG),2253OPT_PASSTHRU_ARGV(0,"directory", &state.git_apply_opts,N_("root"),2254N_("pass it through git-apply"),22550),2256OPT_PASSTHRU_ARGV(0,"exclude", &state.git_apply_opts,N_("path"),2257N_("pass it through git-apply"),22580),2259OPT_PASSTHRU_ARGV(0,"include", &state.git_apply_opts,N_("path"),2260N_("pass it through git-apply"),22610),2262OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts,N_("n"),2263N_("pass it through git-apply"),22640),2265OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts,N_("num"),2266N_("pass it through git-apply"),22670),2268OPT_CALLBACK(0,"patch-format", &patch_format,N_("format"),2269N_("format the patch(es) are in"),2270 parse_opt_patchformat),2271OPT_PASSTHRU_ARGV(0,"reject", &state.git_apply_opts, NULL,2272N_("pass it through git-apply"),2273 PARSE_OPT_NOARG),2274OPT_STRING(0,"resolvemsg", &state.resolvemsg, NULL,2275N_("override error message when patch failure occurs")),2276OPT_CMDMODE(0,"continue", &resume,2277N_("continue applying patches after resolving a conflict"),2278 RESUME_RESOLVED),2279OPT_CMDMODE('r',"resolved", &resume,2280N_("synonyms for --continue"),2281 RESUME_RESOLVED),2282OPT_CMDMODE(0,"skip", &resume,2283N_("skip the current patch"),2284 RESUME_SKIP),2285OPT_CMDMODE(0,"abort", &resume,2286N_("restore the original branch and abort the patching operation."),2287 RESUME_ABORT),2288OPT_BOOL(0,"committer-date-is-author-date",2289&state.committer_date_is_author_date,2290N_("lie about committer date")),2291OPT_BOOL(0,"ignore-date", &state.ignore_date,2292N_("use current timestamp for author date")),2293OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),2294{ OPTION_STRING,'S',"gpg-sign", &state.sign_commit,N_("key-id"),2295N_("GPG-sign commits"),2296 PARSE_OPT_OPTARG, NULL, (intptr_t)""},2297OPT_HIDDEN_BOOL(0,"rebasing", &state.rebasing,2298N_("(internal use for git-rebase)")),2299OPT_END()2300};23012302git_config(git_am_config, NULL);23032304am_state_init(&state,git_path("rebase-apply"));23052306 in_progress =am_in_progress(&state);2307if(in_progress)2308am_load(&state);23092310 argc =parse_options(argc, argv, prefix, options, usage,0);23112312if(binary >=0)2313fprintf_ln(stderr,_("The -b/--binary option has been a no-op for long time, and\n"2314"it will be removed. Please do not use it anymore."));23152316/* Ensure a valid committer ident can be constructed */2317git_committer_info(IDENT_STRICT);23182319if(read_index_preload(&the_index, NULL) <0)2320die(_("failed to read the index"));23212322if(in_progress) {2323/*2324 * Catch user error to feed us patches when there is a session2325 * in progress:2326 *2327 * 1. mbox path(s) are provided on the command-line.2328 * 2. stdin is not a tty: the user is trying to feed us a patch2329 * from standard input. This is somewhat unreliable -- stdin2330 * could be /dev/null for example and the caller did not2331 * intend to feed us a patch but wanted to continue2332 * unattended.2333 */2334if(argc || (resume == RESUME_FALSE && !isatty(0)))2335die(_("previous rebase directory%sstill exists but mbox given."),2336 state.dir);23372338if(resume == RESUME_FALSE)2339 resume = RESUME_APPLY;23402341if(state.signoff == SIGNOFF_EXPLICIT)2342am_append_signoff(&state);2343}else{2344struct argv_array paths = ARGV_ARRAY_INIT;2345int i;23462347/*2348 * Handle stray state directory in the independent-run case. In2349 * the --rebasing case, it is up to the caller to take care of2350 * stray directories.2351 */2352if(file_exists(state.dir) && !state.rebasing) {2353if(resume == RESUME_ABORT) {2354am_destroy(&state);2355am_state_release(&state);2356return0;2357}23582359die(_("Stray%sdirectory found.\n"2360"Use\"git am --abort\"to remove it."),2361 state.dir);2362}23632364if(resume)2365die(_("Resolve operation not in progress, we are not resuming."));23662367for(i =0; i < argc; i++) {2368if(is_absolute_path(argv[i]) || !prefix)2369argv_array_push(&paths, argv[i]);2370else2371argv_array_push(&paths,mkpath("%s/%s", prefix, argv[i]));2372}23732374am_setup(&state, patch_format, paths.argv, keep_cr);23752376argv_array_clear(&paths);2377}23782379switch(resume) {2380case RESUME_FALSE:2381am_run(&state,0);2382break;2383case RESUME_APPLY:2384am_run(&state,1);2385break;2386case RESUME_RESOLVED:2387am_resolve(&state);2388break;2389case RESUME_SKIP:2390am_skip(&state);2391break;2392case RESUME_ABORT:2393am_abort(&state);2394break;2395default:2396die("BUG: invalid resume value");2397}23982399am_state_release(&state);24002401return0;2402}