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"lockfile.h" 14#include"cache-tree.h" 15#include"refs.h" 16#include"commit.h" 17#include"diff.h" 18#include"diffcore.h" 19#include"unpack-trees.h" 20#include"branch.h" 21#include"sequencer.h" 22#include"revision.h" 23#include"merge-recursive.h" 24#include"revision.h" 25#include"log-tree.h" 26#include"notes-utils.h" 27#include"rerere.h" 28#include"prompt.h" 29 30/** 31 * Returns 1 if the file is empty or does not exist, 0 otherwise. 32 */ 33static intis_empty_file(const char*filename) 34{ 35struct stat st; 36 37if(stat(filename, &st) <0) { 38if(errno == ENOENT) 39return1; 40die_errno(_("could not stat%s"), filename); 41} 42 43return!st.st_size; 44} 45 46/** 47 * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators. 48 */ 49static intstrbuf_getline_crlf(struct strbuf *sb,FILE*fp) 50{ 51if(strbuf_getwholeline(sb, fp,'\n')) 52return EOF; 53if(sb->buf[sb->len -1] =='\n') { 54strbuf_setlen(sb, sb->len -1); 55if(sb->len >0&& sb->buf[sb->len -1] =='\r') 56strbuf_setlen(sb, sb->len -1); 57} 58return0; 59} 60 61/** 62 * Returns the length of the first line of msg. 63 */ 64static intlinelen(const char*msg) 65{ 66returnstrchrnul(msg,'\n') - msg; 67} 68 69/** 70 * Returns true if `str` consists of only whitespace, false otherwise. 71 */ 72static intstr_isspace(const char*str) 73{ 74for(; *str; str++) 75if(!isspace(*str)) 76return0; 77 78return1; 79} 80 81enum patch_format { 82 PATCH_FORMAT_UNKNOWN =0, 83 PATCH_FORMAT_MBOX, 84 PATCH_FORMAT_STGIT, 85 PATCH_FORMAT_STGIT_SERIES, 86 PATCH_FORMAT_HG 87}; 88 89enum keep_type { 90 KEEP_FALSE =0, 91 KEEP_TRUE,/* pass -k flag to git-mailinfo */ 92 KEEP_NON_PATCH /* pass -b flag to git-mailinfo */ 93}; 94 95enum scissors_type { 96 SCISSORS_UNSET = -1, 97 SCISSORS_FALSE =0,/* pass --no-scissors to git-mailinfo */ 98 SCISSORS_TRUE /* pass --scissors to git-mailinfo */ 99}; 100 101enum signoff_type { 102 SIGNOFF_FALSE =0, 103 SIGNOFF_TRUE =1, 104 SIGNOFF_EXPLICIT /* --signoff was set on the command-line */ 105}; 106 107struct am_state { 108/* state directory path */ 109char*dir; 110 111/* current and last patch numbers, 1-indexed */ 112int cur; 113int last; 114 115/* commit metadata and message */ 116char*author_name; 117char*author_email; 118char*author_date; 119char*msg; 120size_t msg_len; 121 122/* when --rebasing, records the original commit the patch came from */ 123unsigned char orig_commit[GIT_SHA1_RAWSZ]; 124 125/* number of digits in patch filename */ 126int prec; 127 128/* various operating modes and command line options */ 129int interactive; 130int threeway; 131int quiet; 132int signoff;/* enum signoff_type */ 133int utf8; 134int keep;/* enum keep_type */ 135int message_id; 136int scissors;/* enum scissors_type */ 137struct argv_array git_apply_opts; 138const char*resolvemsg; 139int committer_date_is_author_date; 140int ignore_date; 141int allow_rerere_autoupdate; 142const char*sign_commit; 143int rebasing; 144}; 145 146/** 147 * Initializes am_state with the default values. The state directory is set to 148 * dir. 149 */ 150static voidam_state_init(struct am_state *state,const char*dir) 151{ 152int gpgsign; 153 154memset(state,0,sizeof(*state)); 155 156assert(dir); 157 state->dir =xstrdup(dir); 158 159 state->prec =4; 160 161git_config_get_bool("am.threeway", &state->threeway); 162 163 state->utf8 =1; 164 165git_config_get_bool("am.messageid", &state->message_id); 166 167 state->scissors = SCISSORS_UNSET; 168 169argv_array_init(&state->git_apply_opts); 170 171if(!git_config_get_bool("commit.gpgsign", &gpgsign)) 172 state->sign_commit = gpgsign ?"": NULL; 173} 174 175/** 176 * Releases memory allocated by an am_state. 177 */ 178static voidam_state_release(struct am_state *state) 179{ 180free(state->dir); 181free(state->author_name); 182free(state->author_email); 183free(state->author_date); 184free(state->msg); 185argv_array_clear(&state->git_apply_opts); 186} 187 188/** 189 * Returns path relative to the am_state directory. 190 */ 191staticinlineconst char*am_path(const struct am_state *state,const char*path) 192{ 193returnmkpath("%s/%s", state->dir, path); 194} 195 196/** 197 * If state->quiet is false, calls fprintf(fp, fmt, ...), and appends a newline 198 * at the end. 199 */ 200static voidsay(const struct am_state *state,FILE*fp,const char*fmt, ...) 201{ 202va_list ap; 203 204va_start(ap, fmt); 205if(!state->quiet) { 206vfprintf(fp, fmt, ap); 207putc('\n', fp); 208} 209va_end(ap); 210} 211 212/** 213 * Returns 1 if there is an am session in progress, 0 otherwise. 214 */ 215static intam_in_progress(const struct am_state *state) 216{ 217struct stat st; 218 219if(lstat(state->dir, &st) <0|| !S_ISDIR(st.st_mode)) 220return0; 221if(lstat(am_path(state,"last"), &st) || !S_ISREG(st.st_mode)) 222return0; 223if(lstat(am_path(state,"next"), &st) || !S_ISREG(st.st_mode)) 224return0; 225return1; 226} 227 228/** 229 * Reads the contents of `file` in the `state` directory into `sb`. Returns the 230 * number of bytes read on success, -1 if the file does not exist. If `trim` is 231 * set, trailing whitespace will be removed. 232 */ 233static intread_state_file(struct strbuf *sb,const struct am_state *state, 234const char*file,int trim) 235{ 236strbuf_reset(sb); 237 238if(strbuf_read_file(sb,am_path(state, file),0) >=0) { 239if(trim) 240strbuf_trim(sb); 241 242return sb->len; 243} 244 245if(errno == ENOENT) 246return-1; 247 248die_errno(_("could not read '%s'"),am_path(state, file)); 249} 250 251/** 252 * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE 253 * as a newly-allocated string. VALUE must be a quoted string, and the KEY must 254 * match `key`. Returns NULL on failure. 255 * 256 * This is used by read_author_script() to read the GIT_AUTHOR_* variables from 257 * the author-script. 258 */ 259static char*read_shell_var(FILE*fp,const char*key) 260{ 261struct strbuf sb = STRBUF_INIT; 262const char*str; 263 264if(strbuf_getline(&sb, fp,'\n')) 265goto fail; 266 267if(!skip_prefix(sb.buf, key, &str)) 268goto fail; 269 270if(!skip_prefix(str,"=", &str)) 271goto fail; 272 273strbuf_remove(&sb,0, str - sb.buf); 274 275 str =sq_dequote(sb.buf); 276if(!str) 277goto fail; 278 279returnstrbuf_detach(&sb, NULL); 280 281fail: 282strbuf_release(&sb); 283return NULL; 284} 285 286/** 287 * Reads and parses the state directory's "author-script" file, and sets 288 * state->author_name, state->author_email and state->author_date accordingly. 289 * Returns 0 on success, -1 if the file could not be parsed. 290 * 291 * The author script is of the format: 292 * 293 * GIT_AUTHOR_NAME='$author_name' 294 * GIT_AUTHOR_EMAIL='$author_email' 295 * GIT_AUTHOR_DATE='$author_date' 296 * 297 * where $author_name, $author_email and $author_date are quoted. We are strict 298 * with our parsing, as the file was meant to be eval'd in the old git-am.sh 299 * script, and thus if the file differs from what this function expects, it is 300 * better to bail out than to do something that the user does not expect. 301 */ 302static intread_author_script(struct am_state *state) 303{ 304const char*filename =am_path(state,"author-script"); 305FILE*fp; 306 307assert(!state->author_name); 308assert(!state->author_email); 309assert(!state->author_date); 310 311 fp =fopen(filename,"r"); 312if(!fp) { 313if(errno == ENOENT) 314return0; 315die_errno(_("could not open '%s' for reading"), filename); 316} 317 318 state->author_name =read_shell_var(fp,"GIT_AUTHOR_NAME"); 319if(!state->author_name) { 320fclose(fp); 321return-1; 322} 323 324 state->author_email =read_shell_var(fp,"GIT_AUTHOR_EMAIL"); 325if(!state->author_email) { 326fclose(fp); 327return-1; 328} 329 330 state->author_date =read_shell_var(fp,"GIT_AUTHOR_DATE"); 331if(!state->author_date) { 332fclose(fp); 333return-1; 334} 335 336if(fgetc(fp) != EOF) { 337fclose(fp); 338return-1; 339} 340 341fclose(fp); 342return0; 343} 344 345/** 346 * Saves state->author_name, state->author_email and state->author_date in the 347 * state directory's "author-script" file. 348 */ 349static voidwrite_author_script(const struct am_state *state) 350{ 351struct strbuf sb = STRBUF_INIT; 352 353strbuf_addstr(&sb,"GIT_AUTHOR_NAME="); 354sq_quote_buf(&sb, state->author_name); 355strbuf_addch(&sb,'\n'); 356 357strbuf_addstr(&sb,"GIT_AUTHOR_EMAIL="); 358sq_quote_buf(&sb, state->author_email); 359strbuf_addch(&sb,'\n'); 360 361strbuf_addstr(&sb,"GIT_AUTHOR_DATE="); 362sq_quote_buf(&sb, state->author_date); 363strbuf_addch(&sb,'\n'); 364 365write_file(am_path(state,"author-script"),1,"%s", sb.buf); 366 367strbuf_release(&sb); 368} 369 370/** 371 * Reads the commit message from the state directory's "final-commit" file, 372 * setting state->msg to its contents and state->msg_len to the length of its 373 * contents in bytes. 374 * 375 * Returns 0 on success, -1 if the file does not exist. 376 */ 377static intread_commit_msg(struct am_state *state) 378{ 379struct strbuf sb = STRBUF_INIT; 380 381assert(!state->msg); 382 383if(read_state_file(&sb, state,"final-commit",0) <0) { 384strbuf_release(&sb); 385return-1; 386} 387 388 state->msg =strbuf_detach(&sb, &state->msg_len); 389return0; 390} 391 392/** 393 * Saves state->msg in the state directory's "final-commit" file. 394 */ 395static voidwrite_commit_msg(const struct am_state *state) 396{ 397int fd; 398const char*filename =am_path(state,"final-commit"); 399 400 fd =xopen(filename, O_WRONLY | O_CREAT,0666); 401if(write_in_full(fd, state->msg, state->msg_len) <0) 402die_errno(_("could not write to%s"), filename); 403close(fd); 404} 405 406/** 407 * Loads state from disk. 408 */ 409static voidam_load(struct am_state *state) 410{ 411struct strbuf sb = STRBUF_INIT; 412 413if(read_state_file(&sb, state,"next",1) <0) 414die("BUG: state file 'next' does not exist"); 415 state->cur =strtol(sb.buf, NULL,10); 416 417if(read_state_file(&sb, state,"last",1) <0) 418die("BUG: state file 'last' does not exist"); 419 state->last =strtol(sb.buf, NULL,10); 420 421if(read_author_script(state) <0) 422die(_("could not parse author script")); 423 424read_commit_msg(state); 425 426if(read_state_file(&sb, state,"original-commit",1) <0) 427hashclr(state->orig_commit); 428else if(get_sha1_hex(sb.buf, state->orig_commit) <0) 429die(_("could not parse%s"),am_path(state,"original-commit")); 430 431read_state_file(&sb, state,"threeway",1); 432 state->threeway = !strcmp(sb.buf,"t"); 433 434read_state_file(&sb, state,"quiet",1); 435 state->quiet = !strcmp(sb.buf,"t"); 436 437read_state_file(&sb, state,"sign",1); 438 state->signoff = !strcmp(sb.buf,"t"); 439 440read_state_file(&sb, state,"utf8",1); 441 state->utf8 = !strcmp(sb.buf,"t"); 442 443read_state_file(&sb, state,"keep",1); 444if(!strcmp(sb.buf,"t")) 445 state->keep = KEEP_TRUE; 446else if(!strcmp(sb.buf,"b")) 447 state->keep = KEEP_NON_PATCH; 448else 449 state->keep = KEEP_FALSE; 450 451read_state_file(&sb, state,"messageid",1); 452 state->message_id = !strcmp(sb.buf,"t"); 453 454read_state_file(&sb, state,"scissors",1); 455if(!strcmp(sb.buf,"t")) 456 state->scissors = SCISSORS_TRUE; 457else if(!strcmp(sb.buf,"f")) 458 state->scissors = SCISSORS_FALSE; 459else 460 state->scissors = SCISSORS_UNSET; 461 462read_state_file(&sb, state,"apply-opt",1); 463argv_array_clear(&state->git_apply_opts); 464if(sq_dequote_to_argv_array(sb.buf, &state->git_apply_opts) <0) 465die(_("could not parse%s"),am_path(state,"apply-opt")); 466 467 state->rebasing = !!file_exists(am_path(state,"rebasing")); 468 469strbuf_release(&sb); 470} 471 472/** 473 * Removes the am_state directory, forcefully terminating the current am 474 * session. 475 */ 476static voidam_destroy(const struct am_state *state) 477{ 478struct strbuf sb = STRBUF_INIT; 479 480strbuf_addstr(&sb, state->dir); 481remove_dir_recursively(&sb,0); 482strbuf_release(&sb); 483} 484 485/** 486 * Runs applypatch-msg hook. Returns its exit code. 487 */ 488static intrun_applypatch_msg_hook(struct am_state *state) 489{ 490int ret; 491 492assert(state->msg); 493 ret =run_hook_le(NULL,"applypatch-msg",am_path(state,"final-commit"), NULL); 494 495if(!ret) { 496free(state->msg); 497 state->msg = NULL; 498if(read_commit_msg(state) <0) 499die(_("'%s' was deleted by the applypatch-msg hook"), 500am_path(state,"final-commit")); 501} 502 503return ret; 504} 505 506/** 507 * Runs post-rewrite hook. Returns it exit code. 508 */ 509static intrun_post_rewrite_hook(const struct am_state *state) 510{ 511struct child_process cp = CHILD_PROCESS_INIT; 512const char*hook =find_hook("post-rewrite"); 513int ret; 514 515if(!hook) 516return0; 517 518argv_array_push(&cp.args, hook); 519argv_array_push(&cp.args,"rebase"); 520 521 cp.in =xopen(am_path(state,"rewritten"), O_RDONLY); 522 cp.stdout_to_stderr =1; 523 524 ret =run_command(&cp); 525 526close(cp.in); 527return ret; 528} 529 530/** 531 * Reads the state directory's "rewritten" file, and copies notes from the old 532 * commits listed in the file to their rewritten commits. 533 * 534 * Returns 0 on success, -1 on failure. 535 */ 536static intcopy_notes_for_rebase(const struct am_state *state) 537{ 538struct notes_rewrite_cfg *c; 539struct strbuf sb = STRBUF_INIT; 540const char*invalid_line =_("Malformed input line: '%s'."); 541const char*msg ="Notes added by 'git rebase'"; 542FILE*fp; 543int ret =0; 544 545assert(state->rebasing); 546 547 c =init_copy_notes_for_rewrite("rebase"); 548if(!c) 549return0; 550 551 fp =xfopen(am_path(state,"rewritten"),"r"); 552 553while(!strbuf_getline(&sb, fp,'\n')) { 554unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ]; 555 556if(sb.len != GIT_SHA1_HEXSZ *2+1) { 557 ret =error(invalid_line, sb.buf); 558goto finish; 559} 560 561if(get_sha1_hex(sb.buf, from_obj)) { 562 ret =error(invalid_line, sb.buf); 563goto finish; 564} 565 566if(sb.buf[GIT_SHA1_HEXSZ] !=' ') { 567 ret =error(invalid_line, sb.buf); 568goto finish; 569} 570 571if(get_sha1_hex(sb.buf + GIT_SHA1_HEXSZ +1, to_obj)) { 572 ret =error(invalid_line, sb.buf); 573goto finish; 574} 575 576if(copy_note_for_rewrite(c, from_obj, to_obj)) 577 ret =error(_("Failed to copy notes from '%s' to '%s'"), 578sha1_to_hex(from_obj),sha1_to_hex(to_obj)); 579} 580 581finish: 582finish_copy_notes_for_rewrite(c, msg); 583fclose(fp); 584strbuf_release(&sb); 585return ret; 586} 587 588/** 589 * Determines if the file looks like a piece of RFC2822 mail by grabbing all 590 * non-indented lines and checking if they look like they begin with valid 591 * header field names. 592 * 593 * Returns 1 if the file looks like a piece of mail, 0 otherwise. 594 */ 595static intis_mail(FILE*fp) 596{ 597const char*header_regex ="^[!-9;-~]+:"; 598struct strbuf sb = STRBUF_INIT; 599 regex_t regex; 600int ret =1; 601 602if(fseek(fp,0L, SEEK_SET)) 603die_errno(_("fseek failed")); 604 605if(regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED)) 606die("invalid pattern:%s", header_regex); 607 608while(!strbuf_getline_crlf(&sb, fp)) { 609if(!sb.len) 610break;/* End of header */ 611 612/* Ignore indented folded lines */ 613if(*sb.buf =='\t'|| *sb.buf ==' ') 614continue; 615 616/* It's a header if it matches header_regex */ 617if(regexec(®ex, sb.buf,0, NULL,0)) { 618 ret =0; 619goto done; 620} 621} 622 623done: 624regfree(®ex); 625strbuf_release(&sb); 626return ret; 627} 628 629/** 630 * Attempts to detect the patch_format of the patches contained in `paths`, 631 * returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if 632 * detection fails. 633 */ 634static intdetect_patch_format(const char**paths) 635{ 636enum patch_format ret = PATCH_FORMAT_UNKNOWN; 637struct strbuf l1 = STRBUF_INIT; 638struct strbuf l2 = STRBUF_INIT; 639struct strbuf l3 = STRBUF_INIT; 640FILE*fp; 641 642/* 643 * We default to mbox format if input is from stdin and for directories 644 */ 645if(!*paths || !strcmp(*paths,"-") ||is_directory(*paths)) 646return PATCH_FORMAT_MBOX; 647 648/* 649 * Otherwise, check the first few lines of the first patch, starting 650 * from the first non-blank line, to try to detect its format. 651 */ 652 653 fp =xfopen(*paths,"r"); 654 655while(!strbuf_getline_crlf(&l1, fp)) { 656if(l1.len) 657break; 658} 659 660if(starts_with(l1.buf,"From ") ||starts_with(l1.buf,"From: ")) { 661 ret = PATCH_FORMAT_MBOX; 662goto done; 663} 664 665if(starts_with(l1.buf,"# This series applies on GIT commit")) { 666 ret = PATCH_FORMAT_STGIT_SERIES; 667goto done; 668} 669 670if(!strcmp(l1.buf,"# HG changeset patch")) { 671 ret = PATCH_FORMAT_HG; 672goto done; 673} 674 675strbuf_reset(&l2); 676strbuf_getline_crlf(&l2, fp); 677strbuf_reset(&l3); 678strbuf_getline_crlf(&l3, fp); 679 680/* 681 * If the second line is empty and the third is a From, Author or Date 682 * entry, this is likely an StGit patch. 683 */ 684if(l1.len && !l2.len && 685(starts_with(l3.buf,"From:") || 686starts_with(l3.buf,"Author:") || 687starts_with(l3.buf,"Date:"))) { 688 ret = PATCH_FORMAT_STGIT; 689goto done; 690} 691 692if(l1.len &&is_mail(fp)) { 693 ret = PATCH_FORMAT_MBOX; 694goto done; 695} 696 697done: 698fclose(fp); 699strbuf_release(&l1); 700return ret; 701} 702 703/** 704 * Splits out individual email patches from `paths`, where each path is either 705 * a mbox file or a Maildir. Returns 0 on success, -1 on failure. 706 */ 707static intsplit_mail_mbox(struct am_state *state,const char**paths,int keep_cr) 708{ 709struct child_process cp = CHILD_PROCESS_INIT; 710struct strbuf last = STRBUF_INIT; 711 712 cp.git_cmd =1; 713argv_array_push(&cp.args,"mailsplit"); 714argv_array_pushf(&cp.args,"-d%d", state->prec); 715argv_array_pushf(&cp.args,"-o%s", state->dir); 716argv_array_push(&cp.args,"-b"); 717if(keep_cr) 718argv_array_push(&cp.args,"--keep-cr"); 719argv_array_push(&cp.args,"--"); 720argv_array_pushv(&cp.args, paths); 721 722if(capture_command(&cp, &last,8)) 723return-1; 724 725 state->cur =1; 726 state->last =strtol(last.buf, NULL,10); 727 728return0; 729} 730 731/** 732 * Callback signature for split_mail_conv(). The foreign patch should be 733 * read from `in`, and the converted patch (in RFC2822 mail format) should be 734 * written to `out`. Return 0 on success, or -1 on failure. 735 */ 736typedefint(*mail_conv_fn)(FILE*out,FILE*in,int keep_cr); 737 738/** 739 * Calls `fn` for each file in `paths` to convert the foreign patch to the 740 * RFC2822 mail format suitable for parsing with git-mailinfo. 741 * 742 * Returns 0 on success, -1 on failure. 743 */ 744static intsplit_mail_conv(mail_conv_fn fn,struct am_state *state, 745const char**paths,int keep_cr) 746{ 747static const char*stdin_only[] = {"-", NULL}; 748int i; 749 750if(!*paths) 751 paths = stdin_only; 752 753for(i =0; *paths; paths++, i++) { 754FILE*in, *out; 755const char*mail; 756int ret; 757 758if(!strcmp(*paths,"-")) 759 in = stdin; 760else 761 in =fopen(*paths,"r"); 762 763if(!in) 764returnerror(_("could not open '%s' for reading:%s"), 765*paths,strerror(errno)); 766 767 mail =mkpath("%s/%0*d", state->dir, state->prec, i +1); 768 769 out =fopen(mail,"w"); 770if(!out) 771returnerror(_("could not open '%s' for writing:%s"), 772 mail,strerror(errno)); 773 774 ret =fn(out, in, keep_cr); 775 776fclose(out); 777fclose(in); 778 779if(ret) 780returnerror(_("could not parse patch '%s'"), *paths); 781} 782 783 state->cur =1; 784 state->last = i; 785return0; 786} 787 788/** 789 * A split_mail_conv() callback that converts an StGit patch to an RFC2822 790 * message suitable for parsing with git-mailinfo. 791 */ 792static intstgit_patch_to_mail(FILE*out,FILE*in,int keep_cr) 793{ 794struct strbuf sb = STRBUF_INIT; 795int subject_printed =0; 796 797while(!strbuf_getline(&sb, in,'\n')) { 798const char*str; 799 800if(str_isspace(sb.buf)) 801continue; 802else if(skip_prefix(sb.buf,"Author:", &str)) 803fprintf(out,"From:%s\n", str); 804else if(starts_with(sb.buf,"From") ||starts_with(sb.buf,"Date")) 805fprintf(out,"%s\n", sb.buf); 806else if(!subject_printed) { 807fprintf(out,"Subject:%s\n", sb.buf); 808 subject_printed =1; 809}else{ 810fprintf(out,"\n%s\n", sb.buf); 811break; 812} 813} 814 815strbuf_reset(&sb); 816while(strbuf_fread(&sb,8192, in) >0) { 817fwrite(sb.buf,1, sb.len, out); 818strbuf_reset(&sb); 819} 820 821strbuf_release(&sb); 822return0; 823} 824 825/** 826 * This function only supports a single StGit series file in `paths`. 827 * 828 * Given an StGit series file, converts the StGit patches in the series into 829 * RFC2822 messages suitable for parsing with git-mailinfo, and queues them in 830 * the state directory. 831 * 832 * Returns 0 on success, -1 on failure. 833 */ 834static intsplit_mail_stgit_series(struct am_state *state,const char**paths, 835int keep_cr) 836{ 837const char*series_dir; 838char*series_dir_buf; 839FILE*fp; 840struct argv_array patches = ARGV_ARRAY_INIT; 841struct strbuf sb = STRBUF_INIT; 842int ret; 843 844if(!paths[0] || paths[1]) 845returnerror(_("Only one StGIT patch series can be applied at once")); 846 847 series_dir_buf =xstrdup(*paths); 848 series_dir =dirname(series_dir_buf); 849 850 fp =fopen(*paths,"r"); 851if(!fp) 852returnerror(_("could not open '%s' for reading:%s"), *paths, 853strerror(errno)); 854 855while(!strbuf_getline(&sb, fp,'\n')) { 856if(*sb.buf =='#') 857continue;/* skip comment lines */ 858 859argv_array_push(&patches,mkpath("%s/%s", series_dir, sb.buf)); 860} 861 862fclose(fp); 863strbuf_release(&sb); 864free(series_dir_buf); 865 866 ret =split_mail_conv(stgit_patch_to_mail, state, patches.argv, keep_cr); 867 868argv_array_clear(&patches); 869return ret; 870} 871 872/** 873 * A split_patches_conv() callback that converts a mercurial patch to a RFC2822 874 * message suitable for parsing with git-mailinfo. 875 */ 876static inthg_patch_to_mail(FILE*out,FILE*in,int keep_cr) 877{ 878struct strbuf sb = STRBUF_INIT; 879 880while(!strbuf_getline(&sb, in,'\n')) { 881const char*str; 882 883if(skip_prefix(sb.buf,"# User ", &str)) 884fprintf(out,"From:%s\n", str); 885else if(skip_prefix(sb.buf,"# Date ", &str)) { 886unsigned long timestamp; 887long tz, tz2; 888char*end; 889 890 errno =0; 891 timestamp =strtoul(str, &end,10); 892if(errno) 893returnerror(_("invalid timestamp")); 894 895if(!skip_prefix(end," ", &str)) 896returnerror(_("invalid Date line")); 897 898 errno =0; 899 tz =strtol(str, &end,10); 900if(errno) 901returnerror(_("invalid timezone offset")); 902 903if(*end) 904returnerror(_("invalid Date line")); 905 906/* 907 * mercurial's timezone is in seconds west of UTC, 908 * however git's timezone is in hours + minutes east of 909 * UTC. Convert it. 910 */ 911 tz2 =labs(tz) /3600*100+labs(tz) %3600/60; 912if(tz >0) 913 tz2 = -tz2; 914 915fprintf(out,"Date:%s\n",show_date(timestamp, tz2,DATE_MODE(RFC2822))); 916}else if(starts_with(sb.buf,"# ")) { 917continue; 918}else{ 919fprintf(out,"\n%s\n", sb.buf); 920break; 921} 922} 923 924strbuf_reset(&sb); 925while(strbuf_fread(&sb,8192, in) >0) { 926fwrite(sb.buf,1, sb.len, out); 927strbuf_reset(&sb); 928} 929 930strbuf_release(&sb); 931return0; 932} 933 934/** 935 * Splits a list of files/directories into individual email patches. Each path 936 * in `paths` must be a file/directory that is formatted according to 937 * `patch_format`. 938 * 939 * Once split out, the individual email patches will be stored in the state 940 * directory, with each patch's filename being its index, padded to state->prec 941 * digits. 942 * 943 * state->cur will be set to the index of the first mail, and state->last will 944 * be set to the index of the last mail. 945 * 946 * Set keep_cr to 0 to convert all lines ending with \r\n to end with \n, 1 947 * to disable this behavior, -1 to use the default configured setting. 948 * 949 * Returns 0 on success, -1 on failure. 950 */ 951static intsplit_mail(struct am_state *state,enum patch_format patch_format, 952const char**paths,int keep_cr) 953{ 954if(keep_cr <0) { 955 keep_cr =0; 956git_config_get_bool("am.keepcr", &keep_cr); 957} 958 959switch(patch_format) { 960case PATCH_FORMAT_MBOX: 961returnsplit_mail_mbox(state, paths, keep_cr); 962case PATCH_FORMAT_STGIT: 963returnsplit_mail_conv(stgit_patch_to_mail, state, paths, keep_cr); 964case PATCH_FORMAT_STGIT_SERIES: 965returnsplit_mail_stgit_series(state, paths, keep_cr); 966case PATCH_FORMAT_HG: 967returnsplit_mail_conv(hg_patch_to_mail, state, paths, keep_cr); 968default: 969die("BUG: invalid patch_format"); 970} 971return-1; 972} 973 974/** 975 * Setup a new am session for applying patches 976 */ 977static voidam_setup(struct am_state *state,enum patch_format patch_format, 978const char**paths,int keep_cr) 979{ 980unsigned char curr_head[GIT_SHA1_RAWSZ]; 981const char*str; 982struct strbuf sb = STRBUF_INIT; 983 984if(!patch_format) 985 patch_format =detect_patch_format(paths); 986 987if(!patch_format) { 988fprintf_ln(stderr,_("Patch format detection failed.")); 989exit(128); 990} 991 992if(mkdir(state->dir,0777) <0&& errno != EEXIST) 993die_errno(_("failed to create directory '%s'"), state->dir); 994 995if(split_mail(state, patch_format, paths, keep_cr) <0) { 996am_destroy(state); 997die(_("Failed to split patches.")); 998} 9991000if(state->rebasing)1001 state->threeway =1;10021003write_file(am_path(state,"threeway"),1, state->threeway ?"t":"f");10041005write_file(am_path(state,"quiet"),1, state->quiet ?"t":"f");10061007write_file(am_path(state,"sign"),1, state->signoff ?"t":"f");10081009write_file(am_path(state,"utf8"),1, state->utf8 ?"t":"f");10101011switch(state->keep) {1012case KEEP_FALSE:1013 str ="f";1014break;1015case KEEP_TRUE:1016 str ="t";1017break;1018case KEEP_NON_PATCH:1019 str ="b";1020break;1021default:1022die("BUG: invalid value for state->keep");1023}10241025write_file(am_path(state,"keep"),1,"%s", str);10261027write_file(am_path(state,"messageid"),1, state->message_id ?"t":"f");10281029switch(state->scissors) {1030case SCISSORS_UNSET:1031 str ="";1032break;1033case SCISSORS_FALSE:1034 str ="f";1035break;1036case SCISSORS_TRUE:1037 str ="t";1038break;1039default:1040die("BUG: invalid value for state->scissors");1041}10421043write_file(am_path(state,"scissors"),1,"%s", str);10441045sq_quote_argv(&sb, state->git_apply_opts.argv,0);1046write_file(am_path(state,"apply-opt"),1,"%s", sb.buf);10471048if(state->rebasing)1049write_file(am_path(state,"rebasing"),1,"%s","");1050else1051write_file(am_path(state,"applying"),1,"%s","");10521053if(!get_sha1("HEAD", curr_head)) {1054write_file(am_path(state,"abort-safety"),1,"%s",sha1_to_hex(curr_head));1055if(!state->rebasing)1056update_ref("am","ORIG_HEAD", curr_head, NULL,0,1057 UPDATE_REFS_DIE_ON_ERR);1058}else{1059write_file(am_path(state,"abort-safety"),1,"%s","");1060if(!state->rebasing)1061delete_ref("ORIG_HEAD", NULL,0);1062}10631064/*1065 * NOTE: Since the "next" and "last" files determine if an am_state1066 * session is in progress, they should be written last.1067 */10681069write_file(am_path(state,"next"),1,"%d", state->cur);10701071write_file(am_path(state,"last"),1,"%d", state->last);10721073strbuf_release(&sb);1074}10751076/**1077 * Increments the patch pointer, and cleans am_state for the application of the1078 * next patch.1079 */1080static voidam_next(struct am_state *state)1081{1082unsigned char head[GIT_SHA1_RAWSZ];10831084free(state->author_name);1085 state->author_name = NULL;10861087free(state->author_email);1088 state->author_email = NULL;10891090free(state->author_date);1091 state->author_date = NULL;10921093free(state->msg);1094 state->msg = NULL;1095 state->msg_len =0;10961097unlink(am_path(state,"author-script"));1098unlink(am_path(state,"final-commit"));10991100hashclr(state->orig_commit);1101unlink(am_path(state,"original-commit"));11021103if(!get_sha1("HEAD", head))1104write_file(am_path(state,"abort-safety"),1,"%s",sha1_to_hex(head));1105else1106write_file(am_path(state,"abort-safety"),1,"%s","");11071108 state->cur++;1109write_file(am_path(state,"next"),1,"%d", state->cur);1110}11111112/**1113 * Returns the filename of the current patch email.1114 */1115static const char*msgnum(const struct am_state *state)1116{1117static struct strbuf sb = STRBUF_INIT;11181119strbuf_reset(&sb);1120strbuf_addf(&sb,"%0*d", state->prec, state->cur);11211122return sb.buf;1123}11241125/**1126 * Refresh and write index.1127 */1128static voidrefresh_and_write_cache(void)1129{1130struct lock_file *lock_file =xcalloc(1,sizeof(struct lock_file));11311132hold_locked_index(lock_file,1);1133refresh_cache(REFRESH_QUIET);1134if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))1135die(_("unable to write index file"));1136}11371138/**1139 * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn1140 * branch, returns 1 if there are entries in the index, 0 otherwise. If an1141 * strbuf is provided, the space-separated list of files that differ will be1142 * appended to it.1143 */1144static intindex_has_changes(struct strbuf *sb)1145{1146unsigned char head[GIT_SHA1_RAWSZ];1147int i;11481149if(!get_sha1_tree("HEAD", head)) {1150struct diff_options opt;11511152diff_setup(&opt);1153DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);1154if(!sb)1155DIFF_OPT_SET(&opt, QUICK);1156do_diff_cache(head, &opt);1157diffcore_std(&opt);1158for(i =0; sb && i < diff_queued_diff.nr; i++) {1159if(i)1160strbuf_addch(sb,' ');1161strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);1162}1163diff_flush(&opt);1164returnDIFF_OPT_TST(&opt, HAS_CHANGES) !=0;1165}else{1166for(i =0; sb && i < active_nr; i++) {1167if(i)1168strbuf_addch(sb,' ');1169strbuf_addstr(sb, active_cache[i]->name);1170}1171return!!active_nr;1172}1173}11741175/**1176 * Dies with a user-friendly message on how to proceed after resolving the1177 * problem. This message can be overridden with state->resolvemsg.1178 */1179static void NORETURN die_user_resolve(const struct am_state *state)1180{1181if(state->resolvemsg) {1182printf_ln("%s", state->resolvemsg);1183}else{1184const char*cmdline = state->interactive ?"git am -i":"git am";11851186printf_ln(_("When you have resolved this problem, run\"%s--continue\"."), cmdline);1187printf_ln(_("If you prefer to skip this patch, run\"%s--skip\"instead."), cmdline);1188printf_ln(_("To restore the original branch and stop patching, run\"%s--abort\"."), cmdline);1189}11901191exit(128);1192}11931194static voidam_signoff(struct strbuf *sb)1195{1196char*cp;1197struct strbuf mine = STRBUF_INIT;11981199/* Does it end with our own sign-off? */1200strbuf_addf(&mine,"\n%s%s\n",1201 sign_off_header,1202fmt_name(getenv("GIT_COMMITTER_NAME"),1203getenv("GIT_COMMITTER_EMAIL")));1204if(mine.len < sb->len &&1205!strcmp(mine.buf, sb->buf + sb->len - mine.len))1206goto exit;/* no need to duplicate */12071208/* Does it have any Signed-off-by: in the text */1209for(cp = sb->buf;1210 cp && *cp && (cp =strstr(cp, sign_off_header)) != NULL;1211 cp =strchr(cp,'\n')) {1212if(sb->buf == cp || cp[-1] =='\n')1213break;1214}12151216strbuf_addstr(sb, mine.buf + !!cp);1217exit:1218strbuf_release(&mine);1219}12201221/**1222 * Appends signoff to the "msg" field of the am_state.1223 */1224static voidam_append_signoff(struct am_state *state)1225{1226struct strbuf sb = STRBUF_INIT;12271228strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);1229am_signoff(&sb);1230 state->msg =strbuf_detach(&sb, &state->msg_len);1231}12321233/**1234 * Parses `mail` using git-mailinfo, extracting its patch and authorship info.1235 * state->msg will be set to the patch message. state->author_name,1236 * state->author_email and state->author_date will be set to the patch author's1237 * name, email and date respectively. The patch body will be written to the1238 * state directory's "patch" file.1239 *1240 * Returns 1 if the patch should be skipped, 0 otherwise.1241 */1242static intparse_mail(struct am_state *state,const char*mail)1243{1244FILE*fp;1245struct child_process cp = CHILD_PROCESS_INIT;1246struct strbuf sb = STRBUF_INIT;1247struct strbuf msg = STRBUF_INIT;1248struct strbuf author_name = STRBUF_INIT;1249struct strbuf author_date = STRBUF_INIT;1250struct strbuf author_email = STRBUF_INIT;1251int ret =0;12521253 cp.git_cmd =1;1254 cp.in =xopen(mail, O_RDONLY,0);1255 cp.out =xopen(am_path(state,"info"), O_WRONLY | O_CREAT,0777);12561257argv_array_push(&cp.args,"mailinfo");1258argv_array_push(&cp.args, state->utf8 ?"-u":"-n");12591260switch(state->keep) {1261case KEEP_FALSE:1262break;1263case KEEP_TRUE:1264argv_array_push(&cp.args,"-k");1265break;1266case KEEP_NON_PATCH:1267argv_array_push(&cp.args,"-b");1268break;1269default:1270die("BUG: invalid value for state->keep");1271}12721273if(state->message_id)1274argv_array_push(&cp.args,"-m");12751276switch(state->scissors) {1277case SCISSORS_UNSET:1278break;1279case SCISSORS_FALSE:1280argv_array_push(&cp.args,"--no-scissors");1281break;1282case SCISSORS_TRUE:1283argv_array_push(&cp.args,"--scissors");1284break;1285default:1286die("BUG: invalid value for state->scissors");1287}12881289argv_array_push(&cp.args,am_path(state,"msg"));1290argv_array_push(&cp.args,am_path(state,"patch"));12911292if(run_command(&cp) <0)1293die("could not parse patch");12941295close(cp.in);1296close(cp.out);12971298/* Extract message and author information */1299 fp =xfopen(am_path(state,"info"),"r");1300while(!strbuf_getline(&sb, fp,'\n')) {1301const char*x;13021303if(skip_prefix(sb.buf,"Subject: ", &x)) {1304if(msg.len)1305strbuf_addch(&msg,'\n');1306strbuf_addstr(&msg, x);1307}else if(skip_prefix(sb.buf,"Author: ", &x))1308strbuf_addstr(&author_name, x);1309else if(skip_prefix(sb.buf,"Email: ", &x))1310strbuf_addstr(&author_email, x);1311else if(skip_prefix(sb.buf,"Date: ", &x))1312strbuf_addstr(&author_date, x);1313}1314fclose(fp);13151316/* Skip pine's internal folder data */1317if(!strcmp(author_name.buf,"Mail System Internal Data")) {1318 ret =1;1319goto finish;1320}13211322if(is_empty_file(am_path(state,"patch"))) {1323printf_ln(_("Patch is empty. Was it split wrong?"));1324die_user_resolve(state);1325}13261327strbuf_addstr(&msg,"\n\n");1328if(strbuf_read_file(&msg,am_path(state,"msg"),0) <0)1329die_errno(_("could not read '%s'"),am_path(state,"msg"));1330stripspace(&msg,0);13311332if(state->signoff)1333am_signoff(&msg);13341335assert(!state->author_name);1336 state->author_name =strbuf_detach(&author_name, NULL);13371338assert(!state->author_email);1339 state->author_email =strbuf_detach(&author_email, NULL);13401341assert(!state->author_date);1342 state->author_date =strbuf_detach(&author_date, NULL);13431344assert(!state->msg);1345 state->msg =strbuf_detach(&msg, &state->msg_len);13461347finish:1348strbuf_release(&msg);1349strbuf_release(&author_date);1350strbuf_release(&author_email);1351strbuf_release(&author_name);1352strbuf_release(&sb);1353return ret;1354}13551356/**1357 * Sets commit_id to the commit hash where the mail was generated from.1358 * Returns 0 on success, -1 on failure.1359 */1360static intget_mail_commit_sha1(unsigned char*commit_id,const char*mail)1361{1362struct strbuf sb = STRBUF_INIT;1363FILE*fp =xfopen(mail,"r");1364const char*x;13651366if(strbuf_getline(&sb, fp,'\n'))1367return-1;13681369if(!skip_prefix(sb.buf,"From ", &x))1370return-1;13711372if(get_sha1_hex(x, commit_id) <0)1373return-1;13741375strbuf_release(&sb);1376fclose(fp);1377return0;1378}13791380/**1381 * Sets state->msg, state->author_name, state->author_email, state->author_date1382 * to the commit's respective info.1383 */1384static voidget_commit_info(struct am_state *state,struct commit *commit)1385{1386const char*buffer, *ident_line, *author_date, *msg;1387size_t ident_len;1388struct ident_split ident_split;1389struct strbuf sb = STRBUF_INIT;13901391 buffer =logmsg_reencode(commit, NULL,get_commit_output_encoding());13921393 ident_line =find_commit_header(buffer,"author", &ident_len);13941395if(split_ident_line(&ident_split, ident_line, ident_len) <0) {1396strbuf_add(&sb, ident_line, ident_len);1397die(_("invalid ident line:%s"), sb.buf);1398}13991400assert(!state->author_name);1401if(ident_split.name_begin) {1402strbuf_add(&sb, ident_split.name_begin,1403 ident_split.name_end - ident_split.name_begin);1404 state->author_name =strbuf_detach(&sb, NULL);1405}else1406 state->author_name =xstrdup("");14071408assert(!state->author_email);1409if(ident_split.mail_begin) {1410strbuf_add(&sb, ident_split.mail_begin,1411 ident_split.mail_end - ident_split.mail_begin);1412 state->author_email =strbuf_detach(&sb, NULL);1413}else1414 state->author_email =xstrdup("");14151416 author_date =show_ident_date(&ident_split,DATE_MODE(NORMAL));1417strbuf_addstr(&sb, author_date);1418assert(!state->author_date);1419 state->author_date =strbuf_detach(&sb, NULL);14201421assert(!state->msg);1422 msg =strstr(buffer,"\n\n");1423if(!msg)1424die(_("unable to parse commit%s"),sha1_to_hex(commit->object.sha1));1425 state->msg =xstrdup(msg +2);1426 state->msg_len =strlen(state->msg);1427}14281429/**1430 * Writes `commit` as a patch to the state directory's "patch" file.1431 */1432static voidwrite_commit_patch(const struct am_state *state,struct commit *commit)1433{1434struct rev_info rev_info;1435FILE*fp;14361437 fp =xfopen(am_path(state,"patch"),"w");1438init_revisions(&rev_info, NULL);1439 rev_info.diff =1;1440 rev_info.abbrev =0;1441 rev_info.disable_stdin =1;1442 rev_info.show_root_diff =1;1443 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;1444 rev_info.no_commit_id =1;1445DIFF_OPT_SET(&rev_info.diffopt, BINARY);1446DIFF_OPT_SET(&rev_info.diffopt, FULL_INDEX);1447 rev_info.diffopt.use_color =0;1448 rev_info.diffopt.file = fp;1449 rev_info.diffopt.close_file =1;1450add_pending_object(&rev_info, &commit->object,"");1451diff_setup_done(&rev_info.diffopt);1452log_tree_commit(&rev_info, commit);1453}14541455/**1456 * Writes the diff of the index against HEAD as a patch to the state1457 * directory's "patch" file.1458 */1459static voidwrite_index_patch(const struct am_state *state)1460{1461struct tree *tree;1462unsigned char head[GIT_SHA1_RAWSZ];1463struct rev_info rev_info;1464FILE*fp;14651466if(!get_sha1_tree("HEAD", head))1467 tree =lookup_tree(head);1468else1469 tree =lookup_tree(EMPTY_TREE_SHA1_BIN);14701471 fp =xfopen(am_path(state,"patch"),"w");1472init_revisions(&rev_info, NULL);1473 rev_info.diff =1;1474 rev_info.disable_stdin =1;1475 rev_info.no_commit_id =1;1476 rev_info.diffopt.output_format = DIFF_FORMAT_PATCH;1477 rev_info.diffopt.use_color =0;1478 rev_info.diffopt.file = fp;1479 rev_info.diffopt.close_file =1;1480add_pending_object(&rev_info, &tree->object,"");1481diff_setup_done(&rev_info.diffopt);1482run_diff_index(&rev_info,1);1483}14841485/**1486 * Like parse_mail(), but parses the mail by looking up its commit ID1487 * directly. This is used in --rebasing mode to bypass git-mailinfo's munging1488 * of patches.1489 *1490 * state->orig_commit will be set to the original commit ID.1491 *1492 * Will always return 0 as the patch should never be skipped.1493 */1494static intparse_mail_rebase(struct am_state *state,const char*mail)1495{1496struct commit *commit;1497unsigned char commit_sha1[GIT_SHA1_RAWSZ];14981499if(get_mail_commit_sha1(commit_sha1, mail) <0)1500die(_("could not parse%s"), mail);15011502 commit =lookup_commit_or_die(commit_sha1, mail);15031504get_commit_info(state, commit);15051506write_commit_patch(state, commit);15071508hashcpy(state->orig_commit, commit_sha1);1509write_file(am_path(state,"original-commit"),1,"%s",1510sha1_to_hex(commit_sha1));15111512return0;1513}15141515/**1516 * Applies current patch with git-apply. Returns 0 on success, -1 otherwise. If1517 * `index_file` is not NULL, the patch will be applied to that index.1518 */1519static intrun_apply(const struct am_state *state,const char*index_file)1520{1521struct child_process cp = CHILD_PROCESS_INIT;15221523 cp.git_cmd =1;15241525if(index_file)1526argv_array_pushf(&cp.env_array,"GIT_INDEX_FILE=%s", index_file);15271528/*1529 * If we are allowed to fall back on 3-way merge, don't give false1530 * errors during the initial attempt.1531 */1532if(state->threeway && !index_file) {1533 cp.no_stdout =1;1534 cp.no_stderr =1;1535}15361537argv_array_push(&cp.args,"apply");15381539argv_array_pushv(&cp.args, state->git_apply_opts.argv);15401541if(index_file)1542argv_array_push(&cp.args,"--cached");1543else1544argv_array_push(&cp.args,"--index");15451546argv_array_push(&cp.args,am_path(state,"patch"));15471548if(run_command(&cp))1549return-1;15501551/* Reload index as git-apply will have modified it. */1552discard_cache();1553read_cache_from(index_file ? index_file :get_index_file());15541555return0;1556}15571558/**1559 * Builds an index that contains just the blobs needed for a 3way merge.1560 */1561static intbuild_fake_ancestor(const struct am_state *state,const char*index_file)1562{1563struct child_process cp = CHILD_PROCESS_INIT;15641565 cp.git_cmd =1;1566argv_array_push(&cp.args,"apply");1567argv_array_pushv(&cp.args, state->git_apply_opts.argv);1568argv_array_pushf(&cp.args,"--build-fake-ancestor=%s", index_file);1569argv_array_push(&cp.args,am_path(state,"patch"));15701571if(run_command(&cp))1572return-1;15731574return0;1575}15761577/**1578 * Attempt a threeway merge, using index_path as the temporary index.1579 */1580static intfall_back_threeway(const struct am_state *state,const char*index_path)1581{1582unsigned char orig_tree[GIT_SHA1_RAWSZ], his_tree[GIT_SHA1_RAWSZ],1583 our_tree[GIT_SHA1_RAWSZ];1584const unsigned char*bases[1] = {orig_tree};1585struct merge_options o;1586struct commit *result;1587char*his_tree_name;15881589if(get_sha1("HEAD", our_tree) <0)1590hashcpy(our_tree, EMPTY_TREE_SHA1_BIN);15911592if(build_fake_ancestor(state, index_path))1593returnerror("could not build fake ancestor");15941595discard_cache();1596read_cache_from(index_path);15971598if(write_index_as_tree(orig_tree, &the_index, index_path,0, NULL))1599returnerror(_("Repository lacks necessary blobs to fall back on 3-way merge."));16001601say(state, stdout,_("Using index info to reconstruct a base tree..."));16021603if(!state->quiet) {1604/*1605 * List paths that needed 3-way fallback, so that the user can1606 * review them with extra care to spot mismerges.1607 */1608struct rev_info rev_info;1609const char*diff_filter_str ="--diff-filter=AM";16101611init_revisions(&rev_info, NULL);1612 rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;1613diff_opt_parse(&rev_info.diffopt, &diff_filter_str,1);1614add_pending_sha1(&rev_info,"HEAD", our_tree,0);1615diff_setup_done(&rev_info.diffopt);1616run_diff_index(&rev_info,1);1617}16181619if(run_apply(state, index_path))1620returnerror(_("Did you hand edit your patch?\n"1621"It does not apply to blobs recorded in its index."));16221623if(write_index_as_tree(his_tree, &the_index, index_path,0, NULL))1624returnerror("could not write tree");16251626say(state, stdout,_("Falling back to patching base and 3-way merge..."));16271628discard_cache();1629read_cache();16301631/*1632 * This is not so wrong. Depending on which base we picked, orig_tree1633 * may be wildly different from ours, but his_tree has the same set of1634 * wildly different changes in parts the patch did not touch, so1635 * recursive ends up canceling them, saying that we reverted all those1636 * changes.1637 */16381639init_merge_options(&o);16401641 o.branch1 ="HEAD";1642 his_tree_name =xstrfmt("%.*s",linelen(state->msg), state->msg);1643 o.branch2 = his_tree_name;16441645if(state->quiet)1646 o.verbosity =0;16471648if(merge_recursive_generic(&o, our_tree, his_tree,1, bases, &result)) {1649rerere(state->allow_rerere_autoupdate);1650free(his_tree_name);1651returnerror(_("Failed to merge in the changes."));1652}16531654free(his_tree_name);1655return0;1656}16571658/**1659 * Commits the current index with state->msg as the commit message and1660 * state->author_name, state->author_email and state->author_date as the author1661 * information.1662 */1663static voiddo_commit(const struct am_state *state)1664{1665unsigned char tree[GIT_SHA1_RAWSZ], parent[GIT_SHA1_RAWSZ],1666 commit[GIT_SHA1_RAWSZ];1667unsigned char*ptr;1668struct commit_list *parents = NULL;1669const char*reflog_msg, *author;1670struct strbuf sb = STRBUF_INIT;16711672if(run_hook_le(NULL,"pre-applypatch", NULL))1673exit(1);16741675if(write_cache_as_tree(tree,0, NULL))1676die(_("git write-tree failed to write a tree"));16771678if(!get_sha1_commit("HEAD", parent)) {1679 ptr = parent;1680commit_list_insert(lookup_commit(parent), &parents);1681}else{1682 ptr = NULL;1683say(state, stderr,_("applying to an empty history"));1684}16851686 author =fmt_ident(state->author_name, state->author_email,1687 state->ignore_date ? NULL : state->author_date,1688 IDENT_STRICT);16891690if(state->committer_date_is_author_date)1691setenv("GIT_COMMITTER_DATE",1692 state->ignore_date ?"": state->author_date,1);16931694if(commit_tree(state->msg, state->msg_len, tree, parents, commit,1695 author, state->sign_commit))1696die(_("failed to write commit object"));16971698 reflog_msg =getenv("GIT_REFLOG_ACTION");1699if(!reflog_msg)1700 reflog_msg ="am";17011702strbuf_addf(&sb,"%s: %.*s", reflog_msg,linelen(state->msg),1703 state->msg);17041705update_ref(sb.buf,"HEAD", commit, ptr,0, UPDATE_REFS_DIE_ON_ERR);17061707if(state->rebasing) {1708FILE*fp =xfopen(am_path(state,"rewritten"),"a");17091710assert(!is_null_sha1(state->orig_commit));1711fprintf(fp,"%s",sha1_to_hex(state->orig_commit));1712fprintf(fp,"%s\n",sha1_to_hex(commit));1713fclose(fp);1714}17151716run_hook_le(NULL,"post-applypatch", NULL);17171718strbuf_release(&sb);1719}17201721/**1722 * Validates the am_state for resuming -- the "msg" and authorship fields must1723 * be filled up.1724 */1725static voidvalidate_resume_state(const struct am_state *state)1726{1727if(!state->msg)1728die(_("cannot resume:%sdoes not exist."),1729am_path(state,"final-commit"));17301731if(!state->author_name || !state->author_email || !state->author_date)1732die(_("cannot resume:%sdoes not exist."),1733am_path(state,"author-script"));1734}17351736/**1737 * Interactively prompt the user on whether the current patch should be1738 * applied.1739 *1740 * Returns 0 if the user chooses to apply the patch, 1 if the user chooses to1741 * skip it.1742 */1743static intdo_interactive(struct am_state *state)1744{1745assert(state->msg);17461747if(!isatty(0))1748die(_("cannot be interactive without stdin connected to a terminal."));17491750for(;;) {1751const char*reply;17521753puts(_("Commit Body is:"));1754puts("--------------------------");1755printf("%s", state->msg);1756puts("--------------------------");17571758/*1759 * TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]1760 * in your translation. The program will only accept English1761 * input at this point.1762 */1763 reply =git_prompt(_("Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all: "), PROMPT_ECHO);17641765if(!reply) {1766continue;1767}else if(*reply =='y'|| *reply =='Y') {1768return0;1769}else if(*reply =='a'|| *reply =='A') {1770 state->interactive =0;1771return0;1772}else if(*reply =='n'|| *reply =='N') {1773return1;1774}else if(*reply =='e'|| *reply =='E') {1775struct strbuf msg = STRBUF_INIT;17761777if(!launch_editor(am_path(state,"final-commit"), &msg, NULL)) {1778free(state->msg);1779 state->msg =strbuf_detach(&msg, &state->msg_len);1780}1781strbuf_release(&msg);1782}else if(*reply =='v'|| *reply =='V') {1783const char*pager =git_pager(1);1784struct child_process cp = CHILD_PROCESS_INIT;17851786if(!pager)1787 pager ="cat";1788argv_array_push(&cp.args, pager);1789argv_array_push(&cp.args,am_path(state,"patch"));1790run_command(&cp);1791}1792}1793}17941795/**1796 * Applies all queued mail.1797 *1798 * If `resume` is true, we are "resuming". The "msg" and authorship fields, as1799 * well as the state directory's "patch" file is used as-is for applying the1800 * patch and committing it.1801 */1802static voidam_run(struct am_state *state,int resume)1803{1804const char*argv_gc_auto[] = {"gc","--auto", NULL};1805struct strbuf sb = STRBUF_INIT;18061807unlink(am_path(state,"dirtyindex"));18081809refresh_and_write_cache();18101811if(index_has_changes(&sb)) {1812write_file(am_path(state,"dirtyindex"),1,"t");1813die(_("Dirty index: cannot apply patches (dirty:%s)"), sb.buf);1814}18151816strbuf_release(&sb);18171818while(state->cur <= state->last) {1819const char*mail =am_path(state,msgnum(state));1820int apply_status;18211822if(!file_exists(mail))1823goto next;18241825if(resume) {1826validate_resume_state(state);1827}else{1828int skip;18291830if(state->rebasing)1831 skip =parse_mail_rebase(state, mail);1832else1833 skip =parse_mail(state, mail);18341835if(skip)1836goto next;/* mail should be skipped */18371838write_author_script(state);1839write_commit_msg(state);1840}18411842if(state->interactive &&do_interactive(state))1843goto next;18441845if(run_applypatch_msg_hook(state))1846exit(1);18471848say(state, stdout,_("Applying: %.*s"),linelen(state->msg), state->msg);18491850 apply_status =run_apply(state, NULL);18511852if(apply_status && state->threeway) {1853struct strbuf sb = STRBUF_INIT;18541855strbuf_addstr(&sb,am_path(state,"patch-merge-index"));1856 apply_status =fall_back_threeway(state, sb.buf);1857strbuf_release(&sb);18581859/*1860 * Applying the patch to an earlier tree and merging1861 * the result may have produced the same tree as ours.1862 */1863if(!apply_status && !index_has_changes(NULL)) {1864say(state, stdout,_("No changes -- Patch already applied."));1865goto next;1866}1867}18681869if(apply_status) {1870int advice_amworkdir =1;18711872printf_ln(_("Patch failed at%s%.*s"),msgnum(state),1873linelen(state->msg), state->msg);18741875git_config_get_bool("advice.amworkdir", &advice_amworkdir);18761877if(advice_amworkdir)1878printf_ln(_("The copy of the patch that failed is found in:%s"),1879am_path(state,"patch"));18801881die_user_resolve(state);1882}18831884do_commit(state);18851886next:1887am_next(state);18881889if(resume)1890am_load(state);1891 resume =0;1892}18931894if(!is_empty_file(am_path(state,"rewritten"))) {1895assert(state->rebasing);1896copy_notes_for_rebase(state);1897run_post_rewrite_hook(state);1898}18991900/*1901 * In rebasing mode, it's up to the caller to take care of1902 * housekeeping.1903 */1904if(!state->rebasing) {1905am_destroy(state);1906run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);1907}1908}19091910/**1911 * Resume the current am session after patch application failure. The user did1912 * all the hard work, and we do not have to do any patch application. Just1913 * trust and commit what the user has in the index and working tree.1914 */1915static voidam_resolve(struct am_state *state)1916{1917validate_resume_state(state);19181919say(state, stdout,_("Applying: %.*s"),linelen(state->msg), state->msg);19201921if(!index_has_changes(NULL)) {1922printf_ln(_("No changes - did you forget to use 'git add'?\n"1923"If there is nothing left to stage, chances are that something else\n"1924"already introduced the same changes; you might want to skip this patch."));1925die_user_resolve(state);1926}19271928if(unmerged_cache()) {1929printf_ln(_("You still have unmerged paths in your index.\n"1930"Did you forget to use 'git add'?"));1931die_user_resolve(state);1932}19331934if(state->interactive) {1935write_index_patch(state);1936if(do_interactive(state))1937goto next;1938}19391940rerere(0);19411942do_commit(state);19431944next:1945am_next(state);1946am_load(state);1947am_run(state,0);1948}19491950/**1951 * Performs a checkout fast-forward from `head` to `remote`. If `reset` is1952 * true, any unmerged entries will be discarded. Returns 0 on success, -1 on1953 * failure.1954 */1955static intfast_forward_to(struct tree *head,struct tree *remote,int reset)1956{1957struct lock_file *lock_file;1958struct unpack_trees_options opts;1959struct tree_desc t[2];19601961if(parse_tree(head) ||parse_tree(remote))1962return-1;19631964 lock_file =xcalloc(1,sizeof(struct lock_file));1965hold_locked_index(lock_file,1);19661967refresh_cache(REFRESH_QUIET);19681969memset(&opts,0,sizeof(opts));1970 opts.head_idx =1;1971 opts.src_index = &the_index;1972 opts.dst_index = &the_index;1973 opts.update =1;1974 opts.merge =1;1975 opts.reset = reset;1976 opts.fn = twoway_merge;1977init_tree_desc(&t[0], head->buffer, head->size);1978init_tree_desc(&t[1], remote->buffer, remote->size);19791980if(unpack_trees(2, t, &opts)) {1981rollback_lock_file(lock_file);1982return-1;1983}19841985if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))1986die(_("unable to write new index file"));19871988return0;1989}19901991/**1992 * Clean the index without touching entries that are not modified between1993 * `head` and `remote`.1994 */1995static intclean_index(const unsigned char*head,const unsigned char*remote)1996{1997struct lock_file *lock_file;1998struct tree *head_tree, *remote_tree, *index_tree;1999unsigned char index[GIT_SHA1_RAWSZ];2000struct pathspec pathspec;20012002 head_tree =parse_tree_indirect(head);2003if(!head_tree)2004returnerror(_("Could not parse object '%s'."),sha1_to_hex(head));20052006 remote_tree =parse_tree_indirect(remote);2007if(!remote_tree)2008returnerror(_("Could not parse object '%s'."),sha1_to_hex(remote));20092010read_cache_unmerged();20112012if(fast_forward_to(head_tree, head_tree,1))2013return-1;20142015if(write_cache_as_tree(index,0, NULL))2016return-1;20172018 index_tree =parse_tree_indirect(index);2019if(!index_tree)2020returnerror(_("Could not parse object '%s'."),sha1_to_hex(index));20212022if(fast_forward_to(index_tree, remote_tree,0))2023return-1;20242025memset(&pathspec,0,sizeof(pathspec));20262027 lock_file =xcalloc(1,sizeof(struct lock_file));2028hold_locked_index(lock_file,1);20292030if(read_tree(remote_tree,0, &pathspec)) {2031rollback_lock_file(lock_file);2032return-1;2033}20342035if(write_locked_index(&the_index, lock_file, COMMIT_LOCK))2036die(_("unable to write new index file"));20372038remove_branch_state();20392040return0;2041}20422043/**2044 * Resets rerere's merge resolution metadata.2045 */2046static voidam_rerere_clear(void)2047{2048struct string_list merge_rr = STRING_LIST_INIT_DUP;2049int fd =setup_rerere(&merge_rr,0);20502051if(fd <0)2052return;20532054rerere_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{2063unsigned char head[GIT_SHA1_RAWSZ];20642065am_rerere_clear();20662067if(get_sha1("HEAD", head))2068hashcpy(head, 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;2088unsigned char abort_safety[GIT_SHA1_RAWSZ], head[GIT_SHA1_RAWSZ];20892090if(file_exists(am_path(state,"dirtyindex")))2091return0;20922093if(read_state_file(&sb, state,"abort-safety",1) >0) {2094if(get_sha1_hex(sb.buf, abort_safety))2095die(_("could not parse%s"),am_path(state,"abort_safety"));2096}else2097hashclr(abort_safety);20982099if(get_sha1("HEAD", head))2100hashclr(head);21012102if(!hashcmp(head, abort_safety))2103return1;21042105error(_("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{2116unsigned char curr_head[GIT_SHA1_RAWSZ], orig_head[GIT_SHA1_RAWSZ];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, NULL);2128 has_curr_head = !is_null_sha1(curr_head);2129if(!has_curr_head)2130hashcpy(curr_head, EMPTY_TREE_SHA1_BIN);21312132 has_orig_head = !get_sha1("ORIG_HEAD", orig_head);2133if(!has_orig_head)2134hashcpy(orig_head, EMPTY_TREE_SHA1_BIN);21352136clean_index(curr_head, orig_head);21372138if(has_orig_head)2139update_ref("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(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;2165else2166returnerror(_("Invalid value for --patch-format:%s"), arg);2167return0;2168}21692170enum resume_mode {2171 RESUME_FALSE =0,2172 RESUME_APPLY,2173 RESUME_RESOLVED,2174 RESUME_SKIP,2175 RESUME_ABORT2176};21772178intcmd_am(int argc,const char**argv,const char*prefix)2179{2180struct am_state state;2181int binary = -1;2182int keep_cr = -1;2183int patch_format = PATCH_FORMAT_UNKNOWN;2184enum resume_mode resume = RESUME_FALSE;2185int in_progress;21862187const char*const usage[] = {2188N_("git am [options] [(<mbox>|<Maildir>)...]"),2189N_("git am [options] (--continue | --skip | --abort)"),2190 NULL2191};21922193struct option options[] = {2194OPT_BOOL('i',"interactive", &state.interactive,2195N_("run interactively")),2196OPT_HIDDEN_BOOL('b',"binary", &binary,2197N_("(historical option -- no-op")),2198OPT_BOOL('3',"3way", &state.threeway,2199N_("allow fall back on 3way merging if needed")),2200OPT__QUIET(&state.quiet,N_("be quiet")),2201OPT_SET_INT('s',"signoff", &state.signoff,2202N_("add a Signed-off-by line to the commit message"),2203 SIGNOFF_EXPLICIT),2204OPT_BOOL('u',"utf8", &state.utf8,2205N_("recode into utf8 (default)")),2206OPT_SET_INT('k',"keep", &state.keep,2207N_("pass -k flag to git-mailinfo"), KEEP_TRUE),2208OPT_SET_INT(0,"keep-non-patch", &state.keep,2209N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH),2210OPT_BOOL('m',"message-id", &state.message_id,2211N_("pass -m flag to git-mailinfo")),2212{ OPTION_SET_INT,0,"keep-cr", &keep_cr, NULL,2213N_("pass --keep-cr flag to git-mailsplit for mbox format"),2214 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL,1},2215{ OPTION_SET_INT,0,"no-keep-cr", &keep_cr, NULL,2216N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"),2217 PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL,0},2218OPT_BOOL('c',"scissors", &state.scissors,2219N_("strip everything before a scissors line")),2220OPT_PASSTHRU_ARGV(0,"whitespace", &state.git_apply_opts,N_("action"),2221N_("pass it through git-apply"),22220),2223OPT_PASSTHRU_ARGV(0,"ignore-space-change", &state.git_apply_opts, NULL,2224N_("pass it through git-apply"),2225 PARSE_OPT_NOARG),2226OPT_PASSTHRU_ARGV(0,"ignore-whitespace", &state.git_apply_opts, NULL,2227N_("pass it through git-apply"),2228 PARSE_OPT_NOARG),2229OPT_PASSTHRU_ARGV(0,"directory", &state.git_apply_opts,N_("root"),2230N_("pass it through git-apply"),22310),2232OPT_PASSTHRU_ARGV(0,"exclude", &state.git_apply_opts,N_("path"),2233N_("pass it through git-apply"),22340),2235OPT_PASSTHRU_ARGV(0,"include", &state.git_apply_opts,N_("path"),2236N_("pass it through git-apply"),22370),2238OPT_PASSTHRU_ARGV('C', NULL, &state.git_apply_opts,N_("n"),2239N_("pass it through git-apply"),22400),2241OPT_PASSTHRU_ARGV('p', NULL, &state.git_apply_opts,N_("num"),2242N_("pass it through git-apply"),22430),2244OPT_CALLBACK(0,"patch-format", &patch_format,N_("format"),2245N_("format the patch(es) are in"),2246 parse_opt_patchformat),2247OPT_PASSTHRU_ARGV(0,"reject", &state.git_apply_opts, NULL,2248N_("pass it through git-apply"),2249 PARSE_OPT_NOARG),2250OPT_STRING(0,"resolvemsg", &state.resolvemsg, NULL,2251N_("override error message when patch failure occurs")),2252OPT_CMDMODE(0,"continue", &resume,2253N_("continue applying patches after resolving a conflict"),2254 RESUME_RESOLVED),2255OPT_CMDMODE('r',"resolved", &resume,2256N_("synonyms for --continue"),2257 RESUME_RESOLVED),2258OPT_CMDMODE(0,"skip", &resume,2259N_("skip the current patch"),2260 RESUME_SKIP),2261OPT_CMDMODE(0,"abort", &resume,2262N_("restore the original branch and abort the patching operation."),2263 RESUME_ABORT),2264OPT_BOOL(0,"committer-date-is-author-date",2265&state.committer_date_is_author_date,2266N_("lie about committer date")),2267OPT_BOOL(0,"ignore-date", &state.ignore_date,2268N_("use current timestamp for author date")),2269OPT_RERERE_AUTOUPDATE(&state.allow_rerere_autoupdate),2270{ OPTION_STRING,'S',"gpg-sign", &state.sign_commit,N_("key-id"),2271N_("GPG-sign commits"),2272 PARSE_OPT_OPTARG, NULL, (intptr_t)""},2273OPT_HIDDEN_BOOL(0,"rebasing", &state.rebasing,2274N_("(internal use for git-rebase)")),2275OPT_END()2276};22772278git_config(git_default_config, NULL);22792280am_state_init(&state,git_path("rebase-apply"));22812282 in_progress =am_in_progress(&state);2283if(in_progress)2284am_load(&state);22852286 argc =parse_options(argc, argv, prefix, options, usage,0);22872288if(binary >=0)2289fprintf_ln(stderr,_("The -b/--binary option has been a no-op for long time, and\n"2290"it will be removed. Please do not use it anymore."));22912292/* Ensure a valid committer ident can be constructed */2293git_committer_info(IDENT_STRICT);22942295if(read_index_preload(&the_index, NULL) <0)2296die(_("failed to read the index"));22972298if(in_progress) {2299/*2300 * Catch user error to feed us patches when there is a session2301 * in progress:2302 *2303 * 1. mbox path(s) are provided on the command-line.2304 * 2. stdin is not a tty: the user is trying to feed us a patch2305 * from standard input. This is somewhat unreliable -- stdin2306 * could be /dev/null for example and the caller did not2307 * intend to feed us a patch but wanted to continue2308 * unattended.2309 */2310if(argc || (resume == RESUME_FALSE && !isatty(0)))2311die(_("previous rebase directory%sstill exists but mbox given."),2312 state.dir);23132314if(resume == RESUME_FALSE)2315 resume = RESUME_APPLY;23162317if(state.signoff == SIGNOFF_EXPLICIT)2318am_append_signoff(&state);2319}else{2320struct argv_array paths = ARGV_ARRAY_INIT;2321int i;23222323/*2324 * Handle stray state directory in the independent-run case. In2325 * the --rebasing case, it is up to the caller to take care of2326 * stray directories.2327 */2328if(file_exists(state.dir) && !state.rebasing) {2329if(resume == RESUME_ABORT) {2330am_destroy(&state);2331am_state_release(&state);2332return0;2333}23342335die(_("Stray%sdirectory found.\n"2336"Use\"git am --abort\"to remove it."),2337 state.dir);2338}23392340if(resume)2341die(_("Resolve operation not in progress, we are not resuming."));23422343for(i =0; i < argc; i++) {2344if(is_absolute_path(argv[i]) || !prefix)2345argv_array_push(&paths, argv[i]);2346else2347argv_array_push(&paths,mkpath("%s/%s", prefix, argv[i]));2348}23492350am_setup(&state, patch_format, paths.argv, keep_cr);23512352argv_array_clear(&paths);2353}23542355switch(resume) {2356case RESUME_FALSE:2357am_run(&state,0);2358break;2359case RESUME_APPLY:2360am_run(&state,1);2361break;2362case RESUME_RESOLVED:2363am_resolve(&state);2364break;2365case RESUME_SKIP:2366am_skip(&state);2367break;2368case RESUME_ABORT:2369am_abort(&state);2370break;2371default:2372die("BUG: invalid resume value");2373}23742375am_state_release(&state);23762377return0;2378}