1/* 2 * Builtin "git am" 3 * 4 * Based on git-am.sh by Junio C Hamano. 5 */ 6#include"cache.h" 7#include"config.h" 8#include"builtin.h" 9#include"exec_cmd.h" 10#include"parse-options.h" 11#include"dir.h" 12#include"run-command.h" 13#include"quote.h" 14#include"tempfile.h" 15#include"lockfile.h" 16#include"cache-tree.h" 17#include"refs.h" 18#include"commit.h" 19#include"diff.h" 20#include"diffcore.h" 21#include"unpack-trees.h" 22#include"branch.h" 23#include"sequencer.h" 24#include"revision.h" 25#include"merge-recursive.h" 26#include"revision.h" 27#include"log-tree.h" 28#include"notes-utils.h" 29#include"rerere.h" 30#include"prompt.h" 31#include"mailinfo.h" 32#include"apply.h" 33#include"string-list.h" 34 35/** 36 * Returns 1 if the file is empty or does not exist, 0 otherwise. 37 */ 38static intis_empty_file(const char*filename) 39{ 40struct stat st; 41 42if(stat(filename, &st) <0) { 43if(errno == ENOENT) 44return1; 45die_errno(_("could not stat%s"), filename); 46} 47 48return!st.st_size; 49} 50 51/** 52 * Returns the length of the first line of msg. 53 */ 54static intlinelen(const char*msg) 55{ 56returnstrchrnul(msg,'\n') - msg; 57} 58 59/** 60 * Returns true if `str` consists of only whitespace, false otherwise. 61 */ 62static intstr_isspace(const char*str) 63{ 64for(; *str; str++) 65if(!isspace(*str)) 66return0; 67 68return1; 69} 70 71enum patch_format { 72 PATCH_FORMAT_UNKNOWN =0, 73 PATCH_FORMAT_MBOX, 74 PATCH_FORMAT_STGIT, 75 PATCH_FORMAT_STGIT_SERIES, 76 PATCH_FORMAT_HG, 77 PATCH_FORMAT_MBOXRD 78}; 79 80enum keep_type { 81 KEEP_FALSE =0, 82 KEEP_TRUE,/* pass -k flag to git-mailinfo */ 83 KEEP_NON_PATCH /* pass -b flag to git-mailinfo */ 84}; 85 86enum scissors_type { 87 SCISSORS_UNSET = -1, 88 SCISSORS_FALSE =0,/* pass --no-scissors to git-mailinfo */ 89 SCISSORS_TRUE /* pass --scissors to git-mailinfo */ 90}; 91 92enum signoff_type { 93 SIGNOFF_FALSE =0, 94 SIGNOFF_TRUE =1, 95 SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ 96}; 97 98struct am_state { 99/* state directory path */ 100char*dir; 101 102/* current and last patch numbers, 1-indexed */ 103int cur; 104int last; 105 106/* commit metadata and message */ 107char*author_name; 108char*author_email; 109char*author_date; 110char*msg; 111size_t msg_len; 112 113/* when --rebasing, records the original commit the patch came from */ 114struct object_id orig_commit; 115 116/* number of digits in patch filename */ 117int prec; 118 119/* various operating modes and command line options */ 120int interactive; 121int threeway; 122int quiet; 123int signoff;/* enum signoff_type */ 124int utf8; 125int keep;/* enum keep_type */ 126int message_id; 127int scissors;/* enum scissors_type */ 128struct argv_array git_apply_opts; 129const char*resolvemsg; 130int committer_date_is_author_date; 131int ignore_date; 132int allow_rerere_autoupdate; 133const char*sign_commit; 134int rebasing; 135}; 136 137/** 138 * Initializes am_state with the default values. 139 */ 140static voidam_state_init(struct am_state *state) 141{ 142int gpgsign; 143 144memset(state,0,sizeof(*state)); 145 146 state->dir =git_pathdup("rebase-apply"); 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) 418oidclr(&state->orig_commit); 419else if(get_oid_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 434if(file_exists(am_path(state,"rerere-autoupdate"))) { 435read_state_file(&sb, state,"rerere-autoupdate",1); 436 state->allow_rerere_autoupdate =strcmp(sb.buf,"t") ? 437 RERERE_NOAUTOUPDATE : RERERE_AUTOUPDATE; 438}else{ 439 state->allow_rerere_autoupdate =0; 440} 441 442read_state_file(&sb, state,"keep",1); 443if(!strcmp(sb.buf,"t")) 444 state->keep = KEEP_TRUE; 445else if(!strcmp(sb.buf,"b")) 446 state->keep = KEEP_NON_PATCH; 447else 448 state->keep = KEEP_FALSE; 449 450read_state_file(&sb, state,"messageid",1); 451 state->message_id = !strcmp(sb.buf,"t"); 452 453read_state_file(&sb, state,"scissors",1); 454if(!strcmp(sb.buf,"t")) 455 state->scissors = SCISSORS_TRUE; 456else if(!strcmp(sb.buf,"f")) 457 state->scissors = SCISSORS_FALSE; 458else 459 state->scissors = SCISSORS_UNSET; 460 461read_state_file(&sb, state,"apply-opt",1); 462argv_array_clear(&state->git_apply_opts); 463if(sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) <0) 464die(_("could not parse%s"),am_path(state,"apply-opt")); 465 466 state->rebasing = !!file_exists(am_path(state,"rebasing")); 467 468strbuf_release(&sb); 469} 470 471/** 472 * Removes the am_state directory, forcefully terminating the current am 473 * session. 474 */ 475static voidam_destroy(const struct am_state *state) 476{ 477struct strbuf sb = STRBUF_INIT; 478 479strbuf_addstr(&sb, state->dir); 480remove_dir_recursively(&sb,0); 481strbuf_release(&sb); 482} 483 484/** 485 * Runs applypatch-msg hook. Returns its exit code. 486 */ 487static intrun_applypatch_msg_hook(struct am_state *state) 488{ 489int ret; 490 491assert(state->msg); 492 ret =run_hook_le(NULL,"applypatch-msg",am_path(state,"final-commit"), NULL); 493 494if(!ret) { 495FREE_AND_NULL(state->msg); 496if(read_commit_msg(state) <0) 497die(_("'%s' was deleted by the applypatch-msg hook"), 498am_path(state,"final-commit")); 499} 500 501return ret; 502} 503 504/** 505 * Runs post-rewrite hook. Returns it exit code. 506 */ 507static intrun_post_rewrite_hook(const struct am_state *state) 508{ 509struct child_process cp = CHILD_PROCESS_INIT; 510const char*hook =find_hook("post-rewrite"); 511int ret; 512 513if(!hook) 514return0; 515 516argv_array_push(&cp.args, hook); 517argv_array_push(&cp.args,"rebase"); 518 519 cp.in =xopen(am_path(state,"rewritten"), O_RDONLY); 520 cp.stdout_to_stderr =1; 521 522 ret =run_command(&cp); 523 524close(cp.in); 525return ret; 526} 527 528/** 529 * Reads the state directory's "rewritten" file, and copies notes from the old 530 * commits listed in the file to their rewritten commits. 531 * 532 * Returns 0 on success, -1 on failure. 533 */ 534static intcopy_notes_for_rebase(const struct am_state *state) 535{ 536struct notes_rewrite_cfg *c; 537struct strbuf sb = STRBUF_INIT; 538const char*invalid_line =_("Malformed input line: '%s'."); 539const char*msg ="Notes added by 'git rebase'"; 540FILE*fp; 541int ret =0; 542 543assert(state->rebasing); 544 545 c =init_copy_notes_for_rewrite("rebase"); 546if(!c) 547return0; 548 549 fp =xfopen(am_path(state,"rewritten"),"r"); 550 551while(!strbuf_getline_lf(&sb, fp)) { 552struct object_id from_obj, to_obj; 553 554if(sb.len != GIT_SHA1_HEXSZ *2+1) { 555 ret =error(invalid_line, sb.buf); 556goto finish; 557} 558 559if(get_oid_hex(sb.buf, &from_obj)) { 560 ret =error(invalid_line, sb.buf); 561goto finish; 562} 563 564if(sb.buf[GIT_SHA1_HEXSZ] !=' ') { 565 ret =error(invalid_line, sb.buf); 566goto finish; 567} 568 569if(get_oid_hex(sb.buf + GIT_SHA1_HEXSZ +1, &to_obj)) { 570 ret =error(invalid_line, sb.buf); 571goto finish; 572} 573 574if(copy_note_for_rewrite(c, &from_obj, &to_obj)) 575 ret =error(_("Failed to copy notes from '%s' to '%s'"), 576oid_to_hex(&from_obj),oid_to_hex(&to_obj)); 577} 578 579finish: 580finish_copy_notes_for_rewrite(c, msg); 581fclose(fp); 582strbuf_release(&sb); 583return ret; 584} 585 586/** 587 * Determines if the file looks like a piece of RFC2822 mail by grabbing all 588 * non-indented lines and checking if they look like they begin with valid 589 * header field names. 590 * 591 * Returns 1 if the file looks like a piece of mail, 0 otherwise. 592 */ 593static intis_mail(FILE*fp) 594{ 595const char*header_regex ="^[!-9;-~]+:"; 596struct strbuf sb = STRBUF_INIT; 597 regex_t regex; 598int ret =1; 599 600if(fseek(fp,0L, SEEK_SET)) 601die_errno(_("fseek failed")); 602 603if(regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED)) 604die("invalid pattern:%s", header_regex); 605 606while(!strbuf_getline(&sb, fp)) { 607if(!sb.len) 608break;/* End of header */ 609 610/* Ignore indented folded lines */ 611if(*sb.buf =='\t'|| *sb.buf ==' ') 612continue; 613 614/* It's a header if it matches header_regex */ 615if(regexec(®ex, sb.buf,0, NULL,0)) { 616 ret =0; 617goto done; 618} 619} 620 621done: 622regfree(®ex); 623strbuf_release(&sb); 624return ret; 625} 626 627/** 628 * Attempts to detect the patch_format of the patches contained in `paths`, 629 * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if 630 * detection fails. 631 */ 632static intdetect_patch_format(const char**paths) 633{ 634enum patch_format ret = PATCH_FORMAT_UNKNOWN; 635struct strbuf l1 = STRBUF_INIT; 636struct strbuf l2 = STRBUF_INIT; 637struct strbuf l3 = STRBUF_INIT; 638FILE*fp; 639 640/* 641 * We default to mbox format if input is from stdin and for directories 642 */ 643if(!*paths || !strcmp(*paths,"-") ||is_directory(*paths)) 644return PATCH_FORMAT_MBOX; 645 646/* 647 * Otherwise, check the first few lines of the first patch, starting 648 * from the first non-blank line, to try to detect its format. 649 */ 650 651 fp =xfopen(*paths,"r"); 652 653while(!strbuf_getline(&l1, fp)) { 654if(l1.len) 655break; 656} 657 658if(starts_with(l1.buf,"From ") ||starts_with(l1.buf,"From: ")) { 659 ret = PATCH_FORMAT_MBOX; 660goto done; 661} 662 663if(starts_with(l1.buf,"# This series applies on GIT commit")) { 664 ret = PATCH_FORMAT_STGIT_SERIES; 665goto done; 666} 667 668if(!strcmp(l1.buf,"# HG changeset patch")) { 669 ret = PATCH_FORMAT_HG; 670goto done; 671} 672 673strbuf_reset(&l2); 674strbuf_getline(&l2, fp); 675strbuf_reset(&l3); 676strbuf_getline(&l3, fp); 677 678/* 679 * If the second line is empty and the third is a From, Author or Date 680 * entry, this is likely an StGit patch. 681 */ 682if(l1.len && !l2.len && 683(starts_with(l3.buf,"From:") || 684starts_with(l3.buf,"Author:") || 685starts_with(l3.buf,"Date:"))) { 686 ret = PATCH_FORMAT_STGIT; 687goto done; 688} 689 690if(l1.len &&is_mail(fp)) { 691 ret = PATCH_FORMAT_MBOX; 692goto done; 693} 694 695done: 696fclose(fp); 697strbuf_release(&l1); 698return ret; 699} 700 701/** 702 * Splits out individual email patches from `paths`, where each path is either 703 * a mbox file or a Maildir. Returns 0 on success, -1 on failure. 704 */ 705static intsplit_mail_mbox(struct am_state *state,const char**paths, 706int keep_cr,int mboxrd) 707{ 708struct child_process cp = CHILD_PROCESS_INIT; 709struct strbuf last = STRBUF_INIT; 710 711 cp.git_cmd =1; 712argv_array_push(&cp.args,"mailsplit"); 713argv_array_pushf(&cp.args,"-d%d", state->prec); 714argv_array_pushf(&cp.args,"-o%s", state->dir); 715argv_array_push(&cp.args,"-b"); 716if(keep_cr) 717argv_array_push(&cp.args,"--keep-cr"); 718if(mboxrd) 719argv_array_push(&cp.args,"--mboxrd"); 720argv_array_push(&cp.args,"--"); 721argv_array_pushv(&cp.args, paths); 722 723if(capture_command(&cp, &last,8)) 724return-1; 725 726 state->cur =1; 727 state->last =strtol(last.buf, NULL,10); 728 729return0; 730} 731 732/** 733 * Callback signature for split_mail_conv(). The foreign patch should be 734 * read from `in`, and the converted patch (in RFC2822 mail format) should be 735 * written to `out`. Return 0 on success, or -1 on failure. 736 */ 737typedefint(*mail_conv_fn)(FILE*out,FILE*in,int keep_cr); 738 739/** 740 * Calls `fn` for each file in `paths` to convert the foreign patch to the 741 * RFC2822 mail format suitable for parsing with git-mailinfo. 742 * 743 * Returns 0 on success, -1 on failure. 744 */ 745static intsplit_mail_conv(mail_conv_fn fn,struct am_state *state, 746const char**paths,int keep_cr) 747{ 748static const char*stdin_only[] = {"-", NULL}; 749int i; 750 751if(!*paths) 752 paths = stdin_only; 753 754for(i =0; *paths; paths++, i++) { 755FILE*in, *out; 756const char*mail; 757int ret; 758 759if(!strcmp(*paths,"-")) 760 in = stdin; 761else 762 in =fopen(*paths,"r"); 763 764if(!in) 765returnerror_errno(_("could not open '%s' for reading"), 766*paths); 767 768 mail =mkpath("%s/%0*d", state->dir, state->prec, i +1); 769 770 out =fopen(mail,"w"); 771if(!out) { 772if(in != stdin) 773fclose(in); 774returnerror_errno(_("could not open '%s' for writing"), 775 mail); 776} 777 778 ret =fn(out, in, keep_cr); 779 780fclose(out); 781if(in != stdin) 782fclose(in); 783 784if(ret) 785returnerror(_("could not parse patch '%s'"), *paths); 786} 787 788 state->cur =1; 789 state->last = i; 790return0; 791} 792 793/** 794 * A split_mail_conv() callback that converts an StGit patch to an RFC2822 795 * message suitable for parsing with git-mailinfo. 796 */ 797static intstgit_patch_to_mail(FILE*out,FILE*in,int keep_cr) 798{ 799struct strbuf sb = STRBUF_INIT; 800int subject_printed =0; 801 802while(!strbuf_getline_lf(&sb, in)) { 803const char*str; 804 805if(str_isspace(sb.buf)) 806continue; 807else if(skip_prefix(sb.buf,"Author:", &str)) 808fprintf(out,"From:%s\n", str); 809else if(starts_with(sb.buf,"From") ||starts_with(sb.buf,"Date")) 810fprintf(out,"%s\n", sb.buf); 811else if(!subject_printed) { 812fprintf(out,"Subject:%s\n", sb.buf); 813 subject_printed =1; 814}else{ 815fprintf(out,"\n%s\n", sb.buf); 816break; 817} 818} 819 820strbuf_reset(&sb); 821while(strbuf_fread(&sb,8192, in) >0) { 822fwrite(sb.buf,1, sb.len, out); 823strbuf_reset(&sb); 824} 825 826strbuf_release(&sb); 827return0; 828} 829 830/** 831 * This function only supports a single StGit series file in `paths`. 832 * 833 * Given an StGit series file, converts the StGit patches in the series into 834 * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in 835 * the state directory. 836 * 837 * Returns 0 on success, -1 on failure. 838 */ 839static intsplit_mail_stgit_series(struct am_state *state,const char**paths, 840int keep_cr) 841{ 842const char*series_dir; 843char*series_dir_buf; 844FILE*fp; 845struct argv_array patches = ARGV_ARRAY_INIT; 846struct strbuf sb = STRBUF_INIT; 847int ret; 848 849if(!paths[0] || paths[1]) 850returnerror(_("Only one StGIT patch series can be applied at once")); 851 852 series_dir_buf =xstrdup(*paths); 853 series_dir =dirname(series_dir_buf); 854 855 fp =fopen(*paths,"r"); 856if(!fp) 857returnerror_errno(_("could not open '%s' for reading"), *paths); 858 859while(!strbuf_getline_lf(&sb, fp)) { 860if(*sb.buf =='#') 861continue;/* skip comment lines */ 862 863argv_array_push(&patches,mkpath("%s/%s", series_dir, sb.buf)); 864} 865 866fclose(fp); 867strbuf_release(&sb); 868free(series_dir_buf); 869 870 ret =split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr); 871 872argv_array_clear(&patches); 873return ret; 874} 875 876/** 877 * A split_patches_conv() callback that converts a mercurial patch to a RFC2822 878 * message suitable for parsing with git-mailinfo. 879 */ 880static inthg_patch_to_mail(FILE*out,FILE*in,int keep_cr) 881{ 882struct strbuf sb = STRBUF_INIT; 883 884while(!strbuf_getline_lf(&sb, in)) { 885const char*str; 886 887if(skip_prefix(sb.buf,"# User ", &str)) 888fprintf(out,"From:%s\n", str); 889else if(skip_prefix(sb.buf,"# Date ", &str)) { 890 timestamp_t timestamp; 891long tz, tz2; 892char*end; 893 894 errno =0; 895 timestamp =parse_timestamp(str, &end,10); 896if(errno) 897returnerror(_("invalid timestamp")); 898 899if(!skip_prefix(end," ", &str)) 900returnerror(_("invalid Date line")); 901 902 errno =0; 903 tz =strtol(str, &end,10); 904if(errno) 905returnerror(_("invalid timezone offset")); 906 907if(*end) 908returnerror(_("invalid Date line")); 909 910/* 911 * mercurial's timezone is in seconds west of UTC, 912 * however git's timezone is in hours + minutes east of 913 * UTC. Convert it. 914 */ 915 tz2 =labs(tz) /3600*100+labs(tz) %3600/60; 916if(tz >0) 917 tz2 = -tz2; 918 919fprintf(out,"Date:%s\n",show_date(timestamp, tz2,DATE_MODE(RFC2822))); 920}else if(starts_with(sb.buf,"# ")) { 921continue; 922}else{ 923fprintf(out,"\n%s\n", sb.buf); 924break; 925} 926} 927 928strbuf_reset(&sb); 929while(strbuf_fread(&sb,8192, in) >0) { 930fwrite(sb.buf,1, sb.len, out); 931strbuf_reset(&sb); 932} 933 934strbuf_release(&sb); 935return0; 936} 937 938/** 939 * Splits a list of files/directories into individual email patches. Each path 940 * in `paths` must be a file/directory that is formatted according to 941 * `patch_format`. 942 * 943 * Once split out, the individual email patches will be stored in the state 944 * directory, with each patch's filename being its index, padded to state->prec 945 * digits. 946 * 947 * state->cur will be set to the index of the first mail, and state->last will 948 * be set to the index of the last mail. 949 * 950 * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1 951 * to disable this behavior, -1 to use the default configured setting. 952 * 953 * Returns 0 on success, -1 on failure. 954 */ 955static intsplit_mail(struct am_state *state,enum patch_format patch_format, 956const char**paths,int keep_cr) 957{ 958if(keep_cr <0) { 959 keep_cr =0; 960git_config_get_bool("am.keepcr", &keep_cr); 961} 962 963switch(patch_format) { 964case PATCH_FORMAT_MBOX: 965returnsplit_mail_mbox(state, paths, keep_cr,0); 966case PATCH_FORMAT_STGIT: 967returnsplit_mail_conv(stgit_patch_to_mail, state, paths, keep_cr); 968case PATCH_FORMAT_STGIT_SERIES: 969returnsplit_mail_stgit_series(state, paths, keep_cr); 970case PATCH_FORMAT_HG: 971returnsplit_mail_conv(hg_patch_to_mail, state, paths, keep_cr); 972case PATCH_FORMAT_MBOXRD: 973returnsplit_mail_mbox(state, paths, keep_cr,1); 974default: 975die("BUG: invalid patch_format"); 976} 977return-1; 978} 979 980/** 981 * Setup a new am session for applying patches 982 */ 983static voidam_setup(struct am_state *state,enum patch_format patch_format, 984const char**paths,int keep_cr) 985{ 986struct object_id curr_head; 987const char*str; 988struct strbuf sb = STRBUF_INIT; 989 990if(!patch_format) 991 patch_format =detect_patch_format(paths); 992 993if(!patch_format) { 994fprintf_ln(stderr,_("Patch format detection failed.")); 995exit(128); 996} 997 998if(mkdir(state->dir,0777) <0&& errno != EEXIST) 999die_errno(_("failed to create directory '%s'"), state->dir);10001001if(split_mail(state, patch_format, paths, keep_cr) <0) {1002am_destroy(state);1003die(_("Failed to split patches."));1004}10051006if(state->rebasing)1007 state->threeway =1;10081009write_state_bool(state,"threeway", state->threeway);1010write_state_bool(state,"quiet", state->quiet);1011write_state_bool(state,"sign", state->signoff);1012write_state_bool(state,"utf8", state->utf8);10131014if(state->allow_rerere_autoupdate)1015write_state_bool(state,"rerere-autoupdate",1016 state->allow_rerere_autoupdate == RERERE_AUTOUPDATE);10171018switch(state->keep) {1019case KEEP_FALSE:1020 str ="f";1021break;1022case KEEP_TRUE:1023 str ="t";1024break;1025case KEEP_NON_PATCH:1026 str ="b";1027break;1028default:1029die("BUG: invalid value for state->keep");1030}10311032write_state_text(state,"keep", str);1033write_state_bool(state,"messageid", state->message_id);10341035switch(state->scissors) {1036case SCISSORS_UNSET:1037 str ="";1038break;1039case SCISSORS_FALSE:1040 str ="f";1041break;1042case SCISSORS_TRUE:1043 str ="t";1044break;1045default:1046die("BUG: invalid value for state->scissors");1047}1048write_state_text(state,"scissors", str);10491050sq_quote_argv(&sb, state->git_apply_opts.argv,0);1051write_state_text(state,"apply-opt", sb.buf);10521053if(state->rebasing)1054write_state_text(state,"rebasing","");1055else1056write_state_text(state,"applying","");10571058if(!get_oid("HEAD", &curr_head)) {1059write_state_text(state,"abort-safety",oid_to_hex(&curr_head));1060if(!state->rebasing)1061update_ref_oid("am","ORIG_HEAD", &curr_head, NULL,0,1062 UPDATE_REFS_DIE_ON_ERR);1063}else{1064write_state_text(state,"abort-safety","");1065if(!state->rebasing)1066delete_ref(NULL,"ORIG_HEAD", NULL,0);1067}10681069/*1070 * NOTE: Since the "next" and "last" files determine if an am_state1071 * session is in progress, they should be written last.1072 */10731074write_state_count(state,"next", state->cur);1075write_state_count(state,"last", state->last);10761077strbuf_release(&sb);1078}10791080/**1081 * Increments the patch pointer, and cleans am_state for the application of the1082 * next patch.1083 */1084static voidam_next(struct am_state *state)1085{1086struct object_id head;10871088FREE_AND_NULL(state->author_name);1089FREE_AND_NULL(state->author_email);1090FREE_AND_NULL(state->author_date);1091FREE_AND_NULL(state->msg);1092 state->msg_len =0;10931094unlink(am_path(state,"author-script"));1095unlink(am_path(state,"final-commit"));10961097oidclr(&state->orig_commit);1098unlink(am_path(state,"original-commit"));10991100if(!get_oid("HEAD", &head))1101write_state_text(state,"abort-safety",oid_to_hex(&head));1102else1103write_state_text(state,"abort-safety","");11041105 state->cur++;1106write_state_count(state,"next", state->cur);1107}11081109/**1110 * Returns the filename of the current patch email.1111 */1112static const char*msgnum(const struct am_state *state)1113{1114static struct strbuf sb = STRBUF_INIT;11151116strbuf_reset(&sb);1117strbuf_addf(&sb,"%0*d", state->prec, state->cur);11181119return sb.buf;1120}11211122/**1123 * Refresh and write index.1124 */1125static voidrefresh_and_write_cache(void)1126{1127struct lock_file *lock_file =xcalloc(1,sizeof(struct lock_file));11281129hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);1130refresh_cache(REFRESH_QUIET);1131if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))1132die(_("unable to write index file"));1133}11341135/**1136 * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn1137 * branch, returns 1 if there are entries in the index, 0 otherwise. If an1138 * strbuf is provided, the space-separated list of files that differ will be1139 * appended to it.1140 */1141static intindex_has_changes(struct strbuf *sb)1142{1143struct object_id head;1144int i;11451146if(!get_oid_tree("HEAD", &head)) {1147struct diff_options opt;11481149diff_setup(&opt);1150DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);1151if(!sb)1152DIFF_OPT_SET(&opt, QUICK);1153do_diff_cache(&head, &opt);1154diffcore_std(&opt);1155for(i =0; sb && i < diff_queued_diff.nr; i++) {1156if(i)1157strbuf_addch(sb,' ');1158strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);1159}1160diff_flush(&opt);1161returnDIFF_OPT_TST(&opt, HAS_CHANGES) !=0;1162}else{1163for(i =0; sb && i < active_nr; i++) {1164if(i)1165strbuf_addch(sb,' ');1166strbuf_addstr(sb, active_cache[i]->name);1167}1168return!!active_nr;1169}1170}11711172/**1173 * Dies with a user-friendly message on how to proceed after resolving the1174 * problem. This message can be overridden with state->resolvemsg.1175 */1176static void NORETURN die_user_resolve(const struct am_state *state)1177{1178if(state->resolvemsg) {1179printf_ln("%s", state->resolvemsg);1180}else{1181const char*cmdline = state->interactive ?"git am -i":"git am";11821183printf_ln(_("When you have resolved this problem, run\"%s--continue\"."), cmdline);1184printf_ln(_("If you prefer to skip this patch, run\"%s--skip\"instead."), cmdline);1185printf_ln(_("To restore the original branch and stop patching, run\"%s--abort\"."), cmdline);1186}11871188exit(128);1189}11901191/**1192 * Appends signoff to the "msg" field of the am_state.1193 */1194static voidam_append_signoff(struct am_state *state)1195{1196struct strbuf sb = STRBUF_INIT;11971198strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);1199append_signoff(&sb,0,0);1200 state->msg =strbuf_detach(&sb, &state->msg_len);1201}12021203/**1204 * Parses `mail` using git-mailinfo, extracting its patch and authorship info.1205 * state->msg will be set to the patch message. state->author_name,1206 * state->author_email and state->author_date will be set to the patch author's1207 * name, email and date respectively. The patch body will be written to the1208 * state directory's "patch" file.1209 *1210 * Returns 1 if the patch should be skipped, 0 otherwise.1211 */1212static intparse_mail(struct am_state *state,const char*mail)1213{1214FILE*fp;1215struct strbuf sb = STRBUF_INIT;1216struct strbuf msg = STRBUF_INIT;1217struct strbuf author_name = STRBUF_INIT;1218struct strbuf author_date = STRBUF_INIT;1219struct strbuf author_email = STRBUF_INIT;1220int ret =0;1221struct mailinfo mi;12221223setup_mailinfo(&mi);12241225if(state->utf8)1226 mi.metainfo_charset =get_commit_output_encoding();1227else1228 mi.metainfo_charset = NULL;12291230switch(state->keep) {1231case KEEP_FALSE:1232break;1233case KEEP_TRUE:1234 mi.keep_subject =1;1235break;1236case KEEP_NON_PATCH:1237 mi.keep_non_patch_brackets_in_subject =1;1238break;1239default:1240die("BUG: invalid value for state->keep");1241}12421243if(state->message_id)1244 mi.add_message_id =1;12451246switch(state->scissors) {1247case SCISSORS_UNSET:1248break;1249case SCISSORS_FALSE:1250 mi.use_scissors =0;1251break;1252case SCISSORS_TRUE:1253 mi.use_scissors =1;1254break;1255default:1256die("BUG: invalid value for state->scissors");1257}12581259 mi.input =xfopen(mail,"r");1260 mi.output =xfopen(am_path(state,"info"),"w");1261if(mailinfo(&mi,am_path(state,"msg"),am_path(state,"patch")))1262die("could not parse patch");12631264fclose(mi.input);1265fclose(mi.output);12661267/* Extract message and author information */1268 fp =xfopen(am_path(state,"info"),"r");1269while(!strbuf_getline_lf(&sb, fp)) {1270const char*x;12711272if(skip_prefix(sb.buf,"Subject: ", &x)) {1273if(msg.len)1274strbuf_addch(&msg,'\n');1275strbuf_addstr(&msg, x);1276}else if(skip_prefix(sb.buf,"Author: ", &x))1277strbuf_addstr(&author_name, x);1278else if(skip_prefix(sb.buf,"Email: ", &x))1279strbuf_addstr(&author_email, x);1280else if(skip_prefix(sb.buf,"Date: ", &x))1281strbuf_addstr(&author_date, x);1282}1283fclose(fp);12841285/* Skip pine's internal folder data */1286if(!strcmp(author_name.buf,"Mail System Internal Data")) {1287 ret =1;1288goto finish;1289}12901291if(is_empty_file(am_path(state,"patch"))) {1292printf_ln(_("Patch is empty."));1293die_user_resolve(state);1294}12951296strbuf_addstr(&msg,"\n\n");1297strbuf_addbuf(&msg, &mi.log_message);1298strbuf_stripspace(&msg,0);12991300assert(!state->author_name);1301 state->author_name =strbuf_detach(&author_name, NULL);13021303assert(!state->author_email);1304 state->author_email =strbuf_detach(&author_email, NULL);13051306assert(!state->author_date);1307 state->author_date =strbuf_detach(&author_date, NULL);13081309assert(!state->msg);1310 state->msg =strbuf_detach(&msg, &state->msg_len);13111312finish:1313strbuf_release(&msg);1314strbuf_release(&author_date);1315strbuf_release(&author_email);1316strbuf_release(&author_name);1317strbuf_release(&sb);1318clear_mailinfo(&mi);1319return ret;1320}13211322/**1323 * Sets commit_id to the commit hash where the mail was generated from.1324 * Returns 0 on success, -1 on failure.1325 */1326static intget_mail_commit_oid(struct object_id *commit_id,const char*mail)1327{1328struct strbuf sb = STRBUF_INIT;1329FILE*fp =xfopen(mail,"r");1330const char*x;1331int ret =0;13321333if(strbuf_getline_lf(&sb, fp) ||1334!skip_prefix(sb.buf,"From ", &x) ||1335get_oid_hex(x, commit_id) <0)1336 ret = -1;13371338strbuf_release(&sb);1339fclose(fp);1340return ret;1341}13421343/**1344 * Sets state->msg, state->author_name, state->author_email, state->author_date1345 * to the commit's respective info.1346 */1347static voidget_commit_info(struct am_state *state,struct commit *commit)1348{1349const char*buffer, *ident_line, *msg;1350size_t ident_len;1351struct ident_split id;13521353 buffer =logmsg_reencode(commit, NULL,get_commit_output_encoding());13541355 ident_line =find_commit_header(buffer,"author", &ident_len);13561357if(split_ident_line(&id, ident_line, ident_len) <0)1358die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);13591360assert(!state->author_name);1361if(id.name_begin)1362 state->author_name =1363xmemdupz(id.name_begin, id.name_end - id.name_begin);1364else1365 state->author_name =xstrdup("");13661367assert(!state->author_email);1368if(id.mail_begin)1369 state->author_email =1370xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);1371else1372 state->author_email =xstrdup("");13731374assert(!state->author_date);1375 state->author_date =xstrdup(show_ident_date(&id,DATE_MODE(NORMAL)));13761377assert(!state->msg);1378 msg =strstr(buffer,"\n\n");1379if(!msg)1380die(_("unable to parse commit%s"),oid_to_hex(&commit->object.oid));1381 state->msg =xstrdup(msg +2);1382 state->msg_len =strlen(state->msg);1383unuse_commit_buffer(commit, buffer);1384}13851386/**1387 * Writes `commit` as a patch to the state directory's "patch" file.1388 */1389static voidwrite_commit_patch(const struct am_state *state,struct commit *commit)1390{1391struct rev_info rev_info;1392FILE*fp;13931394 fp =xfopen(am_path(state,"patch"),"w");1395init_revisions(&rev_info, NULL);1396 rev_info.diff =1;1397 rev_info.abbrev =0;1398 rev_info.disable_stdin =1;1399 rev_info.show_root_diff =1;1400 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;1401 rev_info.no_commit_id =1;1402DIFF_OPT_SET(&rev_info.diffopt, BINARY);1403DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);1404 rev_info.diffopt.use_color =0;1405 rev_info.diffopt.file = fp;1406 rev_info.diffopt.close_file =1;1407add_pending_object(&rev_info, &commit->object,"");1408diff_setup_done(&rev_info.diffopt);1409log_tree_commit(&rev_info, commit);1410}14111412/**1413 * Writes the diff of the index against HEAD as a patch to the state1414 * directory's "patch" file.1415 */1416static voidwrite_index_patch(const struct am_state *state)1417{1418struct tree *tree;1419struct object_id head;1420struct rev_info rev_info;1421FILE*fp;14221423if(!get_oid_tree("HEAD", &head))1424 tree =lookup_tree(&head);1425else1426 tree =lookup_tree(&empty_tree_oid);14271428 fp =xfopen(am_path(state,"patch"),"w");1429init_revisions(&rev_info, NULL);1430 rev_info.diff =1;1431 rev_info.disable_stdin =1;1432 rev_info.no_commit_id =1;1433 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;1434 rev_info.diffopt.use_color =0;1435 rev_info.diffopt.file = fp;1436 rev_info.diffopt.close_file =1;1437add_pending_object(&rev_info, &tree->object,"");1438diff_setup_done(&rev_info.diffopt);1439run_diff_index(&rev_info,1);1440}14411442/**1443 * Like parse_mail(), but parses the mail by looking up its commit ID1444 * directly. This is used in --rebasing mode to bypass git-mailinfo's munging1445 * of patches.1446 *1447 * state->orig_commit will be set to the original commit ID.1448 *1449 * Will always return 0 as the patch should never be skipped.1450 */1451static intparse_mail_rebase(struct am_state *state,const char*mail)1452{1453struct commit *commit;1454struct object_id commit_oid;14551456if(get_mail_commit_oid(&commit_oid, mail) <0)1457die(_("could not parse%s"), mail);14581459 commit =lookup_commit_or_die(&commit_oid, mail);14601461get_commit_info(state, commit);14621463write_commit_patch(state, commit);14641465oidcpy(&state->orig_commit, &commit_oid);1466write_state_text(state,"original-commit",oid_to_hex(&commit_oid));14671468return0;1469}14701471/**1472 * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If1473 * `index_file` is not NULL, the patch will be applied to that index.1474 */1475static intrun_apply(const struct am_state *state,const char*index_file)1476{1477struct argv_array apply_paths = ARGV_ARRAY_INIT;1478struct argv_array apply_opts = ARGV_ARRAY_INIT;1479struct apply_state apply_state;1480int res, opts_left;1481static struct lock_file lock_file;1482int force_apply =0;1483int options =0;14841485if(init_apply_state(&apply_state, NULL, &lock_file))1486die("BUG: init_apply_state() failed");14871488argv_array_push(&apply_opts,"apply");1489argv_array_pushv(&apply_opts, state->git_apply_opts.argv);14901491 opts_left =apply_parse_options(apply_opts.argc, apply_opts.argv,1492&apply_state, &force_apply, &options,1493 NULL);14941495if(opts_left !=0)1496die("unknown option passed through to git apply");14971498if(index_file) {1499 apply_state.index_file = index_file;1500 apply_state.cached =1;1501}else1502 apply_state.check_index =1;15031504/*1505 * If we are allowed to fall back on 3-way merge, don't give false1506 * errors during the initial attempt.1507 */1508if(state->threeway && !index_file)1509 apply_state.apply_verbosity = verbosity_silent;15101511if(check_apply_state(&apply_state, force_apply))1512die("BUG: check_apply_state() failed");15131514argv_array_push(&apply_paths,am_path(state,"patch"));15151516 res =apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options);15171518argv_array_clear(&apply_paths);1519argv_array_clear(&apply_opts);1520clear_apply_state(&apply_state);15211522if(res)1523return res;15241525if(index_file) {1526/* Reload index as apply_all_patches() will have modified it. */1527discard_cache();1528read_cache_from(index_file);1529}15301531return0;1532}15331534/**1535 * Builds an index that contains just the blobs needed for a 3way merge.1536 */1537static intbuild_fake_ancestor(const struct am_state *state,const char*index_file)1538{1539struct child_process cp = CHILD_PROCESS_INIT;15401541 cp.git_cmd =1;1542argv_array_push(&cp.args,"apply");1543argv_array_pushv(&cp.args, state->git_apply_opts.argv);1544argv_array_pushf(&cp.args,"--build-fake-ancestor=%s", index_file);1545argv_array_push(&cp.args,am_path(state,"patch"));15461547if(run_command(&cp))1548return-1;15491550return0;1551}15521553/**1554 * Attempt a threeway merge, using index_path as the temporary index.1555 */1556static intfall_back_threeway(const struct am_state *state,const char*index_path)1557{1558struct object_id orig_tree, their_tree, our_tree;1559const struct object_id *bases[1] = { &orig_tree };1560struct merge_options o;1561struct commit *result;1562char*their_tree_name;15631564if(get_oid("HEAD", &our_tree) <0)1565hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);15661567if(build_fake_ancestor(state, index_path))1568returnerror("could not build fake ancestor");15691570discard_cache();1571read_cache_from(index_path);15721573if(write_index_as_tree(orig_tree.hash, &the_index, index_path,0, NULL))1574returnerror(_("Repository lacks necessary blobs to fall back on 3-way merge."));15751576say(state, stdout,_("Using index info to reconstruct a base tree..."));15771578if(!state->quiet) {1579/*1580 * List paths that needed 3-way fallback, so that the user can1581 * review them with extra care to spot mismerges.1582 */1583struct rev_info rev_info;1584const char*diff_filter_str ="--diff-filter=AM";15851586init_revisions(&rev_info, NULL);1587 rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;1588diff_opt_parse(&rev_info.diffopt, &diff_filter_str,1, rev_info.prefix);1589add_pending_oid(&rev_info,"HEAD", &our_tree,0);1590diff_setup_done(&rev_info.diffopt);1591run_diff_index(&rev_info,1);1592}15931594if(run_apply(state, index_path))1595returnerror(_("Did you hand edit your patch?\n"1596"It does not apply to blobs recorded in its index."));15971598if(write_index_as_tree(their_tree.hash, &the_index, index_path,0, NULL))1599returnerror("could not write tree");16001601say(state, stdout,_("Falling back to patching base and 3-way merge..."));16021603discard_cache();1604read_cache();16051606/*1607 * This is not so wrong. Depending on which base we picked, orig_tree1608 * may be wildly different from ours, but their_tree has the same set of1609 * wildly different changes in parts the patch did not touch, so1610 * recursive ends up canceling them, saying that we reverted all those1611 * changes.1612 */16131614init_merge_options(&o);16151616 o.branch1 ="HEAD";1617 their_tree_name =xstrfmt("%.*s",linelen(state->msg), state->msg);1618 o.branch2 = their_tree_name;16191620if(state->quiet)1621 o.verbosity =0;16221623if(merge_recursive_generic(&o, &our_tree, &their_tree,1, bases, &result)) {1624rerere(state->allow_rerere_autoupdate);1625free(their_tree_name);1626returnerror(_("Failed to merge in the changes."));1627}16281629free(their_tree_name);1630return0;1631}16321633/**1634 * Commits the current index with state->msg as the commit message and1635 * state->author_name, state->author_email and state->author_date as the author1636 * information.1637 */1638static voiddo_commit(const struct am_state *state)1639{1640struct object_id tree, parent, commit;1641const struct object_id *old_oid;1642struct commit_list *parents = NULL;1643const char*reflog_msg, *author;1644struct strbuf sb = STRBUF_INIT;16451646if(run_hook_le(NULL,"pre-applypatch", NULL))1647exit(1);16481649if(write_cache_as_tree(tree.hash,0, NULL))1650die(_("git write-tree failed to write a tree"));16511652if(!get_oid_commit("HEAD", &parent)) {1653 old_oid = &parent;1654commit_list_insert(lookup_commit(&parent), &parents);1655}else{1656 old_oid = NULL;1657say(state, stderr,_("applying to an empty history"));1658}16591660 author =fmt_ident(state->author_name, state->author_email,1661 state->ignore_date ? NULL : state->author_date,1662 IDENT_STRICT);16631664if(state->committer_date_is_author_date)1665setenv("GIT_COMMITTER_DATE",1666 state->ignore_date ?"": state->author_date,1);16671668if(commit_tree(state->msg, state->msg_len, tree.hash, parents, commit.hash,1669 author, state->sign_commit))1670die(_("failed to write commit object"));16711672 reflog_msg =getenv("GIT_REFLOG_ACTION");1673if(!reflog_msg)1674 reflog_msg ="am";16751676strbuf_addf(&sb,"%s: %.*s", reflog_msg,linelen(state->msg),1677 state->msg);16781679update_ref_oid(sb.buf,"HEAD", &commit, old_oid,0,1680 UPDATE_REFS_DIE_ON_ERR);16811682if(state->rebasing) {1683FILE*fp =xfopen(am_path(state,"rewritten"),"a");16841685assert(!is_null_oid(&state->orig_commit));1686fprintf(fp,"%s",oid_to_hex(&state->orig_commit));1687fprintf(fp,"%s\n",oid_to_hex(&commit));1688fclose(fp);1689}16901691run_hook_le(NULL,"post-applypatch", NULL);16921693strbuf_release(&sb);1694}16951696/**1697 * Validates the am_state for resuming -- the "msg" and authorship fields must1698 * be filled up.1699 */1700static voidvalidate_resume_state(const struct am_state *state)1701{1702if(!state->msg)1703die(_("cannot resume:%sdoes not exist."),1704am_path(state,"final-commit"));17051706if(!state->author_name || !state->author_email || !state->author_date)1707die(_("cannot resume:%sdoes not exist."),1708am_path(state,"author-script"));1709}17101711/**1712 * Interactively prompt the user on whether the current patch should be1713 * applied.1714 *1715 * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to1716 * skip it.1717 */1718static intdo_interactive(struct am_state *state)1719{1720assert(state->msg);17211722if(!isatty(0))1723die(_("cannot be interactive without stdin connected to a terminal."));17241725for(;;) {1726const char*reply;17271728puts(_("Commit Body is:"));1729puts("--------------------------");1730printf("%s", state->msg);1731puts("--------------------------");17321733/*1734 * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]1735 * in your translation. The program will only accept English1736 * input at this point.1737 */1738 reply =git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);17391740if(!reply) {1741continue;1742}else if(*reply =='y'|| *reply =='Y') {1743return0;1744}else if(*reply =='a'|| *reply =='A') {1745 state->interactive =0;1746return0;1747}else if(*reply =='n'|| *reply =='N') {1748return1;1749}else if(*reply =='e'|| *reply =='E') {1750struct strbuf msg = STRBUF_INIT;17511752if(!launch_editor(am_path(state,"final-commit"), &msg, NULL)) {1753free(state->msg);1754 state->msg =strbuf_detach(&msg, &state->msg_len);1755}1756strbuf_release(&msg);1757}else if(*reply =='v'|| *reply =='V') {1758const char*pager =git_pager(1);1759struct child_process cp = CHILD_PROCESS_INIT;17601761if(!pager)1762 pager ="cat";1763prepare_pager_args(&cp, pager);1764argv_array_push(&cp.args,am_path(state,"patch"));1765run_command(&cp);1766}1767}1768}17691770/**1771 * Applies all queued mail.1772 *1773 * If `resume` is true, we are "resuming". The "msg" and authorship fields, as1774 * well as the state directory's "patch" file is used as-is for applying the1775 * patch and committing it.1776 */1777static voidam_run(struct am_state *state,int resume)1778{1779const char*argv_gc_auto[] = {"gc","--auto", NULL};1780struct strbuf sb = STRBUF_INIT;17811782unlink(am_path(state,"dirtyindex"));17831784refresh_and_write_cache();17851786if(index_has_changes(&sb)) {1787write_state_bool(state,"dirtyindex",1);1788die(_("Dirty index: cannot apply patches (dirty:%s)"), sb.buf);1789}17901791strbuf_release(&sb);17921793while(state->cur <= state->last) {1794const char*mail =am_path(state,msgnum(state));1795int apply_status;17961797reset_ident_date();17981799if(!file_exists(mail))1800goto next;18011802if(resume) {1803validate_resume_state(state);1804}else{1805int skip;18061807if(state->rebasing)1808 skip =parse_mail_rebase(state, mail);1809else1810 skip =parse_mail(state, mail);18111812if(skip)1813goto next;/* mail should be skipped */18141815if(state->signoff)1816am_append_signoff(state);18171818write_author_script(state);1819write_commit_msg(state);1820}18211822if(state->interactive &&do_interactive(state))1823goto next;18241825if(run_applypatch_msg_hook(state))1826exit(1);18271828say(state, stdout,_("Applying: %.*s"),linelen(state->msg), state->msg);18291830 apply_status =run_apply(state, NULL);18311832if(apply_status && state->threeway) {1833struct strbuf sb = STRBUF_INIT;18341835strbuf_addstr(&sb,am_path(state,"patch-merge-index"));1836 apply_status =fall_back_threeway(state, sb.buf);1837strbuf_release(&sb);18381839/*1840 * Applying the patch to an earlier tree and merging1841 * the result may have produced the same tree as ours.1842 */1843if(!apply_status && !index_has_changes(NULL)) {1844say(state, stdout,_("No changes -- Patch already applied."));1845goto next;1846}1847}18481849if(apply_status) {1850int advice_amworkdir =1;18511852printf_ln(_("Patch failed at%s%.*s"),msgnum(state),1853linelen(state->msg), state->msg);18541855git_config_get_bool("advice.amworkdir", &advice_amworkdir);18561857if(advice_amworkdir)1858printf_ln(_("The copy of the patch that failed is found in:%s"),1859am_path(state,"patch"));18601861die_user_resolve(state);1862}18631864do_commit(state);18651866next:1867am_next(state);18681869if(resume)1870am_load(state);1871 resume =0;1872}18731874if(!is_empty_file(am_path(state,"rewritten"))) {1875assert(state->rebasing);1876copy_notes_for_rebase(state);1877run_post_rewrite_hook(state);1878}18791880/*1881 * In rebasing mode, it's up to the caller to take care of1882 * housekeeping.1883 */1884if(!state->rebasing) {1885am_destroy(state);1886close_all_packs();1887run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);1888}1889}18901891/**1892 * Resume the current am session after patch application failure. The user did1893 * all the hard work, and we do not have to do any patch application. Just1894 * trust and commit what the user has in the index and working tree.1895 */1896static voidam_resolve(struct am_state *state)1897{1898validate_resume_state(state);18991900say(state, stdout,_("Applying: %.*s"),linelen(state->msg), state->msg);19011902if(!index_has_changes(NULL)) {1903printf_ln(_("No changes - did you forget to use 'git add'?\n"1904"If there is nothing left to stage, chances are that something else\n"1905"already introduced the same changes; you might want to skip this patch."));1906die_user_resolve(state);1907}19081909if(unmerged_cache()) {1910printf_ln(_("You still have unmerged paths in your index.\n"1911"You should 'git add' each file with resolved conflicts to mark them as such.\n"1912"You might run `git rm` on a file to accept\"deleted by them\"for it."));1913die_user_resolve(state);1914}19151916if(state->interactive) {1917write_index_patch(state);1918if(do_interactive(state))1919goto next;1920}19211922rerere(0);19231924do_commit(state);19251926next:1927am_next(state);1928am_load(state);1929am_run(state,0);1930}19311932/**1933 * Performs a checkout fast-forward from `head` to `remote`. If `reset` is1934 * true, any unmerged entries will be discarded. Returns 0 on success, -1 on1935 * failure.1936 */1937static intfast_forward_to(struct tree *head,struct tree *remote,int reset)1938{1939struct lock_file *lock_file;1940struct unpack_trees_options opts;1941struct tree_desc t[2];19421943if(parse_tree(head) ||parse_tree(remote))1944return-1;19451946 lock_file =xcalloc(1,sizeof(struct lock_file));1947hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);19481949refresh_cache(REFRESH_QUIET);19501951memset(&opts,0,sizeof(opts));1952 opts.head_idx =1;1953 opts.src_index = &the_index;1954 opts.dst_index = &the_index;1955 opts.update =1;1956 opts.merge =1;1957 opts.reset = reset;1958 opts.fn = twoway_merge;1959init_tree_desc(&t[0], head->buffer, head->size);1960init_tree_desc(&t[1], remote->buffer, remote->size);19611962if(unpack_trees(2, t, &opts)) {1963rollback_lock_file(lock_file);1964return-1;1965}19661967if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))1968die(_("unable to write new index file"));19691970return0;1971}19721973/**1974 * Merges a tree into the index. The index's stat info will take precedence1975 * over the merged tree's. Returns 0 on success, -1 on failure.1976 */1977static intmerge_tree(struct tree *tree)1978{1979struct lock_file *lock_file;1980struct unpack_trees_options opts;1981struct tree_desc t[1];19821983if(parse_tree(tree))1984return-1;19851986 lock_file =xcalloc(1,sizeof(struct lock_file));1987hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);19881989memset(&opts,0,sizeof(opts));1990 opts.head_idx =1;1991 opts.src_index = &the_index;1992 opts.dst_index = &the_index;1993 opts.merge =1;1994 opts.fn = oneway_merge;1995init_tree_desc(&t[0], tree->buffer, tree->size);19961997if(unpack_trees(1, t, &opts)) {1998rollback_lock_file(lock_file);1999return-1;2000}20012002if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))2003die(_("unable to write new index file"));20042005return0;2006}20072008/**2009 * Clean the index without touching entries that are not modified between2010 * `head` and `remote`.2011 */2012static intclean_index(const struct object_id *head,const struct object_id *remote)2013{2014struct tree *head_tree, *remote_tree, *index_tree;2015struct object_id index;20162017 head_tree =parse_tree_indirect(head);2018if(!head_tree)2019returnerror(_("Could not parse object '%s'."),oid_to_hex(head));20202021 remote_tree =parse_tree_indirect(remote);2022if(!remote_tree)2023returnerror(_("Could not parse object '%s'."),oid_to_hex(remote));20242025read_cache_unmerged();20262027if(fast_forward_to(head_tree, head_tree,1))2028return-1;20292030if(write_cache_as_tree(index.hash,0, NULL))2031return-1;20322033 index_tree =parse_tree_indirect(&index);2034if(!index_tree)2035returnerror(_("Could not parse object '%s'."),oid_to_hex(&index));20362037if(fast_forward_to(index_tree, remote_tree,0))2038return-1;20392040if(merge_tree(remote_tree))2041return-1;20422043remove_branch_state();20442045return0;2046}20472048/**2049 * Resets rerere's merge resolution metadata.2050 */2051static voidam_rerere_clear(void)2052{2053struct string_list merge_rr = STRING_LIST_INIT_DUP;2054rerere_clear(&merge_rr);2055string_list_clear(&merge_rr,1);2056}20572058/**2059 * Resume the current am session by skipping the current patch.2060 */2061static voidam_skip(struct am_state *state)2062{2063struct object_id head;20642065am_rerere_clear();20662067if(get_oid("HEAD", &head))2068hashcpy(head.hash, EMPTY_TREE_SHA1_BIN);20692070if(clean_index(&head, &head))2071die(_("failed to clean index"));20722073am_next(state);2074am_load(state);2075am_run(state,0);2076}20772078/**2079 * Returns true if it is safe to reset HEAD to the ORIG_HEAD, false otherwise.2080 *2081 * It is not safe to reset HEAD when:2082 * 1. git-am previously failed because the index was dirty.2083 * 2. HEAD has moved since git-am previously failed.2084 */2085static intsafe_to_abort(const struct am_state *state)2086{2087struct strbuf sb = STRBUF_INIT;2088struct object_id abort_safety, head;20892090if(file_exists(am_path(state,"dirtyindex")))2091return0;20922093if(read_state_file(&sb, state,"abort-safety",1) >0) {2094if(get_oid_hex(sb.buf, &abort_safety))2095die(_("could not parse%s"),am_path(state,"abort-safety"));2096}else2097oidclr(&abort_safety);20982099if(get_oid("HEAD", &head))2100oidclr(&head);21012102if(!oidcmp(&head, &abort_safety))2103return1;21042105warning(_("You seem to have moved HEAD since the last 'am' failure.\n"2106"Not rewinding to ORIG_HEAD"));21072108return0;2109}21102111/**2112 * Aborts the current am session if it is safe to do so.2113 */2114static voidam_abort(struct am_state *state)2115{2116struct object_id curr_head, orig_head;2117int has_curr_head, has_orig_head;2118char*curr_branch;21192120if(!safe_to_abort(state)) {2121am_destroy(state);2122return;2123}21242125am_rerere_clear();21262127 curr_branch =resolve_refdup("HEAD",0, curr_head.hash, NULL);2128 has_curr_head = curr_branch && !is_null_oid(&curr_head);2129if(!has_curr_head)2130hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);21312132 has_orig_head = !get_oid("ORIG_HEAD", &orig_head);2133if(!has_orig_head)2134hashcpy(orig_head.hash, EMPTY_TREE_SHA1_BIN);21352136clean_index(&curr_head, &orig_head);21372138if(has_orig_head)2139update_ref_oid("am --abort","HEAD", &orig_head,2140 has_curr_head ? &curr_head : NULL,0,2141 UPDATE_REFS_DIE_ON_ERR);2142else if(curr_branch)2143delete_ref(NULL, curr_branch, NULL, REF_NODEREF);21442145free(curr_branch);2146am_destroy(state);2147}21482149/**2150 * parse_options() callback that validates and sets opt->value to the2151 * PATCH_FORMAT_* enum value corresponding to `arg`.2152 */2153static intparse_opt_patchformat(const struct option *opt,const char*arg,int unset)2154{2155int*opt_value = opt->value;21562157if(!strcmp(arg,"mbox"))2158*opt_value = PATCH_FORMAT_MBOX;2159else if(!strcmp(arg,"stgit"))2160*opt_value = PATCH_FORMAT_STGIT;2161else if(!strcmp(arg,"stgit-series"))2162*opt_value = PATCH_FORMAT_STGIT_SERIES;2163else if(!strcmp(arg,"hg"))2164*opt_value = PATCH_FORMAT_HG;2165else if(!strcmp(arg,"mboxrd"))2166*opt_value = PATCH_FORMAT_MBOXRD;2167else2168returnerror(_("Invalid value for --patch-format:%s"), arg);2169return0;2170}21712172enum resume_mode {2173 RESUME_FALSE =0,2174 RESUME_APPLY,2175 RESUME_RESOLVED,2176 RESUME_SKIP,2177 RESUME_ABORT2178};21792180static intgit_am_config(const char*k,const char*v,void*cb)2181{2182int status;21832184 status =git_gpg_config(k, v, NULL);2185if(status)2186return status;21872188returngit_default_config(k, v, NULL);2189}21902191intcmd_am(int argc,const char**argv,const char*prefix)2192{2193struct am_state state;2194int binary = -1;2195int keep_cr = -1;2196int patch_format = PATCH_FORMAT_UNKNOWN;2197enum resume_mode resume = RESUME_FALSE;2198int in_progress;21992200const char*const usage[] = {2201N_("git am [<options>] [(<mbox> | <Maildir>)...]"),2202N_("git am [<options>] (--continue | --skip | --abort)"),2203 NULL2204};22052206struct option options[] = {2207OPT_BOOL('i',"interactive", &state.interactive,2208N_("run interactively")),2209OPT_HIDDEN_BOOL('b',"binary", &binary,2210N_("historical option -- no-op")),2211OPT_BOOL('3',"3way", &state.threeway,2212N_("allow fall back on 3way merging if needed")),2213OPT__QUIET(&state.quiet,N_("be quiet")),2214OPT_SET_INT('s',"signoff", &state.signoff,2215N_("add a Signed-off-by line to the commit message"),2216 SIGNOFF_EXPLICIT),2217OPT_BOOL('u',"utf8", &state.utf8,2218N_("recode into utf8 (default)")),2219OPT_SET_INT('k',"keep", &state.keep,2220N_("pass -k flag to git-mailinfo"), KEEP_TRUE),2221OPT_SET_INT(0,"keep-non-patch", &state.keep,2222N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),2223OPT_BOOL('m',"message-id", &state.message_id,2224N_("pass -m flag to git-mailinfo")),2225{ OPTION_SET_INT,0,"keep-cr", &keep_cr, NULL,2226N_("pass --keep-cr flag to git-mailsplit for mbox format"),2227 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL,1},2228{ OPTION_SET_INT,0,"no-keep-cr", &keep_cr, NULL,2229N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),2230 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL,0},2231OPT_BOOL('c',"scissors", &state.scissors,2232N_("strip everything before a scissors line")),2233OPT_PASSTHRU_ARGV(0,"whitespace", &state.git_apply_opts,N_("action"),2234N_("pass it through git-apply"),22350),2236OPT_PASSTHRU_ARGV(0,"ignore-space-change", &state.git_apply_opts, NULL,2237N_("pass it through git-apply"),2238 PARSE_OPT_NOARG),2239OPT_PASSTHRU_ARGV(0,"ignore-whitespace", &state.git_apply_opts, NULL,2240N_("pass it through git-apply"),2241 PARSE_OPT_NOARG),2242OPT_PASSTHRU_ARGV(0,"directory", &state.git_apply_opts,N_("root"),2243N_("pass it through git-apply"),22440),2245OPT_PASSTHRU_ARGV(0,"exclude", &state.git_apply_opts,N_("path"),2246N_("pass it through git-apply"),22470),2248OPT_PASSTHRU_ARGV(0,"include", &state.git_apply_opts,N_("path"),2249N_("pass it through git-apply"),22500),2251OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts,N_("n"),2252N_("pass it through git-apply"),22530),2254OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts,N_("num"),2255N_("pass it through git-apply"),22560),2257OPT_CALLBACK(0,"patch-format", &patch_format,N_("format"),2258N_("format the patch(es) are in"),2259 parse_opt_patchformat),2260OPT_PASSTHRU_ARGV(0,"reject", &state.git_apply_opts, NULL,2261N_("pass it through git-apply"),2262 PARSE_OPT_NOARG),2263OPT_STRING(0,"resolvemsg", &state.resolvemsg, NULL,2264N_("override error message when patch failure occurs")),2265OPT_CMDMODE(0,"continue", &resume,2266N_("continue applying patches after resolving a conflict"),2267 RESUME_RESOLVED),2268OPT_CMDMODE('r',"resolved", &resume,2269N_("synonyms for --continue"),2270 RESUME_RESOLVED),2271OPT_CMDMODE(0,"skip", &resume,2272N_("skip the current patch"),2273 RESUME_SKIP),2274OPT_CMDMODE(0,"abort", &resume,2275N_("restore the original branch and abort the patching operation."),2276 RESUME_ABORT),2277OPT_BOOL(0,"committer-date-is-author-date",2278&state.committer_date_is_author_date,2279N_("lie about committer date")),2280OPT_BOOL(0,"ignore-date", &state.ignore_date,2281N_("use current timestamp for author date")),2282OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),2283{ OPTION_STRING,'S',"gpg-sign", &state.sign_commit,N_("key-id"),2284N_("GPG-sign commits"),2285 PARSE_OPT_OPTARG, NULL, (intptr_t)""},2286OPT_HIDDEN_BOOL(0,"rebasing", &state.rebasing,2287N_("(internal use for git-rebase)")),2288OPT_END()2289};22902291if(argc ==2&& !strcmp(argv[1],"-h"))2292usage_with_options(usage, options);22932294git_config(git_am_config, NULL);22952296am_state_init(&state);22972298 in_progress =am_in_progress(&state);2299if(in_progress)2300am_load(&state);23012302 argc =parse_options(argc, argv, prefix, options, usage,0);23032304if(binary >=0)2305fprintf_ln(stderr,_("The -b/--binary option has been a no-op for long time, and\n"2306"it will be removed. Please do not use it anymore."));23072308/* Ensure a valid committer ident can be constructed */2309git_committer_info(IDENT_STRICT);23102311if(read_index_preload(&the_index, NULL) <0)2312die(_("failed to read the index"));23132314if(in_progress) {2315/*2316 * Catch user error to feed us patches when there is a session2317 * in progress:2318 *2319 * 1. mbox path(s) are provided on the command-line.2320 * 2. stdin is not a tty: the user is trying to feed us a patch2321 * from standard input. This is somewhat unreliable -- stdin2322 * could be /dev/null for example and the caller did not2323 * intend to feed us a patch but wanted to continue2324 * unattended.2325 */2326if(argc || (resume == RESUME_FALSE && !isatty(0)))2327die(_("previous rebase directory%sstill exists but mbox given."),2328 state.dir);23292330if(resume == RESUME_FALSE)2331 resume = RESUME_APPLY;23322333if(state.signoff == SIGNOFF_EXPLICIT)2334am_append_signoff(&state);2335}else{2336struct argv_array paths = ARGV_ARRAY_INIT;2337int i;23382339/*2340 * Handle stray state directory in the independent-run case. In2341 * the --rebasing case, it is up to the caller to take care of2342 * stray directories.2343 */2344if(file_exists(state.dir) && !state.rebasing) {2345if(resume == RESUME_ABORT) {2346am_destroy(&state);2347am_state_release(&state);2348return0;2349}23502351die(_("Stray%sdirectory found.\n"2352"Use\"git am --abort\"to remove it."),2353 state.dir);2354}23552356if(resume)2357die(_("Resolve operation not in progress, we are not resuming."));23582359for(i =0; i < argc; i++) {2360if(is_absolute_path(argv[i]) || !prefix)2361argv_array_push(&paths, argv[i]);2362else2363argv_array_push(&paths,mkpath("%s/%s", prefix, argv[i]));2364}23652366am_setup(&state, patch_format, paths.argv, keep_cr);23672368argv_array_clear(&paths);2369}23702371switch(resume) {2372case RESUME_FALSE:2373am_run(&state,0);2374break;2375case RESUME_APPLY:2376am_run(&state,1);2377break;2378case RESUME_RESOLVED:2379am_resolve(&state);2380break;2381case RESUME_SKIP:2382am_skip(&state);2383break;2384case RESUME_ABORT:2385am_abort(&state);2386break;2387default:2388die("BUG: invalid resume value");2389}23902391am_state_release(&state);23922393return0;2394}